//===========================================================================//
// File:	btl4app.cpp                                                         //
// Project: BattleTech           Brick: BattleTech LBE Application            //
// Contents: BTL4Application -- the top-level LBE-mode application object.     //
//           Builds the registry, renderers, mode/controls managers, the      //
//           mission, and the player's viewpoint entity; drives the run /     //
//           stop mission state.                                              //
//---------------------------------------------------------------------------//
//   Date    Who  Modification                                               //
// --------  ---  ---------------------------------------------------------- //
//                                                                           //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
//                      PROPRIETARY AND CONFIDENTIAL                         //
//===========================================================================//
//
// RECONSTRUCTED from the shipped binary (BTL4OPT.EXE).  The class declaration
// is taken VERBATIM from the surviving header BTL4APP.HPP; the method bodies
// are recovered from the Ghidra pseudo-C for the 0x4d34xx-0x4d3axx cluster
// (see all/part_014.c) and cross-checked against Red Planet's direct analog
// RP_L4\RPL4APP.cpp and the MUNGA base MUNGA\APP.cpp.
//
// The 12 functions of this object file map 1:1 onto the 12 methods declared
// in BTL4APP.HPP, in source (compiler-emission) order:
//
//   @004d34c4  BTL4Application::BTL4Application        (constructor)
//   @004d350c  BTL4Application::~BTL4Application        (scalar-deleting dtor)
//   @004d3538  BTL4Application::MakeRegistry
//   @004d3564  BTL4Application::MakeVideoRenderer
//   @004d35b0  BTL4Application::MakeAudioRenderer
//   @004d3614  BTL4Application::MakeModeManager
//   @004d3644  BTL4Application::MakeControlsManager
//   @004d368c  BTL4Application::MakeGaugeRenderer
//   @004d36c4  BTL4Application::MakeMission
//   @004d36f4  BTL4Application::MakeViewpointEntity     (anchor; 822 bytes)
//   @004d3a2c  BTL4Application::RunMissionMessageHandler
//   @004d3a94  BTL4Application::StopMissionMessageHandler
//
// Helper / engine-internal name mapping used below (FUN_ -> intent):
//   FUN_0047b410  L4Application::L4Application (base ctor)
//   FUN_0047b624  L4Application::~L4Application (base dtor)
//   FUN_0047b788  L4Application::RunMissionMessageHandler (base)
//   FUN_0047b864  L4Application::StopMissionMessageHandler (base)
//   FUN_00402298  operator new / Mem_Alloc(size)
//   FUN_004022d0  operator delete / Mem_Free(p)
//   FUN_004c1548  BTRegistry::BTRegistry            (0x10 bytes)
//   FUN_004ceecc  BTL4VideoRenderer::BTL4VideoRenderer (0x3e0 bytes)
//   FUN_004d2b6c  BTL4AudioRenderer::BTL4AudioRenderer (0x1b0 bytes)
//   FUN_0044c2d0  ModeManager init helper           (BTL4ModeManager, 0xc bytes)
//   FUN_00475c18  L4ControlsManager::L4ControlsManager (0xd10 bytes)
//   FUN_004cbea0  BTL4GaugeRenderer::BTL4GaugeRenderer (0x1c53c bytes)
//   FUN_004d2be0  BTL4Mission::BTL4Mission          (0xfc bytes)
//   FUN_004dee74  getenv()
//   FUN_0044e19c  GetSampleRate() (reads division-params float @+0x10)
//   FUN_0042d16c  CameraShip::Make
//   FUN_004a2d48  Mech::Make
//   FUN_00476eac  ControlsManager::SetJoystickDeadBand
//   FUN_00476eec  ControlsManager::SetThrottleDeadBand
//   FUN_00476f10  ControlsManager::SetPedalsDeadBand
//   FUN_0047ac5c  CameraThrustmasterMapper::CameraThrustmasterMapper
//   FUN_0047ad3c  CameraRIOMapper::CameraRIOMapper
//   FUN_0047aa78  CameraL4Mapper::CameraL4Mapper
//   FUN_0042c630  CameraShip::SetMappingSubsystem
//   FUN_004d21d0  MechThrustmasterMapper::MechThrustmasterMapper
//   FUN_004d266c  MechRIOMapper::MechRIOMapper
//   FUN_00402460  (controls-mapper resource ctor / Str_Copy "ControlsMapper")
//   FUN_0049fe40  Mech::SetMappingSubsystem
//   FUN_00407064  ResourceFile::SearchList(id, type)
//   FUN_004070dc  ResourceFile::SearchList(id, name)
//   FUN_00406cd0  ResourceDescription::Lock
//   FUN_0047703c  ControlsManager::CreateStreamedMappings
//   FUN_0046ff64  GaugeRenderer::ConfigureForModel(name, entity)
//   FUN_004dbb24  DEBUG_STREAM << (msg)      FUN_004d9c38  << flush
//                 (the two together == the Tell()/Msg() macro)
//   FUN_0040385c  Fail(msg, file, line)
//   DAT_004efc94  the global  application  pointer
//   DAT_00524e20  the debug message stream
//
// L4Application base member offsets observed in the decomp:
//   this+0x3c  controls manager   (GetControlsManager)
//   this+0x4c  gauge renderer     (GetGaugeRenderer)
//   this+0x50  mode manager       (GetModeManager)
//   this+0x68  resource file      (GetResourceFile)
//   this+0xd0  division-params present flag (cf. RP "divisionParameters")
//   controlsManager+0xc98  primaryControlType (2==ThrustMaster, 4==RIO)
//   modeManager+0x4  current mode mask    modeManager+0x8  saved mode mask
//

#include <bt.hpp>
#pragma hdrstop

#if !defined(BTL4APP_HPP)
#	include <btl4app.hpp>
#endif
#if !defined(BTL4VID_HPP)
#	include <btl4vid.hpp>
#endif
#if !defined(BTL4ARND_HPP)
#	include <btl4arnd.hpp>
#endif
#if !defined(BTL4GRND_HPP)
#	include <btl4grnd.hpp>
#endif
#if !defined(BTL4MSSN_HPP)
#	include <btl4mssn.hpp>
#endif
#if !defined(BTL4MODE_HPP)
#	include <btl4mode.hpp>
#endif
#if !defined(BTL4MPPR_HPP)
#	include <btl4mppr.hpp>
#endif
#if !defined(BTREG_HPP)
#	include <btreg.hpp>
#endif
#if !defined(L4CTRL_HPP)
#	include <l4ctrl.hpp>		// LBE4ControlsManager
#endif
#if !defined(CAMSHIP_HPP)
#	include <camship.hpp>		// CameraShip
#endif
#if !defined(CAMMPPR_HPP)
#	include <cammppr.hpp>		// CameraControlsMapper
#endif
#if !defined(L4MPPR_HPP)
#	include <l4mppr.hpp>		// Camera{Thrustmaster,RIO,L4}Mapper
#endif

//
// Audio sound-card environment variables (both must be present for the
// audio renderer to be built).  Observed at @004d35b0 as the live form of
// the front/rear-card check that RPL4Application left commented out.
//
#define AWE_FRONT_ENV_VAR	"AWE_FRONT"		// s_AWE_FRONT_0051ec1e
#define AWE_REAR_ENV_VAR	"AWE_REAR"		// s_AWE_REAR_0051ec28

//#############################################################################
// Message Support
//
const Receiver::HandlerEntry
	BTL4Application::MessageHandlerEntries[]=
{
	MESSAGE_ENTRY(BTL4Application, RunMission),
	MESSAGE_ENTRY(BTL4Application, StopMission)
};

BTL4Application::MessageHandlerSet
	BTL4Application::MessageHandlers(
		ELEMENTS(BTL4Application::MessageHandlerEntries),
		BTL4Application::MessageHandlerEntries,
		L4Application::GetMessageHandlers()		// WinTesla: GetMessageHandlers() replaces the static MessageHandlers
	);

//#############################################################################
// Shared Data Support
//
Derivation
	BTL4Application::ClassDerivations(
		L4Application::GetClassDerivations(),	// WinTesla: GetClassDerivations() (returns Derivation*)
		"BTL4Application"
	);

BTL4Application::SharedData
	BTL4Application::DefaultData(
		&BTL4Application::ClassDerivations,		// SharedData(Derivation*, MessageHandlerSet&)
		BTL4Application::MessageHandlers
	);


//
//#############################################################################
// BTL4Application                                                  @004d34c4
//#############################################################################
//
	BTL4Application::BTL4Application(
		HINSTANCE hInstance,
		HWND hWnd,
		ResourceFile *resource_file,
		ClassID class_ID,
		SharedData &shared_data
	):
		//
		// Base call FUN_0047b410(this, resource_file, 1, class_ID, shared_data).
		// The literal "1" is the L4 game identifier (BTL4) -- the analog of
		// RPL4Application passing the RPL4 enum to L4Application.  The WinTesla
		// L4Application ctor additionally takes (hInstance, hWnd) up front
		// (cf. RPL4Application::RPL4Application).
		//
		L4Application(hInstance, hWnd, resource_file, BTL4, class_ID, shared_data)
	{
		// vtable installed by the compiler == &PTR_FUN_0051ed9c
		Tell("BTL4Application::BTL4Application\n");	// FUN_004dbb24/004d9c38
	}

//
//#############################################################################
// ~BTL4Application                                                 @004d350c
//#############################################################################
//
	BTL4Application::~BTL4Application()
	{
		// FUN_004d350c is the compiler-generated scalar-deleting destructor;
		// the body is empty -- it merely reinstalls the vtable and chains to
		// ~L4Application (FUN_0047b624).
	}

//
//#############################################################################
// MakeRegistry                                                     @004d3538
//#############################################################################
//
	Registry*
		BTL4Application::MakeRegistry()
	{
		Check(this);
		return new BTRegistry(GetResourceFile());	// FUN_00402298(0x10) -> FUN_004c1548
	}

//
//#############################################################################
// MakeVideoRenderer                                                @004d3564
//#############################################################################
//
	VideoRenderer*
		BTL4Application::MakeVideoRenderer()
	{
		//
		// Only build the renderer when the division parameters are present
		// (this+0xd0 != 0), exactly as RPL4Application gates on
		// "divisionParameters".
		//
		// The DOS-era "division parameters present" flag (this+0xd0) is the
		// WinTesla public char* L4Application::divisionParameters.  The
		// reconstructed BTL4VideoRenderer ctor (btl4vid.hpp) takes the standard
		// Renderer calibration tuple (rate, complexity, priority) followed by
		// the interest type/depth; it derives the window + screen geometry from
		// the L4Application statics internally.
		//
		if (divisionParameters)
		{
			return
				new BTL4VideoRenderer(
					DefaultRendererRate,		// calibration rate
					MaxRendererComplexity,		// calibration complexity
					DefaultRendererPriority,	// calibration priority
					VisualInterestType,			// DAT_004e8238
					DefaultInterestDepth		// DAT_004e823c
				);
		}
		return NULL;
	}

//
//#############################################################################
// MakeAudioRenderer                                                @004d35b0
//#############################################################################
//
	AudioRenderer*
		BTL4Application::MakeAudioRenderer()
	{
		//
		// Both AWE sound-card environment variables must be present.  This is
		// the live form of the front/rear-card test RPL4Application left
		// commented out (blaster1/blaster2).
		//
		char *front_card = getenv(AWE_FRONT_ENV_VAR);	// FUN_004dee74
		char *rear_card  = getenv(AWE_REAR_ENV_VAR);

		if (front_card == NULL || rear_card == NULL)
		{
			return NULL;
		}

		//
		// FUN_0044e19c read the sample rate as a float out of the division
		// parameter block (@+0x10).  In WinTesla that block is the public
		// L4Application::divisionParameters char*; fall back to 1000.0f (the
		// RP default) when it is absent.  Second arg is the mission-review
		// flag (the 1995 binary hard-coded it off; DAT_004fd550 == 0).
		//
		RendererRate sample_rate =
			divisionParameters
				? (RendererRate)*reinterpret_cast<float*>(divisionParameters + 0x10)
				: (RendererRate)1000.0f;

		return
			new BTL4AudioRenderer(
				sample_rate,
				False
			);
	}

//
//#############################################################################
// MakeModeManager                                                  @004d3614
//#############################################################################
//
	ModeManager*
		BTL4Application::MakeModeManager()
	{
		// FUN_00402298(0xc) -> FUN_0044c2d0(p, 0x50421)
		BTL4ModeManager
			*manager = new BTL4ModeManager(BTL4ModeManager::ModeInitial);
		Check(manager);
		return manager;
	}

//
//#############################################################################
// MakeControlsManager                                              @004d3644
//#############################################################################
//
	ControlsManager*
		BTL4Application::MakeControlsManager()
	{
		// FUN_00402298(0xd10) -> FUN_00475c18(p)
		// WinTesla renamed the DOS L4ControlsManager to LBE4ControlsManager.
		LBE4ControlsManager
			*manager = new LBE4ControlsManager;
		Check(manager);

		//
		// Register this application as the controls owner / key-command
		// handler.  The DOS decomp showed a virtual call
		// (manager+0x68)->vtbl[+0x14](-1, this, 7, this) -- "mode mask = all,
		// receiver = this, message = KeyCommand (7), handler = this".  In
		// WinTesla that is the keyboard-group Add() the base
		// L4Application::MakeControlsManager performs.
		//
		manager->keyboardGroup[LBE4ControlsManager::KeyboardPC].Add(
			ModeManager::ModeAlwaysActive,
			this,
			KeyCommandMessageID,
			this
		);

		return manager;
	}

//
//#############################################################################
// MakeGaugeRenderer                                                @004d368c
//#############################################################################
//
	GaugeRenderer*
		BTL4Application::MakeGaugeRenderer(int *secondaryIndex, int *aux1Index, int *aux2Index)
	{
		//
		// Only build the gauge renderer when the L4GAUGE environment variable
		// is set -- matches RPL4Application's getenv("L4GAUGE") gate.
		//
		// SIGNATURE NOTE: the shipped BT override (@004d368c) took no arguments,
		// but the 2007 WinTesla engine WIDENED the base virtual to
		//   Application::MakeGaugeRenderer(int* secondaryIndex, int* aux1, int* aux2)
		// (the secondary/aux surface indices for windowed mode) and calls THAT
		// 3-arg virtual from Application::Initialize (APP.cpp:382).  A no-arg
		// method here only HIDES the virtual -- it never overrides it -- so the
		// engine's L4Application::MakeGaugeRenderer ran instead and built a BASE
		// L4GaugeRenderer (which never parses gauge\l4gauge.cfg -> empty symbol
		// table -> "undefined label 'bhk1Init'" -> no ports).  Matching the 3-arg
		// signature makes this the real override.  BT is fullscreen with no
		// secondary/aux gauge indices, so the args are ignored (the
		// BTL4GaugeRenderer ctor hardcodes windowed=false / NULL indices).
		//
		(void)secondaryIndex; (void)aux1Index; (void)aux2Index;

		if (getenv("L4GAUGE") == NULL)				// FUN_004dee74
		{
			return NULL;
		}

		return new BTL4GaugeRenderer;			// FUN_00402298(0x1c53c) -> FUN_004cbea0
	}

//
//#############################################################################
// MakeMission                                                      @004d36c4
//#############################################################################
//
	Mission*
		BTL4Application::MakeMission(
			NotationFile *notation_file,
			ResourceFile *resources
		)
	{
		// FUN_00402298(0xfc) -> FUN_004d2be0(p, notation_file, resources)
		return new BTL4Mission(notation_file, resources);
	}

//
//#############################################################################
// MakeViewpointEntity                                              @004d36f4
//#############################################################################
//
// Anchor function (822 bytes).  Creates the viewpoint entity for the local
// player and wires up its controls mapper + gauges.  Structurally identical
// to RPL4Application::MakeViewpointEntity, except the second case creates a
// Mech (class 0xbb9) instead of a VTV.
//
	Entity*
		BTL4Application::MakeViewpointEntity(Entity::MakeMessage *message)
	{
		Check(this);

		// WinTesla: GetControlsManager() yields the LBE4ControlsManager
		// (the renamed DOS L4ControlsManager); the deadband / primary-control
		// accessors live on it, not on the ControlsManager base.
		LBE4ControlsManager
			*controls = (LBE4ControlsManager*)GetControlsManager();	// *(this+0x3c)

		Entity
			*viewing_entity = NULL;
		char
			*primary_mapping_name = NULL;

		switch (message->classToCreate)				// *(message+0x1c)
		{
		case CameraShipClassID:						// 0x45
			//===============================================================
			// Create Camera ship
			//===============================================================
			{
				CameraShip
					*viewing_camera =
						CameraShip::Make((CameraShip::MakeMessage*)message);
				Check(viewing_camera);

				viewing_entity = viewing_camera;

				//------------------------------------------------------------
				// Create the controls-mapping subsystem resource
				//------------------------------------------------------------
				CameraControlsMapper::SubsystemResource
					control_subsystem_resource;

				Str_Copy(
					control_subsystem_resource.subsystemName,
					"ControlsMapper",
					sizeof(control_subsystem_resource.subsystemName)
				);
				control_subsystem_resource.classID = TrivialSubsystemClassID;	// 0xf
				control_subsystem_resource.subsystemModelSize =					// 0x30
					sizeof(control_subsystem_resource);

				CameraControlsMapper
					*camera_mapper;

				switch (controls->primaryControlType)		// *(controls+0xc98)
				{
				case LBE4ControlsManager::PrimaryThrustMaster:	// 2
					camera_mapper = new CameraThrustmasterMapper(	// FUN_0047ac5c
						viewing_camera,
						CameraShip::ControlsMapperSubsystem,
						&control_subsystem_resource
					);
					primary_mapping_name = "Thrustmaster";
					break;

				case LBE4ControlsManager::PrimaryRIO:				// 4
					camera_mapper = new CameraRIOMapper(			// FUN_0047ad3c
						viewing_camera,
						CameraShip::ControlsMapperSubsystem,
						&control_subsystem_resource
					);
					primary_mapping_name = "L4";
					break;

				default:
					camera_mapper = new CameraL4Mapper(				// FUN_0047aa78
						viewing_camera,
						CameraShip::ControlsMapperSubsystem,
						&control_subsystem_resource
					);
					// primary mapping name is NULL
					break;
				}

				//------------------------------------------------------------
				// Attach the mapping subsystem to the entity
				//------------------------------------------------------------
				viewing_camera->SetMappingSubsystem(camera_mapper);	// FUN_0042c630
			}
			break;

		case MechClassID:							// 0xbb9
			//===============================================================
			// Create Mech (the BattleTech analog of RP's VTV case)
			//===============================================================
			{
				Mech
					*viewing_mech = Mech::Make((Mech::MakeMessage*)message);	// FUN_004a2d48
				Check(viewing_mech);

				viewing_entity = viewing_mech;

				//------------------------------------------------------------
				// Control deadbanding
				//------------------------------------------------------------
				controls->SetJoystickDeadBand(0.05f);		// FUN_00476eac
				controls->SetThrottleDeadBand(0.05f);		// FUN_00476eec
				controls->SetPedalsDeadBand(0.05f);			// FUN_00476f10

				MechControlsMapper
					*mech_mapper;

				switch (controls->primaryControlType)		// *(controls+0xc98)
				{
				case LBE4ControlsManager::PrimaryThrustMaster:	// 2
					{
						//
						// The DOS binary heap-allocated the subsystem resource
						// and ref-counted it (FUN_00402460 + puVar2[3]++).  The
						// WinTesla Subsystem__SubsystemResource is a plain struct
						// that the Subsystem ctor copies out of, so we build it
						// on the stack and name it via Str_Copy -- the exact
						// idiom RP uses for its VTV/Camera mapper resources.
						//
						MechControlsMapper::SubsystemResource
							control_subsystem_resource;

						Str_Copy(
							control_subsystem_resource.subsystemName,
							"ControlsMapper",
							sizeof(control_subsystem_resource.subsystemName)
						);
						control_subsystem_resource.classID = TrivialSubsystemClassID;
						control_subsystem_resource.subsystemModelSize =
							sizeof(control_subsystem_resource);

						mech_mapper = new MechThrustmasterMapper(	// FUN_004d21d0
							viewing_mech,
							0,
							&control_subsystem_resource,
							(ClassID)MechControlsMapperClassID,		// 0x7dc
							MechThrustmasterMapper::DefaultData	// &DAT_0051dc68
						);
					}
					primary_mapping_name = "Thrustmaster";
					break;

				case LBE4ControlsManager::PrimaryRIO:				// 4
					{
						MechControlsMapper::SubsystemResource
							control_subsystem_resource;

						Str_Copy(
							control_subsystem_resource.subsystemName,
							"ControlsMapper",
							sizeof(control_subsystem_resource.subsystemName)
						);
						control_subsystem_resource.classID = TrivialSubsystemClassID;
						control_subsystem_resource.subsystemModelSize =
							sizeof(control_subsystem_resource);

						mech_mapper = new MechRIOMapper(			// FUN_004d266c
							viewing_mech,
							0,
							&control_subsystem_resource,
							(ClassID)MechControlsMapperClassID,		// 0x7dc
							MechRIOMapper::DefaultData			// &DAT_0051dcf0
						);
					}
					primary_mapping_name = "L4";
					break;

				default:
					//
					// TODO(bring-up): the shipped pod binary Fail()s here when no
					// RIO/Thrustmaster is detected (primaryControlType==None on a
					// dev box with no joystick).  To let the spawn pipeline finish
					// and the mech reach the render/frame loop, fall back to the
					// RIO mapper (the pod's real L4 device).  Keyboard/gamepad
					// remap is a separate pod-bring-up task (CLAUDE.md Phase 8).
					//
					DEBUG_STREAM << "*****Mech has no controls mapping -- bring-up RIO fallback*****";
					{
						MechControlsMapper::SubsystemResource
							control_subsystem_resource;

						Str_Copy(
							control_subsystem_resource.subsystemName,
							"ControlsMapper",
							sizeof(control_subsystem_resource.subsystemName)
						);
						control_subsystem_resource.classID = TrivialSubsystemClassID;
						control_subsystem_resource.subsystemModelSize =
							sizeof(control_subsystem_resource);

						mech_mapper = new MechRIOMapper(
							viewing_mech,
							0,
							&control_subsystem_resource,
							(ClassID)MechControlsMapperClassID,
							MechRIOMapper::DefaultData
						);
					}
					primary_mapping_name = "L4";
					break;
				}

				//------------------------------------------------------------
				// Attach the mapping subsystem and enable collision
				//------------------------------------------------------------
				viewing_mech->SetMappingSubsystem(mech_mapper);	// FUN_0049fe40
				viewing_mech->StartCollisionAssistant();		// vtbl[+0x38]
			}
			break;
		}

		//-------------------------------------------------------------------
		// Load the control-mapping resource stream for the entity
		//-------------------------------------------------------------------
		if (primary_mapping_name != NULL)
		{
			ResourceDescription
				*resource_description =
					application->GetResourceFile()->SearchList(	// *(DAT_004efc94+0x68)
						viewing_entity->GetResourceID(),		// piVar5[0x6f]
						ResourceDescription::ControlMappingsListResourceType	// 6
					);

			if (resource_description != NULL)
			{
				resource_description =
					GetResourceFile()->SearchList(			// *(this+0x68)
						resource_description->resourceID,
						primary_mapping_name
					);

				if (resource_description != NULL)
				{
					resource_description->Lock();			// FUN_00406cd0
					controls->CreateStreamedMappings(		// FUN_0047703c
						viewing_entity,
						(int*)resource_description->resourceAddress
					);
					resource_description->Unlock();
				}
			}
		}

		//-------------------------------------------------------------------
		// Configure the gauges for this entity
		//-------------------------------------------------------------------
		BTL4GaugeRenderer
			*gauge_renderer = (BTL4GaugeRenderer*)GetGaugeRenderer();	// *(this+0x4c)

		if (getenv("BT_VSS_LOG"))
			DEBUG_STREAM << "[vss] MakeViewpointEntity: gaugeRenderer="
						<< (int)(gauge_renderer != NULL) << " -> ConfigureForModel(Init)\n" << std::flush;
		if (gauge_renderer != NULL)
		{
			gauge_renderer->ConfigureForModel("Init", viewing_entity);	// FUN_0046ff64
		}

		return viewing_entity;
	}

//
//#############################################################################
// RunMissionMessageHandler                                         @004d3a2c
//#############################################################################
//
	void
		BTL4Application::RunMissionMessageHandler(RunMissionMessage *message)
	{
		Tell("BTL4Application::RunMissionMessageHandler\n");	// FUN_004dbb24/004d9c38

		//
		// Turn on the plasma score display: save the current mode mask and
		// set the ModePlasmaDisplay bit (1<<22 == 0x400000) on the mode
		// manager (this+0x50).
		//
		BTL4ModeManager
			*mode_manager = (BTL4ModeManager*)GetModeManager();	// *(this+0x50)

		// WinTesla ModeManager exposes the mask through AddModeMask(), which
		// stashes the old mask into previousModeMask (== the DOS "savedMode"
		// = "currentMode" step) and ORs in the new bits (== "currentMode |=").
		mode_manager->AddModeMask(BTL4ModeManager::ModePlasmaDisplay);	// |= 0x400000

		Tell("Turning Plasma Score Display On\n");

		L4Application::RunMissionMessageHandler(message);		// FUN_0047b788
	}

//
//#############################################################################
// StopMissionMessageHandler                                        @004d3a94
//#############################################################################
//
	void
		BTL4Application::StopMissionMessageHandler(StopMissionMessage *message)
	{
		//
		// Turn off the plasma score display: save the current mode mask and
		// clear the ModePlasmaDisplay bit (~0x400000 == 0xffbfffff).
		//
		BTL4ModeManager
			*mode_manager = (BTL4ModeManager*)GetModeManager();	// *(this+0x50)

		// RemoveModeMask() = stash old mask into previousModeMask (the DOS
		// "savedMode = currentMode") then clear the bits ("currentMode &= ~").
		mode_manager->RemoveModeMask(BTL4ModeManager::ModePlasmaDisplay);	// &= 0xffbfffff

		L4Application::StopMissionMessageHandler(message);		// FUN_0047b864

		Tell("Turning Plasma Score Display Off\n");
	}
