/*
**	Command & Conquer Renegade(tm)
**	Copyright 2025 Electronic Arts Inc.
**
**	This program is free software: you can redistribute it and/or modify
**	it under the terms of the GNU General Public License as published by
**	the Free Software Foundation, either version 3 of the License, or
**	(at your option) any later version.
**
**	This program is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**	GNU General Public License for more details.
**
**	You should have received a copy of the GNU General Public License
**	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/***********************************************************************************************
 ***                            Confidential - Westwood Studios                              ***
 ***********************************************************************************************
 *                                                                                             *
 *                 Project Name : Commando                                                     *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Commando/combatgmode.cpp                     $*
 *                                                                                             *
 *                      $Author:: Steve_t                                                     $*
 *                                                                                             *
 *                     $Modtime:: 2/23/02 2:04p                                               $*
 *                                                                                             *
 *                    $Revision:: 267                                                         $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "combatgmode.h"
#include "level.h"
#include "input.h"
#include "cnetwork.h"
#include "AudibleSound.H"
#include "debug.h"
#include "registry.h"
#include "_globals.h"
#include "console.h"
#include "radar.h"
#include "spawn.h"
#include "ccamera.h"
#include "scorescreen.h"
#include "devoptions.h"
#include "useroptions.h"
#include "systemsettings.h"
#include "gameobjmanager.h"
#include "building.h"
#include "saveloadstatus.h"
#include "playertype.h"
#include "bitpackids.h"
#include "vehicle.h"
#include "doors.h"
#include "elevator.h"
#include "msgloop.h"
#include "assetmgr.h"
#include "font3d.h"
#include "wwprofile.h"
#include "gametype.h"
#include "messagewindow.h"
#include "objectives.h"
#include "multihud.h"
#include "textureloader.h"
#include "wolgmode.h"
#include "renegadedialogmgr.h"
#include "textdisplay.h"
#include "playermanager.h"
#include "teammanager.h"
#include "consolefunction.h"
#include "overlay.h"
#include "gameinitmgr.h"
#include "campaign.h"
#include "diaglog.h"
#include "god.h"
#include "sbbomanager.h"
#include "savegame.h"
#include "commandosaveload.h"
#include "dlgmpingamechat.h"
#include "loadingevent.h"
#include "cheatmgr.h"
#include "menubackdrop.h"
#include "translatedb.h"
#include "thread.h"
#include "dlgcncteaminfo.h"
#include "dlgcncbattleinfo.h"
#include "bandwidthgraph.h"
#include "suicideevent.h"
#include "assetstatus.h"
#include "stylemgr.h"
#include "gametype.h"
#include "wwmemlog.h"
#include "directinput.h"
#include "cncmodesettings.h"
#include "announceevent.h"
#include "fastallocator.h"
#include "dlgcncserverinfo.h"
#include "radiocommanddisplay.h"
#include "hudinfo.h"
#include "string_ids.h"
#include "radiocommanddisplay.h"
#include "consolemode.h"
#include "specialbuilds.h"
#include <wwlib\realcrc.h>
#include "demosupport.h"
#include "natter.h"
#include "gamespyadmin.h"
#include "dialogtests.h"
#include "dialogmgr.h"
#include "GameSpy_QnR.h"

/*
**
*/
extern bool g_is_loading;
bool g_b_core_restart;//TSS081401
bool g_client_quit = false;

AudibleSoundClass	*	CombatGameModeClass::BackgroundMusic	= NULL;
int						CombatGameModeClass::IsHudShown			= true;
static int				ForceGod											= false;
static bool				ForceGodPending								= true;
static int				DefaultToFirstPerson							= true;
static bool				PendingCampaignContinue						= false;


/*
**
*/
class	CombatGameMiscHandlerClass : public CombatMiscHandlerClass {
public:
	virtual	void	Mission_Complete( bool success );
	virtual	void	Star_Killed( void );
};

CombatGameMiscHandlerClass		GameMiscHandler;

/*
**
*/
static void Combat_To_Menu (RenegadeDialogMgrClass::LOCATION location)
{
   //
	// Remember: Combat and Menu are not mutually exclusive.
	// Therefore this function may be called when menu is already active.
	//
	if (!GameModeManager::Find("Menu")->Is_Active()) {

		//
		// Do not suspend Combat in Multiplayer game!
		//
		if (IS_SOLOPLAY) {
			GameModeManager::Find( "Combat" )->Suspend();	// suspend Combat Mode
		}

		//if (IS_SOLOPLAY) {
		if (IS_MISSION) {
			RenegadeDialogMgrClass::Goto_Location (location);
		} else {
			RenegadeDialogMgrClass::Goto_Location (RenegadeDialogMgrClass::LOC_CNC_REFERENCE);
		}
	}
}

/*
**
*/
static void Start_In_Game_Help(void)
{
	//
	// Remember: Combat and Menu are not mutually exclusive.
	// Therefore this function may be called when menu is already active.
	//
	if (!GameModeManager::Find("Menu")->Is_Active()) {

		//
		// Do not suspend Combat in Multiplayer game!
		//
		if (IS_SOLOPLAY) {
			GameModeManager::Find( "Combat" )->Suspend();	// suspend Combat Mode
		}

		RenegadeDialogMgrClass::Goto_Location (RenegadeDialogMgrClass::LOC_IN_GAME_HELP);
	}
}

void	CombatGameModeClass::Combat_Keyboard( void )
{
	WWPROFILE( "Combat_Keyboard" );

#ifdef ATI_DEMO_HACK
	// HACK: Make double-ESC exit back to desktop
	static unsigned esc_pressed_time=0x7ffff;
	if ( Input::Get_State( INPUT_FUNCTION_MENU_TOGGLE ) ) {
		unsigned current_time=WW3D::Get_Sync_Time();
		if ((current_time-esc_pressed_time)<5000) {
			cDevOptions::QuickFullExit.Set(true);
			esc_pressed_time=0x7ffff;
		}
		else {
			Get_Text_Display()->Print_System( "Press ESC again to exit to desktop...\n" );
		}
		esc_pressed_time=current_time;
	}


	// HACK: In ATI demo toggle the statistics display using TAB
	if ( Input::Get_State( INPUT_FUNCTION_EVA_MISSION_OBJECTIVES_TOGGLE ) ) {
		if (StatisticsDisplayManager::Is_Current_Display( "fps" )) {
			StatisticsDisplayManager::Set_Display( "off" );
		}
		else {
			StatisticsDisplayManager::Set_Display( "fps" );
		}
	}
#else
	bool to_menu=Input::Get_State( INPUT_FUNCTION_MENU_TOGGLE );
	// If game doesn't have a focus and combat is active, suspend combat by entering menu
	if (!GameInFocus) {
		if (IS_SOLOPLAY && COMBAT_CAMERA && !COMBAT_CAMERA->Is_In_Cinematic() ) {
			to_menu|=GameModeManager::Find( "Combat" )->Is_Active();
		}
	}
	if ( to_menu ) {
		  Combat_To_Menu( RenegadeDialogMgrClass::LOC_ENCYCLOPEDIA );
	}

	//
	//	Handle the in-game EVA mission objectives window
	//
	if ( IS_MISSION && Input::Get_State( INPUT_FUNCTION_EVA_MISSION_OBJECTIVES_TOGGLE ) ) {
		if ( ObjectiveManager::Is_Viewer_Displayed() ) {
			ObjectiveManager::Page_Down_Viewer();
		} else {
			ObjectiveManager::Display_Viewer( true );
		}
   }
#endif

	if ( Input::Get_State( INPUT_FUNCTION_HELP_SCREEN ) ) {
		Start_In_Game_Help();
	}

	if ( IS_SOLOPLAY && Input::Get_State( INPUT_FUNCTION_EVA_OBJECTIVES_SCREEN ) ) {
		Combat_To_Menu( RenegadeDialogMgrClass::LOC_OBJECTIVES );
	}

	if ( IS_SOLOPLAY && Input::Get_State( INPUT_FUNCTION_EVA_MAP_SCREEN ) ) {
		Combat_To_Menu( RenegadeDialogMgrClass::LOC_MAP );
	}

	//
	// Display the server info screen
	//
	if (!IS_MISSION && Input::Get_State( INPUT_FUNCTION_SERVER_INFO_TOGGLE )) {
		START_DIALOG (CNCServerInfoDialogClass);
	}

	//
	//	Display the team information page
	//
	if (!IS_MISSION && cNetwork::I_Am_Client() && COMBAT_STAR != NULL) {
		if (Input::Get_State( INPUT_FUNCTION_TEAM_INFO_TOGGLE )) {
			START_DIALOG (CNCTeamInfoDialogClass);
		} else if (Input::Get_State( INPUT_FUNCTION_BATTLE_INFO_TOGGLE )) {
			START_DIALOG (CNCBattleInfoDialogClass);
		}/* else if (Input::Get_State( INPUT_FUNCTION_SERVER_INFO_TOGGLE )) {
			START_DIALOG (CNCServerInfoDialogClass);
		}*/

		// Handle radio commands
		for (int radioCmdIndex = INPUT_FUNCTION_RADIO_CMD_01; radioCmdIndex <= INPUT_FUNCTION_RADIO_CMD_30; ++radioCmdIndex) {
			if (Input::Get_State((InputFunction)radioCmdIndex)) {
				CNCModeSettingsDef* cncDef = CNCModeSettingsDef::Get_Instance();

				if (cncDef) {
					int radioCmdNum = (radioCmdIndex - INPUT_FUNCTION_RADIO_CMD_01);
					int radioCmd = cncDef->Get_Radio_Command(radioCmdNum);

					if (radioCmd) {
						CSAnnouncement* announce = new CSAnnouncement;
						assert(announce != NULL);
						announce->Init(cNetwork::Get_My_Team_Number(), radioCmd, ANNOUNCEMENT_TEAM, radioCmdNum);
						RadioCommandDisplayClass::Reset_Display_Timer();
					}
				}

				break;
			}
		}
	}

	if (!IS_MISSION && Input::Get_State( INPUT_FUNCTION_EVA_MISSION_OBJECTIVES_TOGGLE )) {
		//MultiHUDClass::Toggle_Verbose_Lists();
		MultiHUDClass::Next_Playerlist_Format();
	}

#ifdef WWDEBUG
   if ( Input::Get_State( INPUT_FUNCTION_QUICK_FULL_EXIT ) ) {
		cDevOptions::QuickFullExit.Set(true);
		WWDEBUG_SAY(("Quick full exit requested by keypress.\n"));
	}
	if (cDevOptions::QuickFullExit.Is_True()) {

		GameInitMgrClass::End_Game();

		GameInitMgrClass::End_Client_Server();

		extern void Stop_Main_Loop (int exitCode);
		Stop_Main_Loop (EXIT_SUCCESS);
	}
#endif

	if (	(The_Game() != NULL &&
		    (Input::Get_State(INPUT_FUNCTION_BEGIN_PUBLIC_MESSAGE) ||
		     (Input::Get_State(INPUT_FUNCTION_BEGIN_TEAM_MESSAGE) && cNetwork::I_Am_Client()))) &&
			!IS_MISSION)
	{
		MPIngameChatPopupClass * p_dialog = new MPIngameChatPopupClass;
		WWASSERT(p_dialog != NULL);

		//
		//	Configure the dialog
		//
		if (Input::Get_State(INPUT_FUNCTION_BEGIN_PUBLIC_MESSAGE)) {
			p_dialog->Set_Default_Type(TEXT_MESSAGE_PUBLIC);
		} else {
			p_dialog->Set_Default_Type(TEXT_MESSAGE_TEAM);
		}

		p_dialog->Start_Dialog();
		REF_PTR_RELEASE (p_dialog);
	}

	if ( Input::Get_State( INPUT_FUNCTION_QUICKSAVE )) {
		// Only for singeplay
		if ( IS_MISSION ) {
			Quick_Save();
		}
	}
}

/*
** called when the mode is activated
*/
void	CombatGameModeClass::Init()
{
	Debug_Say(("CombatGameModeClass::Init\n"));

	if (!IS_MISSION) {
		MultiHUDClass::Init();
	}

	PendingCampaignContinue	= false;

	//
	//	Initialize the radio command display window
	//
	RadioCommandDisplayClass::Initialize ();

	//
	// Notify combat about the state of the CameraLockedToTurret user option.
	//
	VehicleGameObj::Set_Camera_Locked_To_Turret(cUserOptions::CameraLockedToTurret.Get());
}

/*
** called when the mode is deactivated
*/
void 	CombatGameModeClass::Shutdown()
{
	Debug_Say(("CombatGameModeClass::Shutdown\n"));

   Core_Shutdown();

	//
	//	Shutdown the radio command display window
	//
	RadioCommandDisplayClass::Shutdown ();
	return ;
}

class LoadingScreenClass
{
	MenuBackDropClass	backdrop;
	Render2DSentenceClass backdropText;
	Render2DSentenceClass backdropText2;	// The BIG text
	float	LoadTime;
	float	LoadPercentage;
	float	LoadPercentageDrawn;
	float	LoadPercentageClamp;
	float	LoadPercentageRate;

public:
	LoadingScreenClass()
	{
		int color = 0xFFFFFFFF;

		WWMEMLOG(MEM_GAMEDATA);

		backdropText.Set_Texture_Size_Hint( 256 );
		backdropText2.Set_Texture_Size_Hint( 256 );

		LoadTime = 0.001f;
		LoadPercentage = 0;
		LoadPercentageDrawn = 0;
		LoadPercentageRate = 0;

		FontCharsClass *font	= StyleMgrClass::Peek_Font( StyleMgrClass::FONT_INGAME_TXT );
		backdropText.Set_Font( font );

		font = StyleMgrClass::Peek_Font( StyleMgrClass::FONT_INGAME_BIG_TXT );
		backdropText2.Set_Font( font );

		// Parse Descriptions
		int count = CampaignManager::Get_Backdrop_Description_Count();
		for ( int i = 0; i < count; i++ ) {
			const char * read = CampaignManager::Get_Backdrop_Description( i );
//			Debug_Say(( "Parse %s\n", read ));
			StringClass desc = read;
			while ( desc.Get_Length() && desc[0] <= ' ' ) desc.Erase( 0, 1 );
			while ( desc.Get_Length() && desc[desc.Get_Length()-1]<=' ' ) desc.Erase(desc.Get_Length()-1, 1 );

			// Parse Big Translated Text
			if ( ::strnicmp( "Text2", desc, 5 ) == 0 ) {
				desc.Erase( 0, 5 );
				while ( desc.Get_Length() && desc[0] <= ' ' ) desc.Erase( 0, 1 );
				float x,y;
				::sscanf( desc, "%f,%f,", &x, &y );
				const char * str = ::strchr( desc, ',' );
				if ( str != NULL ) {
					str = ::strchr( str+1, ',' );
					if ( str != NULL ) {
						WideStringClass wide_str = TranslateDBClass::Get_String( str+1 );
						backdropText2.Build_Sentence( wide_str );
						// Scale
						x *= Render2DClass::Get_Screen_Resolution().Right / 640.0f;
						y *= Render2DClass::Get_Screen_Resolution().Bottom / 480.0f;
						backdropText2.Set_Location( Vector2( (int)x, (int)y ) );
						backdropText2.Draw_Sentence( color );
						color=0xFFFFFFFF;
						backdropText2.Set_Wrapping_Width( 0 );
					}
				}
			}

			// Parse Translated Text
			if ( ::strnicmp( "Text", desc, 4 ) == 0 ) {
				desc.Erase( 0, 4 );
				while ( desc.Get_Length() && desc[0] <= ' ' ) desc.Erase( 0, 1 );
				float x,y;
				::sscanf( desc, "%f,%f,", &x, &y );
				const char * str = ::strchr( desc, ',' );
				if ( str != NULL ) {
					str = ::strchr( str+1, ',' );
					if ( str != NULL ) {
						WideStringClass wide_str = TranslateDBClass::Get_String( str+1 );
						backdropText.Build_Sentence( wide_str );
						// Scale
						x *= Render2DClass::Get_Screen_Resolution().Right / 640.0f;
						y *= Render2DClass::Get_Screen_Resolution().Bottom / 480.0f;
						backdropText.Set_Location( Vector2( (int)x, (int)y ) );
						backdropText.Draw_Sentence( color );
						color=0xFFFFFFFF;
						backdropText.Set_Wrapping_Width( 0 );
					}
				}
			}

			// Set Big Wrapping Width
			if ( ::strnicmp( "Wrap2", desc, 5 ) == 0 ) {
				desc.Erase( 0, 5 );
				while ( desc.Get_Length() && desc[0] <= ' ' ) desc.Erase( 0, 1 );
				float w;
				::sscanf( desc, "%f,", &w );
				// Scale
				w *= Render2DClass::Get_Screen_Resolution().Right / 640.0f;
				backdropText2.Set_Wrapping_Width( w );
			}

			// Set Wrapping Width
			if ( ::strnicmp( "Wrap", desc, 4 ) == 0 ) {
				desc.Erase( 0, 4 );
				while ( desc.Get_Length() && desc[0] <= ' ' ) desc.Erase( 0, 1 );
				float w;
				::sscanf( desc, "%f,", &w );
				// Scale
				w *= Render2DClass::Get_Screen_Resolution().Right / 640.0f;
				backdropText.Set_Wrapping_Width( w );
			}

			// Parse test Text
			if ( ::strnicmp( "Test", desc, 4 ) == 0 ) {
				desc.Erase( 0, 4 );
				while ( desc.Get_Length() && desc[0] <= ' ' ) desc.Erase( 0, 1 );
				float x,y;
				::sscanf( desc, "%f,%f,", &x, &y );
				const char * str = ::strchr( desc, ',' );
				if ( str != NULL ) {
					str = ::strchr( str+1, ',' );
					if ( str != NULL ) {
						WideStringClass wide_str;
						wide_str.Convert_From( str+1 );
						backdropText.Build_Sentence( wide_str );
						// Scale
						x *= Render2DClass::Get_Screen_Resolution().Right / 640.0f;
						y *= Render2DClass::Get_Screen_Resolution().Bottom / 480.0f;
						backdropText.Set_Location( Vector2( (int)x, (int)y ) );
						backdropText.Draw_Sentence( color );
						color=0xFFFFFFFF;
						backdropText.Set_Wrapping_Width( 0 );
					}
				}
			}

			// Parse back model
			if ( ::strnicmp( "Model", desc, 5 ) == 0 ) {
				desc.Erase( 0, 5 );
				while ( desc.Get_Length() && desc[0] <= ' ' ) desc.Erase( 0, 1 );

				backdrop.Set_Model(desc);
				StringClass anim_name;
				anim_name.Format( "%s.%s", desc, desc );
				backdrop.Set_Animation( anim_name );
				backdrop.Set_Animation_Percentage( 0 );
			}

			if ( ::strnicmp( "Color", desc, 5 ) == 0 ) {
				desc.Erase( 0, 5 );
				while ( desc.Get_Length() && desc[0] <= ' ' ) desc.Erase( 0, 1 );
				float r,g,b;
				::sscanf( desc, "%f,%f,%f", &r, &g, &b );

				Vector3 c( r/255.0f, g/255.0f, b/255.0f );
				color = c.Convert_To_ARGB();
			}


		}
		SaveLoadStatus::Reset_Status_Count();
	}

	~LoadingScreenClass()
	{
	}

	float	Get_Predicted_Percentage( int state )
	{
		switch( state )
		{
#define	TOTAL	(18.0f)
			case 0:	return 0.1f / TOTAL;
			case 1:	return 0.2f / TOTAL;
			case 2:	return 0.3f / TOTAL;
			case 3:	return 0.7f / TOTAL;
			case 4:	return 3.8f / TOTAL;
			case 5:	return 15.5f / TOTAL;
			case 6:	return 17.6f / TOTAL;
			default:	return 1.0f;
		}
	}


	void Render(bool update_network = false)
	{
		TimeManager::Update_Frame_Time();
		LoadTime += TimeManager::Get_Frame_Seconds();

	   WW3D::Begin_Render( true, true, Vector3(0.0f,0.0f,0.0f), update_network ? &cNetwork::Update : NULL);

		backdrop.Render();
		backdropText.Render();
		backdropText2.Render();

		static int last_count = -1;
		static int _last_percent_drawn = -1;
		if ( last_count != CombatManager::Get_Load_Progress() ) {
			last_count = CombatManager::Get_Load_Progress();
			Debug_Say(( " ****** Status Count %d at %f\n", last_count, LoadTime ));
			LoadPercentage = Get_Predicted_Percentage( last_count );
			LoadPercentageRate = LoadPercentage / LoadTime;
			LoadPercentageClamp = Get_Predicted_Percentage( last_count+1 );
		}

		LoadPercentage += LoadPercentageRate * TimeManager::Get_Frame_Seconds();
		LoadPercentage = WWMath::Clamp( LoadPercentage, 0, LoadPercentageClamp );
		LoadPercentageDrawn += ( LoadPercentage - LoadPercentageDrawn ) * 0.1f;
		backdrop.Set_Animation_Percentage( LoadPercentageDrawn );
		if (ConsoleBox.Is_Exclusive() && _last_percent_drawn != LoadPercentageDrawn) {
			_last_percent_drawn = LoadPercentageDrawn;
			ConsoleBox.Print("Load %d%% complete\r", (int)(LoadPercentageDrawn * 100.0f));
		}

#if 0
		StringClass txt=SaveLoadStatus::Get_Status_Text(0);
		txt=SaveLoadStatus::Get_Status_Text(1);
#endif

		WW3D::End_Render();
	}
};

#define LOADTIME_NETWORK_UPDATE if (!IS_SOLOPLAY) cNetwork::Update()


/*
**
*/
void CombatGameModeClass::Load_Level( void )
{
	WWLOG_PREPARE_TIME_AND_MEMORY("CombatGameModeClass::Load_Level");
	WWMEMLOG(MEM_GAMEDATA);
	Debug_Say(("CombatGameModeClass::Load_Level\n"));

	ConsoleBox.Print("Loading level %s\n", The_Game()->Get_Map_Name());

	CombatManager::Set_Load_Progress(0);
	LoadingScreenClass loading_screen;	// Try moving this to very start of loading
	loading_screen.Render(true);

	// Hack load reg for default first person.  Is dont again later.
	Load_Registry_Keys();
	WWLOG_INTERMEDIATE("Load_REgistry_Keys");

	// Flush out current level
	INIT_STATUS("Release current level");
	LevelManager::Release_Level();
	WWLOG_INTERMEDIATE("Releasing old level");

	if (!ConsoleBox.Is_Exclusive()) {
		WW3D::_Invalidate_Textures();
		WWLOG_INTERMEDIATE("WW3D::_Invalidate_Textures()");
		AssetStatusClass::Peek_Instance()->Enable_Load_On_Demand_Reporting(false);
	}

//	LoadingScreenClass loading_screen;
//	loading_screen.Render(true);

	PendingCampaignContinue	= false;
	g_is_loading = true;

	//
	// Stop the network layer from processing packets. This is needed in case a packet import or export accesses the datasafe
	// from the main thread while the loader thread is loading stuff into the datasafe. This won't shut off packet acks so no-one
	// will be disconnected as a result of this.
	//
	if (cNetwork::PServerConnection) {
		cNetwork::PServerConnection->Allow_Packet_Processing(false);
	}

	if (!IS_SOLOPLAY && cNetwork::PClientConnection != NULL) {
		cNetwork::PClientConnection->Allow_Extra_Timeout_For_Loading();
	}

	CombatManager::Pre_Load_Level(ConsoleBox.Is_Exclusive() ? false : true);

	INIT_STATUS("Apply system settings");
	SystemSettings::Apply_All();

	CombatManager::Set_Combat_Misc_Handler( &GameMiscHandler );

	// Flush out current level
//	INIT_STATUS("Release current level");
//	LevelManager::Release_Level();

	WWASSERT(PTheGameData != NULL);
	StringClass map_name(The_Game()->Get_Map_Name(),true);
	WWASSERT( !map_name.Is_Empty() );

	bool preload_assets = true;
#ifdef WWDEBUG
	preload_assets = cDevOptions::PreloadAssets.Get();
#endif

	DIAG_LOG(( "LOAD", "%s", map_name ));

	NetworkObjectMgrClass::Set_Is_Level_Loading (true);

	TextureLoader::Suspend_Texture_Load();

	CombatManager::Load_Level_Threaded( map_name, preload_assets );

	WWLOG_INTERMEDIATE("Level load preprocessing");

	while (!CombatManager::Is_Load_Level_Complete() ) {
		loading_screen.Render(true);
	   Windows_Message_Handler();
		LOADTIME_NETWORK_UPDATE;
		ThreadClass::Sleep_Ms(50);
	}
	WWLOG_INTERMEDIATE("Threaded level load");

	GenericDataSafeClass::Set_Preferred_Thread(GetCurrentThreadId());
	TextureLoader::Continue_Texture_Load();
	WWLOG_INTERMEDIATE("TextureLoader::Continue_Texture_Load()");

	LOADTIME_NETWORK_UPDATE;

	// Post load processing ----------------------------------------------------
	INIT_STATUS("Post_Load_Processing");
	loading_screen.Render(true);
   Windows_Message_Handler();
	SaveLoadSystemClass::Post_Load_Processing(IS_SOLOPLAY ? NULL : &cNetwork::Update);
	NetworkObjectMgrClass::Set_Is_Level_Loading (false);
	WWLOG_INTERMEDIATE("SaveLoadSystemClass::Post_Load_Processing(&cNetwork::Update)");

	INIT_STATUS("Post_Load_Level");
	loading_screen.Render(true);
   Windows_Message_Handler();
	LOADTIME_NETWORK_UPDATE;
	CombatManager::Post_Load_Level();
	WWLOG_INTERMEDIATE("CombatManager::Post_Load_Level()");

	INIT_STATUS("Post_Load_Id_Uniqueness_Check");
	loading_screen.Render(true);
   Windows_Message_Handler();
	LOADTIME_NETWORK_UPDATE;
	Post_Load_Id_Uniqueness_Check();
	WWLOG_INTERMEDIATE("Post_Load_Id_Uniqueness_Check()");

	INIT_STATUS("Post_Load_Dynamic_Object_Filtering");
	loading_screen.Render(true);
   Windows_Message_Handler();
	LOADTIME_NETWORK_UPDATE;
	Post_Load_Dynamic_Object_Filtering();
	WWLOG_INTERMEDIATE("Post_Load_Dynamic_Object_Filtering()");

	// Spawn point validation --------------------------------------------------
	INIT_STATUS("Spawn_Point_Validation");
	loading_screen.Render(true);
   Windows_Message_Handler();
	LOADTIME_NETWORK_UPDATE;
	Spawn_Point_Validation();
	WWLOG_INTERMEDIATE("Spawn_Point_Validation()");
	// -------------------------------------------------------------------------

   //DefinitionMgrClass::List_Available_Definitions();

	INIT_STATUS("Compute world size");
	Compute_World_Size();
	WWLOG_INTERMEDIATE("Compute_World_Size()");

	//
	// Set precision for other stuff
	// This stuff should be placed elsewhere... when there are a few
	// more of them.
	//
	ControlClass::Set_Precision();
	//SoldierGameObj::Set_Precision();
	HumanStateClass::Set_Precision();
	VehicleGameObj::Set_Precision();
	DoorPhysClass::Set_Precision();
	ElevatorPhysClass::Set_Precision();
	DefenseObjectClass::Set_Precision();
	BuildingGameObj::Set_Precision();

	// Override background color if this is a dedicated server
	if (cNetwork::I_Am_Only_Server()) {
		GameModeManager::Set_Background_Color( Vector3( 0.0f, 0.0f, 0.4f ) );
	}

	INIT_STATUS("Registry keys");
   Load_Registry_Keys();
   Save_Registry_Keys();
	WWLOG_INTERMEDIATE("SaveLoadRegistry");

	// Radar init --------------------------------------------------------------
	INIT_STATUS("Init radar class");		// Init the radar after the game is loaded (so we have the global settings
	loading_screen.Render(true);
   Windows_Message_Handler();
	LOADTIME_NETWORK_UPDATE;
	RadarManager::Init();
	WWASSERT(PTheGameData != NULL);
	RadarManager::Set_Radar_Mode(The_Game()->Get_Radar_Mode());
	WWLOG_INTERMEDIATE("Init Radar");
	// -------------------------------------------------------------------------

	const bool is_reloaded = true;
	The_Game()->Reset_Game(is_reloaded);
	WWLOG_INTERMEDIATE("The_Game()->Reset_Game(is_reloaded)");

	// Buildings init ----------------------------------------------------------
	// After the level loads, the buildings collect all of their components
	INIT_STATUS("Init_Buildings");
	loading_screen.Render(true);
   Windows_Message_Handler();
	LOADTIME_NETWORK_UPDATE;
	GameObjManager::Init_Buildings();
	WWLOG_INTERMEDIATE("GameObjManager::Init_Buildings()");
	// -------------------------------------------------------------------------

	// Update texture loader ---------------------------------------------------
	// This is needed here as we want to apply all textures that may have changed
	// during the game loading before the game actually starts.
	INIT_STATUS("Init_Textures");
	loading_screen.Render(true);
   Windows_Message_Handler();
	LOADTIME_NETWORK_UPDATE;
	TextureLoader::Update(IS_SOLOPLAY ? NULL : &cNetwork::Update);
	WWLOG_INTERMEDIATE("TextureLoader::Update(&cNetwork::Update)");
	// -------------------------------------------------------------------------

	//	Notify the game of "game start"
	WWASSERT(PTheGameData != NULL);
	The_Game()->On_Game_Begin();
	WWLOG_INTERMEDIATE("The_Game()->On_Game_Begin()");

	ForceGodPending = ForceGod != 0;

/*	if ( GameModeManager::Find( "Overlay3D" ) ) {
	   ((Overlay3DGameModeClass*)GameModeManager::Find( "Overlay3D" ))->Start_Intro();
	}
*/

	LOADTIME_NETWORK_UPDATE;
	AssetStatusClass::Peek_Instance()->Enable_Load_On_Demand_Reporting(true);
	g_is_loading = false;

	ConsoleBox.Print("Load %d%% complete\n", 100);
	ConsoleBox.Print("Level loaded OK\n");

	GameSpyQnR.Init();

	//
	// Re-enable packet processing.
	//
	if (cNetwork::PServerConnection) {
		cNetwork::PServerConnection->Allow_Packet_Processing(true);
	}

	if (!ConsoleBox.Is_Exclusive()) {
		unsigned ffheap=FastAllocatorGeneral::Get_Allocator()->Get_Total_Heap_Size();
		unsigned ffuse=FastAllocatorGeneral::Get_Allocator()->Get_Total_Allocated_Size();
		unsigned actualuse=FastAllocatorGeneral::Get_Allocator()->Get_Total_Actual_Memory_Usage();
		unsigned count=FastAllocatorGeneral::Get_Allocator()->Get_Total_Allocation_Count();
		StringClass working_string(0,true);
		working_string.Format(
			"\nMalloc count: %d\n"
			"Free count: %d\n"
			"FF Heap: %d.%3.3d.%3.3d (%d Mb)\n"
			"FF Use: %d.%3.3d.%3.3d (%d Mb)\n"
			"Actual Use: %d.%3.3d.%3.3d (%d Mb)\n"
			"FF Count: %d\n"
			,
			WW3D::Get_Last_Frame_Memory_Allocation_Count(),
			WW3D::Get_Last_Frame_Memory_Free_Count(),
			ffheap/(1000*1000),(ffheap/1000)%1000,ffheap%1000, ffheap/(1024*1024),
			ffuse/(1000*1000),(ffuse/10000)%1000,ffuse%1000, ffuse/(1024*1024),
			actualuse/(1000*1000),(actualuse/1000)%1000,actualuse%1000, actualuse/(1024*1024),
			count);
		WWDEBUG_SAY((working_string));
	}
}


void 	CombatGameModeClass::Core_Shutdown()
{
	Debug_Say(("CombatGameModeClass::Core_Shutdown\n"));

	//
	// Switch to the in-game sound page to ensure all those sounds get freed
	// as we're shutting down the level.
	//
	WWAudioClass::Get_Instance ()->Push_Active_Sound_Page (WWAudioClass::PAGE_PRIMARY);

	MultiHUDClass::Shutdown();
	//cPlayerManager::Shutdown();
	//cTeamManager::Shutdown();

	// Shutdown the sound library
	if (BackgroundMusic != NULL) {
		BackgroundMusic->Stop ();
		BackgroundMusic->Release_Ref ();
		BackgroundMusic = NULL;
	}

	if (IS_MISSION) {
		//
		//	Keep the GOD manager from offering a respawn when the star is gone
		//
		cGod::Exit();
	}

	CombatManager::Unload_Level();

	LevelManager::Release_Level();

 	RadarManager::Shutdown();

	//
	//	Restore the current sound page
	//
	WWAudioClass::Get_Instance ()->Pop_Active_Sound_Page ();
}

//-----------------------------------------------------------------------------
void CombatGameModeClass::Post_Load_Id_Uniqueness_Check(void)
{
	bool list_ids = false;

	//
	// Make sure no 2 phys objects have the same id.
	//

//	Debug_Say(("Testing id uniqueness for physical objects:\n"));

	int phys_obj_count = 0;
	SLNode<BaseGameObj> * objnode;
	for (objnode = GameObjManager::Get_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) {
		WWASSERT(objnode->Data() != NULL);
      PhysicalGameObj * p_phys_obj = objnode->Data()->As_PhysicalGameObj();
		if (p_phys_obj != NULL) {

			phys_obj_count++;

			if (list_ids) {
				Debug_Say(("  Object %d, model %s\n",
					p_phys_obj->Get_ID(),
					p_phys_obj->Peek_Model()->Get_Name()));
			}

			SLNode<BaseGameObj> * objnode2;
			for (objnode2 = GameObjManager::Get_Game_Obj_List()->Head(); objnode2; objnode2 = objnode2->Next()) {
				WWASSERT(objnode2->Data() != NULL);
				PhysicalGameObj * p_phys_obj_2 = objnode2->Data()->As_PhysicalGameObj();
				if (p_phys_obj_2 != NULL &&
					p_phys_obj_2->Get_ID() == p_phys_obj->Get_ID()) {

					//WWASSERT(p_phys_obj_2 == p_phys_obj);
					if (p_phys_obj_2 != p_phys_obj) {
						Debug_Say(("Level file error: Two phys objects found with id %d.\n",
							p_phys_obj_2->Get_ID()));
						DIE;
					}
				}
			}
		}
	}

//	Debug_Say(("  %d unique physical objects were loaded.\n", phys_obj_count));



	//
	// Make sure no 2 static objects have the same id.
	//
//	Debug_Say(("Testing id uniqueness for static objects:\n"));

	WWASSERT(COMBAT_SCENE != NULL);
	int static_obj_count = 0;
	RefPhysListIterator iter_1 = COMBAT_SCENE->Get_Static_Object_Iterator();
	for (iter_1.First(); !iter_1.Is_Done(); iter_1.Next()) {

		//StaticPhysClass * p_obj_1 = (StaticPhysClass *) iter_1.Peek_Obj();
		PhysClass * p_obj_1 = iter_1.Peek_Obj();
		WWASSERT(p_obj_1 != NULL);
		int object_id_1 = p_obj_1->Get_ID();

		static_obj_count++;

		if (list_ids) {
			//Debug_Say(("  Object %d\n", object_id_1));
			Debug_Say(("  Object %d, model %s\n",
				p_obj_1->Get_ID(),
				p_obj_1->Peek_Model()->Get_Name()));
		}

		RefPhysListIterator iter_2 = COMBAT_SCENE->Get_Static_Object_Iterator();
		for (iter_2.First(); !iter_2.Is_Done(); iter_2.Next()) {
			//StaticPhysClass * p_obj_2 = (StaticPhysClass *) iter_2.Peek_Obj();
			PhysClass * p_obj_2 = iter_2.Peek_Obj();
			WWASSERT(p_obj_2 != NULL);
			int object_id_2 = p_obj_2->Get_ID();

			//
			//
			//     DO     NOT      DISABLE     THIS
			//
			//
			if (object_id_1 == object_id_2 && p_obj_1 != p_obj_2) {
				Debug_Say(("Fatal level file error: Two static objects found with id %d.\n",
					object_id_1));
				DIE;
			}
		}
	}

//	Debug_Say(("  %d unique static objects were loaded.\n", static_obj_count));
}

//-----------------------------------------------------------------------------
void CombatGameModeClass::Post_Load_Dynamic_Object_Filtering(void)
{
	//
	// Look through the soldiers and remove any that aren't
	// appropriate for this particular game type.
	//
	//
	// Any commando's that are tagged as having human players must be removed.
	// The player id field is not valid...
	// The exception is the commando belonging to the first player.
	//
	// Remove any soldiers with invalid player types for this game.
	//
	// Remove any grunts unless this is a mission.
	//

	WWASSERT(PTheGameData != NULL);

   SLNode<SmartGameObj> * objnode;
   for (objnode = GameObjManager::Get_Smart_Game_Obj_List()->Head(); objnode; objnode = objnode->Next()) {
		SmartGameObj * p_smart_obj = objnode->Data();
      WWASSERT(p_smart_obj != NULL);

		if (p_smart_obj->As_SoldierGameObj() != NULL) {

			if (p_smart_obj->Has_Player() && (p_smart_obj->Get_Control_Owner() > 1 ||
				(!cNetwork::I_Am_Client()))) {
				Debug_Say(("* Removing loaded soldier belonging to another player.\n"));
	         p_smart_obj->Set_Delete_Pending();
			}

			if (!p_smart_obj->Is_Delete_Pending() &&
				!The_Game()->Is_Valid_Player_Type(p_smart_obj->Get_Player_Type())) {
				Debug_Say(("* Removing loaded soldier due to invalid playertype for this game.\n"));
	         p_smart_obj->Set_Delete_Pending();
			}

			if (!p_smart_obj->Is_Delete_Pending() && !p_smart_obj->Has_Player() &&
				!IS_SOLOPLAY) {
				Debug_Say(("* Removing loaded soldier grunt in non-mission game (id = %d).\n",
					p_smart_obj->Get_ID()));
	         p_smart_obj->Set_Delete_Pending();
			}
		}
   }
}

//-----------------------------------------------------------------------------
void CombatGameModeClass::Compute_World_Size(void)
{
	//
	// Tell networking system about the size of the world, to be used
	// in filtering.
	//
	Vector3 min;
	Vector3 max;
	WWASSERT(PhysicsSceneClass::Get_Instance() != NULL);
	PhysicsSceneClass::Get_Instance()->Get_Level_Extents(min, max);
	//Debug_Say(("World extends from (%5.2f, %5.2f, %5.2f) to (%5.2f, %5.2f, %5.2f)\n",
	//	min.X, min.Y, min.Z, max.X, max.Y, max.Z));

	Vector3 longest_distance = max - min;
	//Debug_Say(("Longest distance is %5.2fm\n", longest_distance.Length()));
	WWASSERT(PTheGameData != NULL);
	The_Game()->Set_Maximum_World_Distance(longest_distance.Length());

	double margin = 1; // comfort zone

	//
	// Also, set encoding precision for positional information
	//
	cEncoderList::Set_Precision(BITPACK_WORLD_POSITION_X,
		min.X - margin, max.X + margin, 0.2);
	cEncoderList::Set_Precision(BITPACK_WORLD_POSITION_Y,
		min.Y - margin, max.Y + margin, 0.2);
	cEncoderList::Set_Precision(BITPACK_WORLD_POSITION_Z,
		min.Z - margin, max.Z + margin, 0.2);
	cEncoderList::Set_Precision(BITPACK_BUILDING_RADIUS,
		0.0, 50.0, 0.1);
}

//-----------------------------------------------------------------------------
#define ADD_CASE(exp) case exp: return #exp; break;
static LPCSTR Playertype_To_String(int player_type)
{
	switch (player_type) {
		ADD_CASE(PLAYERTYPE_MUTANT);
		ADD_CASE(PLAYERTYPE_NEUTRAL);
		ADD_CASE(PLAYERTYPE_RENEGADE);
		ADD_CASE(PLAYERTYPE_NOD);
		ADD_CASE(PLAYERTYPE_GDI);

		default:
			DIE;
			return "ERROR"; // to avoid compiler warning
	}
}

//-----------------------------------------------------------------------------
void CombatGameModeClass::Spawn_Point_Validation(void)
{
	if (cNetwork::I_Am_Server()) {

		bool is_spawners_valid = true;

		for (int spawn_type = PLAYERTYPE_RENEGADE; spawn_type <= PLAYERTYPE_LAST; spawn_type++) {

			if ( !SpawnManager::Spawner_Exists( spawn_type ) ) {
				Debug_Say(("  Error: No spawn point(s) of type %s found.\n",
					Playertype_To_String(spawn_type)));
				is_spawners_valid = false;
			} else {
				Debug_Say(("  Spawn point(s) of type %s found.\n",
					Playertype_To_String(spawn_type)));
			}
		}

		if (!IS_SOLOPLAY) {
			WWASSERT(is_spawners_valid);
		}
	}
}

//-----------------------------------------------------------------------------

void CombatGameModeClass::Core_Restart(void)
{
	Debug_Say(("CombatGameModeClass::Core_Restart\n"));

	Core_Shutdown();

	GameModeManager::Safely_Deactivate();

	INIT_STATUS("Reloading...");

	if ( IS_MISSION ) {
#ifndef MULTIPLAYERDEMO
		StringClass new_name;
		WWASSERT(PTheGameData != NULL);

		new_name = CombatManager::Get_Last_LSD_Name();

		// convert .LSD to .MIX
		if ( new_name.Get_Length() > 4 &&
			::stricmp( &new_name[new_name.Get_Length() - 4], ".LSD" ) == 0 ) {
			new_name.Erase( new_name.Get_Length() - 4, 4 );
			new_name += ".MIX";
		}

		The_Game()->Set_Map_Name( new_name );
#endif // !MULTIPLAYERDEMO
	} else {

		//
		// Reset the timer system if we are a dedicated server and there is no-one in the game.
		// ST - 1/30/2002 12:44PM
		//
		if (The_Game() && The_Game()->IsDedicated.Is_True()) {
			if (cNetwork::PServerConnection && cNetwork::PServerConnection->Get_Num_RHosts() == 0) {
				GameModeClass *game_mode = GameModeManager::Find("LAN");

				//
				// Don't do this if the NAT thread is busy since it uses timers extensively.
				//
				if ((game_mode && game_mode->Is_Active()) || WOLNATInterface.Is_NAT_Thread_Busy() == false) {
					//
					// Danger! Danger!
					// This could cause code that uses time deltas to fail. Hopefully there isn't any code doing that right now.
					//
					SystemTime.Reset();
					TimeManager::Reset();
					GenericDataSafeClass::Reset_Timers();

					GameModeClass* game_mode = GameModeManager::Find("WOL");

					if (game_mode && game_mode->Is_Active()) {
						WolGameModeClass* wol_game = reinterpret_cast<WolGameModeClass*>(game_mode);
						wol_game->System_Timer_Reset();
					}
				}
			}
		}
	}

	//
	// Reset packet optimizer bandwidth stats.
	//
	PacketManager.Reset_Stats();

	Load_Level();

#ifdef WW3D_COMPILE_WITH_DX8__
	DX8MeshRendererContainerClass::Invalidate_All();
#endif
}

//-----------------------------------------------------------------------------

/*
**
*/
void CombatGameModeClass::Load_Registry_Keys(void)
{
	RegistryClass * registry = new RegistryClass( APPLICATION_SUB_KEY_NAME_OPTIONS );
	WWASSERT( registry );
	if ( registry->Is_Valid() ) {
		IsHudShown = registry->Get_Int( "IsHudShown", IsHudShown );
		ForceGod = registry->Get_Int( "ForceGod", ForceGod );

		//TSS
		DefaultToFirstPerson = registry->Get_Int( "DefaultToFirstPerson", DefaultToFirstPerson );
		CombatManager::Set_First_Person_Default(DefaultToFirstPerson == TRUE);
	}
	delete registry;
}

void CombatGameModeClass::Save_Registry_Keys(void)
{
	RegistryClass * registry = new RegistryClass( APPLICATION_SUB_KEY_NAME_OPTIONS );
	WWASSERT( registry );
	if ( registry->Is_Valid() ) {
		registry->Set_Int( "IsHudShown",	IsHudShown );
		registry->Set_Int( "ForceGod",	ForceGod );

		//TSS
		DefaultToFirstPerson = CombatManager::Get_First_Person_Default();
		registry->Set_Int( "DefaultToFirstPerson",	DefaultToFirstPerson );
	}
	delete registry;
}

/*
** called each time through the main loop when active
*/
void 	CombatGameModeClass::Think()
{
	WWPROFILE( "Combat Think" );

	if ( !Is_Active() ) {
		return;
	}

	Combat_Keyboard();

	//
	// Test again to see if we are active, because quick exit may inactivate us.
	//
	if ( !Is_Active() ) {
		return;
	}

	CombatManager::Generate_Control();

	//
	// Network Update needs to be between input and think
	//

	float time_1;
	{
	WWMeasureItClass net_upd_time_s(&time_1);
	cNetwork::Update();
	}

	float time_2;
	{
	WWMeasureItClass combat_think_time_s(&time_2);
	CombatManager::Think();
	}

	if (cNetwork::I_Am_Server())
	{
		WWPROFILE( "cSbboManager stuff" );
		cSbboManager::Increment_Accum_Time_S_Net_Update(time_1);
		cSbboManager::Increment_Accum_Time_S_Combat_Think(time_2);
		cSbboManager::Think();
	}

	DEMO_SECURITY_CHECK;

	if ( COMBAT_STAR ) {
		WWPROFILE( "Stuff 1" );
		Vector3 pos;
		COMBAT_STAR->Get_Position( &pos );
		float facing = COMBAT_STAR->Get_Facing();
		Get_Console()->Set_Player_Position( pos, facing );

		if ( ForceGodPending ) {
			ForceGodPending = false;
			ConsoleFunctionManager::Parse_Input( "God" );
		}

	}

	MultiHUDClass::Think();
	cPlayerManager::Think();
	cTeamManager::Think();

	if ( PendingCampaignContinue ) {
		WWPROFILE( "Stuff 2" );
		PendingCampaignContinue	= false;
		Debug_Say(( "Handle Pending Campaign Continue\n" ));
		CampaignManager::Continue();
	}


#ifdef MULTIPLAYERDEMO
	//
	// Security check. Randomly every few minutes, check to see if a special
	// MP demo object (id 100277) exists. If it doesn't, assume someone has hacked in
	// a different map. Bail.
	//
	if ((::rand() % 10131 == 939) && (NetworkObjectMgrClass::Find_Object(100277) == NULL)) {
		WWDEBUG_SAY(("MP DEMO OBJECT NOT FOUND... BAILING.\n"));
		Suspend();
		GameInitMgrClass::End_Game();
		extern void Stop_Main_Loop (int);
		Stop_Main_Loop(EXIT_SUCCESS);
	}
#endif // MULTIPLAYERDEMO


	if (g_b_core_restart)	{
		WWPROFILE( "g_b_core_restart" );

		g_b_core_restart = false;

		//Core_Restart();

		cPlayer * p_me = cNetwork::Get_My_Player_Object();

		if (p_me != NULL) {// && cNetwork::I_Am_Only_Client()) {

			p_me->Set_Is_In_Game(false);
			cLoadingEvent * p_loading_1 = new cLoadingEvent;
			p_loading_1->Init(true);

			Core_Restart();

			p_me->Set_Is_In_Game(true);
			cLoadingEvent * p_loading_2 = new cLoadingEvent;
			p_loading_2->Init(false);

		} else {
			Core_Restart();
		}

		if (!IS_MISSION) {
			MultiHUDClass::Init();
		}

		cNetwork::Enable_Waiting_Players();

/*
#if(0)
		WWDEBUG_SAY(("****** CombatGameModeClass::Think On_Game_Begin()\n"));
		WWASSERT(The_Game() != NULL);

		//		The_Game()->On_Game_End();
		The_Game()->Reset_Game(false);
		The_Game()->On_Game_Begin();//TSS091201
#endif
*/
	}

	// Autosave, after one run throught main loop
	if ( CombatManager::Is_Autosave_Requested() ) {
		WWPROFILE( "Autosaving" );
		Debug_Say(( "Autosaving\n" ));
		int time=TIMEGETTIME();
		CombatManager::Request_Autosave( false );
		SaveGameManager::Set_Description( TRANSLATE( IDS_SAVE_AUTOSAVE ) );
		SaveGameManager::Save_Game( "save\\autosave.sav", &_CommandoSaveLoad, NULL );
		time=TIMEGETTIME()-time;
		Debug_Say(( "Autosaving Complete, took %d.%2.2d seconds\n",time/1000,(time/10)%100 ));
	}

	//TSS090401
	if (g_client_quit)
	{
		WWPROFILE( "g_client_quit" );

		//
		// This becomes true when the connection to the server breaks
		//
		g_client_quit = false;

		Suspend();
		GameInitMgrClass::End_Game();

		//GAMESPY
		if (cGameSpyAdmin::Get_Is_Launched_From_Gamespy())
		{
#ifdef MULTIPLAYERDEMO
			DialogMgrClass::Flush_Dialogs ();
			START_DIALOG (SplashOutroMenuDialogClass);
#else
			extern void Stop_Main_Loop (int);
			Stop_Main_Loop(EXIT_SUCCESS);
#endif // MULTIPLAYERDEMO
		}
		else
		{
			GameInitMgrClass::Display_End_Game_Menu();
		}
	}
}

void 	CombatGameModeClass::Render()
{
	if ( !Is_Active() ) {
		return;
	}

	if (cNetwork::I_Am_Client()) {

		/*
		** In multi-play, only render the combatmanager when we have a valid camera and the menu is not active
		*/
		bool menu_active = false;
		GameModeClass * menu_mode = GameModeManager::Find( "Menu" );		// Activate the main menu
		if ((menu_mode != NULL) && (menu_mode->Get_State() == GAME_MODE_ACTIVE)) {
			menu_active = true;
		}

		if ( COMBAT_CAMERA && COMBAT_CAMERA->Is_Valid() && (menu_active == false)) {
			CombatManager::Render();
		}
   }

	MultiHUDClass::Render();
	cBandwidthGraph::Render();
	cPlayerManager::Render();
	cTeamManager::Render();
	WWASSERT(PTheGameData != NULL);
	The_Game()->Render();
	RadioCommandDisplayClass::Render ();
}

//-----------------------------------------------------------------------------
void CombatGameModeClass::Toggle_Multi_Hud(void)
{
	MultiHUDClass::Toggle();
}

// --------------------------------------------------------------

void	CombatGameMiscHandlerClass::Mission_Complete( bool success )
{
	if (IS_MISSION) {
		if ( success ) {
			PendingCampaignContinue	= true;
		} else {
			cGod::Mission_Failed();
		}
#if 0
		GameModeManager::Find("Combat")->Suspend();
//		GameModeManager::Find("Combat")->Deactivate();
//		GameModeManager::Find("ScoreScreen")->Activate();
//		((ScoreScreenGameModeClass*)GameModeManager::Find("ScoreScreen"))->Set_Success( success );

		if ( GameModeManager::Find( "Overlay3D" ) ) {
		   ((Overlay3DGameModeClass*)GameModeManager::Find( "Overlay3D" ))->Start_End_Screen();
		}
#endif
	}
}

void	CombatGameMiscHandlerClass::Star_Killed( void )
{
	cGod::Star_Killed();
}

void	CombatGameModeClass::Resume(void)
{
	//
	//	Show the message window (if necessary)
	//
	if (	CombatManager::Get_Message_Window () != NULL &&
			CombatManager::Get_Message_Window ()->Has_Data ())
	{
		CombatManager::Get_Message_Window ()->Force_Display (true);
	}

	GameMajorModeClass::Resume();
	return ;
}

void	CombatGameModeClass::Suspend(void)
{
	//
	//	Hide the message window
	//
	if (CombatManager::Get_Message_Window () != NULL) {
		CombatManager::Get_Message_Window ()->Force_Display (false);
	}

	//
	//	Hide the objective viewer
	//
	ObjectiveManager::Display_Viewer (false);

	GameMajorModeClass::Suspend();
	return ;
}


/*
**
*/
void	CombatGameModeClass::Quick_Save( void )
{
	bool	saveA = true;

	RegistryClass * registry = new RegistryClass( APPLICATION_SUB_KEY_NAME_OPTIONS );
	WWASSERT( registry );
	if ( registry->Is_Valid() ) {
		saveA = registry->Get_Bool( "QuicksaveA", saveA );
	}

#define	SAVEGAME_NAME_A	"save\\quicksaveA.sav"
#define	SAVEGAME_NAME_B	"save\\quicksaveB.sav"

	// Check for a missing file
   if ( !cMiscUtil::File_Exists(SAVEGAME_NAME_B) ) {
		saveA = false;
   }
   if ( !cMiscUtil::File_Exists(SAVEGAME_NAME_A) ) {
		saveA = true;
   }

	if ( saveA ) {
		SaveGameManager::Set_Description( TRANSLATE(IDS_SAVE_QUICKSAVE_A) );
		SaveGameManager::Save_Game( SAVEGAME_NAME_A, &_CommandoSaveLoad, NULL );
		Debug_Say(( "Quicksaved A\n" ));
	} else {
		SaveGameManager::Set_Description( TRANSLATE(IDS_SAVE_QUICKSAVE_B) );
		SaveGameManager::Save_Game( SAVEGAME_NAME_B, &_CommandoSaveLoad, NULL );
		Debug_Say(( "Quicksaved B\n" ));
	}
	saveA = !saveA;

	if ( registry->Is_Valid() ) {
		registry->Set_Bool( "QuicksaveA",	saveA );
	}
	delete registry;

	// Display "Quick Saved"
	HUDInfo::Set_HUD_Help_Text( TRANSLATE( IDS_M00DSGN_DSGN1017I1DSGN_TXT ), Vector3( 0,1,0 ) );

}



























	/**/
	/*
	if (::rand() % 10131 == 939)) {
		bool bail = true;

		ScriptableGameObj * p_obj = GameObjManager::Find_ScriptableGameObj(100277);
		if (p_obj != NULL && p_obj->Get_Definition().Get_Name()) {
			bail = false;
		}

		if (bail) {
			Suspend();
			GameInitMgrClass::End_Game();
			extern void Stop_Main_Loop (int);
			Stop_Main_Loop(EXIT_SUCCESS);
		}
	}
	*/