/*
**	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/gamedata.cpp                        $*
 *                                                                                             *
 *                      $Author:: Steve_t                                                     $*
 *                                                                                             *
 *                     $Modtime:: 11/01/02 4:32p                                              $*
 *                                                                                             *
 *                    $Revision:: 221                                                         $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

#include "gamedata.h"

#include <stdio.h>
#include <string.h>

#include "win.h"
#include "miscutil.h"
#include "cnetwork.h"
#include "chatshre.h"
#include "wwdebug.h"
#include "netinterface.h"
#include "translatedb.h"
#include "playertype.h"
#include "teammanager.h"
#include "playermanager.h"
#include "assets.h"
#include "translatedb.h"
#include "string_ids.h"
#include "gamemode.h"
#include "savegame.h"
#include "bandwidth.h"
#include "gametype.h"
#include "systimer.h"
#include "objlibrary.h"
#include "scriptzone.h"
#include "aabox.h"
#include "rendobj.h"
#include "weaponbag.h"
#include "multihud.h"
#include "wwaudio.h"
#include "textdisplay.h"
#include "render2d.h"
#include "render2dsentence.h"
#include "wwprofile.h"
#include "winevent.h"
#include "realcrc.h"
#include "useroptions.h"
#include "gdsingleplayer.h"
#include "gdcnc.h"
#include "wolgmode.h"
#include "font3d.h"
#include "nat.h"
#include "csdamageevent.h"
#include "sctextobj.h"
#include "widestring.h"
#include "vehicle.h"
#include "wolgameinfo.h"
#include "spawn.h"
#include "dlgcncwinscreen.h"
#include "dialogmgr.h"
#include "rawfile.h"
#include "consolemode.h"
#include "gamedataupdateevent.h"
#include "stylemgr.h"
#include "modpackagemgr.h"
#include "modpackage.h"
#include "bandwidthcheck.h"
#include "serversettings.h"

#include "gamespyadmin.h"
#include "specialbuilds.h"
#include "demosupport.h"
#include "specialbuilds.h"

const int cGameData::MAX_TIME_LIMIT       = 999;


cGameData *	PTheGameData = NULL;

static WideStringClass renderer_time_text;
static WideStringClass renderer_dedicated_server_label;
static WideStringClass renderer_gameplay_label;
static unsigned renderer_dedicated_server_color;
static unsigned renderer_gameplay_color;

//
// Class statics
//
const StringClass			cGameData::INI_SECTION_NAME	= "Settings";
const float					cGameData::LIMITS_X_POS			= 0.8f;
Render2DSentenceClass	*		cGameData::PTextRenderer		= NULL;
int							cGameData::HostedGameNumber	= 0;
bool							cGameData::IsManualRestart		= false;
bool							cGameData::IsManualExit			= false;
WideStringClass			cGameData::WinText;

//
// hack
//
ULONG g_ip_override = INADDR_NONE;

//------------------------------------------------------------------------------------
void cGameData::Onetime_Init(void)
{
	WWDEBUG_SAY(("cGameData::Onetime_Init\n"));
	if (!ConsoleBox.Is_Exclusive()) {
		PTextRenderer = new Render2DSentenceClass;
		StyleMgrClass::Assign_Font(PTextRenderer, StyleMgrClass::FONT_INGAME_TXT);
		PTextRenderer->Set_Mono_Spaced(true);
		PTextRenderer->Set_Clipping_Rect(Render2DClass::Get_Screen_Resolution());
	}
}

//------------------------------------------------------------------------------------
void cGameData::Onetime_Shutdown(void)
{
	WWDEBUG_SAY(("cGameData::Onetime_Shutdown\n"));

	if (!ConsoleBox.Is_Exclusive()) {
		WWASSERT(PTextRenderer != NULL);
		delete PTextRenderer;
		PTextRenderer = NULL;
	}
}

//-----------------------------------------------------------------------------
cGameData::cGameData(void)	:
	DoExeVersionsMatch (true),
	DoStringVersionsMatch (true),
	DoMapsLoop (true),
	IsMapCycleOver (false),
	MapCycleIndex(0)
{
   //WWDEBUG_SAY(("cGameData::cGameData\n"));

	IsIntermission.Set(				false);
	IsDedicated.Set(					false);
	IsAutoRestart.Set(				false);
	IsFriendlyFirePermitted.Set(	false);
	IsTeamChangingAllowed.Set(		true);
	IsPassworded.Set(					false);
	IsFreeWeapons.Set(				false);
	IsLaddered.Set(					false);
	IsClanGame.Set(					false);
	IsClientTrusted.Set(				true);
	RemixTeams.Set(					false);
	CanRepairBuildings.Set(			true);
	DriverIsAlwaysGunner.Set(		true);
	SpawnWeapons.Set(					false);

	mWinnerID							= -1;
	MaxPlayers							= 16;
	TimeLimitMinutes					= 0;
	RadarMode							= RADAR_ALL;
	IniFilename							= "";
	Motd.Format(L"");

	Set_Password(						L"");
   //Set_Owner(							"UNOWNED");
   Set_Owner(							cNetInterface::Get_Nickname());

	IntermissionTimeSeconds			= 15;
   Set_Version_Number(				cNetwork::Get_Exe_Key());
	Set_Ip_Address(0);
	Set_Port(							4848);//TSS - hardcoded port

   Set_Current_Players(0);
	IntermissionTimeRemaining		= 0;
	TimeRemainingSeconds				= 0;
	MaximumWorldDistance				= 0;
	MinQualifyingTimeMinutes		= 2;

	IsQuickMatchServer				= false;
	//ServerIsGameplayPermitted		= false;
	WinType								= WIN_TYPE_TIME;
	GameDurationS						= 0;
	LastServerConfigModTime			= 0;
	SettingsDescription = 			TRANSLATE(IDS_SERVER_SAVELOAD_CUSTOM_DEFAULT);

	Clear_Clans();

	MvpCount = 0;

#ifdef MULTIPLAYERDEMO
	MapName.Format("C&C_Under.mix");
#endif // MULTIPLAYERDEMO
}

//-----------------------------------------------------------------------------
cGameData::~cGameData(void)
{
   //WWDEBUG_SAY(("cGameData::~cGameData\n"));
}

//-----------------------------------------------------------------------------
cGameData& cGameData::operator=(const cGameData& rhs)
{
	IsIntermission						= rhs.IsIntermission;

	IsDedicated							= rhs.IsDedicated;
	IsAutoRestart						= rhs.IsAutoRestart;
	IsFriendlyFirePermitted			= rhs.IsFriendlyFirePermitted;
	IsTeamChangingAllowed			= rhs.IsTeamChangingAllowed;
	IsPassworded						= rhs.IsPassworded;
	IsFreeWeapons						= rhs.IsFreeWeapons;
	IsLaddered							= rhs.IsLaddered;
	IsClanGame							= rhs.IsClanGame;
	IsClientTrusted					= rhs.IsClientTrusted;
	RemixTeams							= rhs.RemixTeams;
	CanRepairBuildings				= rhs.CanRepairBuildings;
	DriverIsAlwaysGunner				= rhs.DriverIsAlwaysGunner;
	SpawnWeapons						= rhs.SpawnWeapons;
   Password								= rhs.Password;
	GameTitle							= rhs.GameTitle;
	Motd									= rhs.Motd;

#ifdef MULTIPLAYERDEMO
	MapName.Format("C&C_Under.mix");
#else
   MapName								= rhs.MapName;
	ModName								= rhs.ModName;
#endif

	Owner									= rhs.Owner;

   MaxPlayers							= rhs.MaxPlayers;
   TimeLimitMinutes					= rhs.TimeLimitMinutes;
	IntermissionTimeSeconds			= rhs.IntermissionTimeSeconds;
   VersionNumber						= rhs.VersionNumber;
	IpAddress							= rhs.IpAddress;
	Port									= rhs.Port;
	IniFilename							= rhs.IniFilename;
	RadarMode							= rhs.RadarMode;

	CurrentPlayers						= rhs.CurrentPlayers;
	IntermissionTimeRemaining		= rhs.IntermissionTimeRemaining;
	TimeRemainingSeconds				= rhs.TimeRemainingSeconds;
	MaximumWorldDistance				= rhs.MaximumWorldDistance;
	MinQualifyingTimeMinutes		= rhs.MinQualifyingTimeMinutes;

	for (int clanSlot = 0; clanSlot < MAX_CLAN_SLOTS; ++clanSlot) {
		mClanSlots[clanSlot] = rhs.mClanSlots[clanSlot];
	}

	return (*this);
}

//-----------------------------------------------------------------------------
void cGameData::Reset_Game(bool is_reloaded)
{
   WWDEBUG_SAY(("cGameData::Reset_Game\n"));

	//
	// Game_Reset is called on both C & S at the beginning of every game.
	//
	mWinnerID = -1;

	IsIntermission.Set(false);

	//
	// 12/11/01
	// This dialog flush was added to remove the purchase screen because it persists
	// over reload.
	//
	if (GameModeManager::Find ("Combat")->Is_Active ()) {
		if (GameModeManager::Find ("Menu")->Is_Active ()) {
			GameModeManager::Find ("Menu")->Deactivate ();
		} else {
			DialogMgrClass::Flush_Dialogs ();
		}
	}

	if (Get_Text_Display()) {
   	WWASSERT(Get_Text_Display() != NULL);
   	Get_Text_Display()->Flush();
	}

	if (!IS_MISSION) {
		WWAudioClass::Get_Instance()->Create_Instant_Sound("Game_Start", Matrix3D(1));
	}

	//TSS092601 if ( !is_reloaded ) {
	if ( !IS_MISSION || !is_reloaded ) {
		cPlayerManager::Reset_Players();
	}
	//}
   cTeamManager::Reset_Teams();

	if (Is_Time_Limit()) {
		Reset_Time_Remaining_Seconds();

		if (cNetwork::I_Am_Server()) {
			cGameDataUpdateEvent * p_event = new cGameDataUpdateEvent();
			p_event->Init(-1);
		}
   }

	if (cNetwork::I_Am_Server())
	{
		HostedGameNumber++;
	}
}

//-----------------------------------------------------------------------------
void cGameData::Swap_Team_Sides(void)
{
	//WWASSERT(Is_Team_Game());
	WWASSERT(cNetwork::I_Am_Server());

	for (
		SLNode<cPlayer> * player_node = cPlayerManager::Get_Player_Object_List()->Head();
		player_node;
		player_node = player_node->Next()) {

		cPlayer * p_player = player_node->Data();
		WWASSERT(p_player != NULL);

		int team = p_player->Get_Player_Type();

		WWASSERT(team == PLAYERTYPE_NOD || team == PLAYERTYPE_GDI);

		int new_team = PLAYERTYPE_NOD;
		if (team == PLAYERTYPE_NOD)
		{
			new_team = PLAYERTYPE_GDI;
		}
		else
		{
			new_team = PLAYERTYPE_NOD;
		}

		p_player->Set_Player_Type(new_team);
	}

	//
	// Inform the players
	//
	//WideStringClass text;
	//text.Format(L"_TEAMS_SWAPPED_!_");
	WideStringClass text = TRANSLATE(IDS_MP_TEAMS_SWAPPED);
	cScTextObj * p_text_obj = new cScTextObj();
	p_text_obj->Init(text, TEXT_MESSAGE_PUBLIC, false, HOST_TEXT_SENDER, -1);
}

//-----------------------------------------------------------------------------
void cGameData::Remix_Team_Sides(void)
{
	//WWASSERT(Is_Team_Game());
	WWASSERT(cNetwork::I_Am_Server());

	WWASSERT(IsClanGame.Is_False() && "Remix teams not allowed");

	if (IsClanGame.Is_True()) {
		return;
	}

	//
	// Randomly choose a team for each player
	//
	for (
		SLNode<cPlayer> * player_node = cPlayerManager::Get_Player_Object_List()->Head();
		player_node;
		player_node = player_node->Next()) {

		cPlayer * p_player = player_node->Data();
		WWASSERT(p_player != NULL);

		int new_team = PLAYERTYPE_NOD;
		if (rand() % 2 == 0)
		{
			new_team = PLAYERTYPE_GDI;
		}
		else
		{
			new_team = PLAYERTYPE_NOD;
		}

		p_player->Set_Player_Type(new_team);
	}

	//
	// Inform the players
	//
	//WideStringClass text;
	//text.Format(L"_TEAMS_REMIXED_!_");
	WideStringClass text = TRANSLATE(IDS_MP_TEAMS_REMIXED);
	cScTextObj * p_text_obj = new cScTextObj();
	p_text_obj->Init(text, TEXT_MESSAGE_PUBLIC, false, HOST_TEXT_SENDER, -1);
}

//-----------------------------------------------------------------------------
void cGameData::Rebalance_Team_Sides(void)
{
	//WWASSERT(Is_Team_Game());
	WWASSERT(cNetwork::I_Am_Server());

	WWASSERT(IsClanGame.Is_False() && "Rebalance teams not allowed");

	if (IsClanGame.Is_True()) {
		return;
	}

	//
	// Rebalance teams numerically
	//
	cTeam * p_nod = cTeamManager::Find_Team(PLAYERTYPE_NOD);
	WWASSERT(p_nod != NULL);

	cTeam * p_gdi = cTeamManager::Find_Team(PLAYERTYPE_GDI);
	WWASSERT(p_gdi != NULL);

	int move_count = 0;

	int nod_tally = 0;
	int gdi_tally = 0;

	do
	{
		nod_tally = p_nod->Tally_Size();
		gdi_tally = p_gdi->Tally_Size();

		if (nod_tally > gdi_tally + 1)
		{
			cPlayer * p_player = cPlayerManager::Find_Team_Player(PLAYERTYPE_NOD);
			WWASSERT(p_player != NULL);
			p_player->Set_Player_Type(PLAYERTYPE_GDI);
			move_count++;
		}
		else if (gdi_tally > nod_tally + 1)
		{
			cPlayer * p_player = cPlayerManager::Find_Team_Player(PLAYERTYPE_GDI);
			WWASSERT(p_player != NULL);
			p_player->Set_Player_Type(PLAYERTYPE_NOD);
			move_count++;
		}

	} while (::abs(nod_tally - gdi_tally) > 1);

	if (move_count > 0) {
		//
		// Inform the players
		//
		//WideStringClass text;
		//text.Format(L"_TEAMS_REBALANCED_!_");
		WideStringClass text = TRANSLATE(IDS_MP_TEAMS_REBALANCED);
		cScTextObj * p_text_obj = new cScTextObj();
		p_text_obj->Init(text, TEXT_MESSAGE_PUBLIC, false, HOST_TEXT_SENDER, -1);
	}
}

//-----------------------------------------------------------------------------
void cGameData::Set_Ip_And_Port(void)
{
	/*
	SOCKADDR_IN local_address;
   bool retcode = cNetUtil::Get_Local_Address(&local_address);
	WWASSERT(retcode == true);

	//
	// hack
	//
	if (g_ip_override != INADDR_NONE) {
		local_address.sin_addr.s_addr = g_ip_override;
	}

	Set_Ip_Address(local_address.sin_addr.s_addr);
	*/

	ULONG ip = 0;
	if (cGameSpyAdmin::Get_Is_Server_Gamespy_Listed()) {
		ip = cUserOptions::PreferredGameSpyNic.Get();
	} else {
		ip = cUserOptions::PreferredLanNic.Get();
	}

	if (g_ip_override != INADDR_NONE) {
		ip = g_ip_override;
	}

	//
	// IP is set automatically in WOL unless there is an IP override and a port overrride.
	//
	if (GameModeManager::Find("WOL")->Is_Active()) {

		if (g_ip_override == INADDR_NONE || WOLNATInterface.Get_Force_Port() == 0) {
			unsigned long temp = FirewallHelper.Get_Local_Address();
			if (temp) {
				//ip = temp;
				ip = ::ntohl(temp);
			}
		}
	}

	//WWASSERT(ip != 0);
	Set_Ip_Address(ip);
}

//-----------------------------------------------------------------------------
//
// A convenience function...
//
bool cGameData::Set_Generic_Num(int generic_num,
   int lower_bound, int upper_bound, int & set_num)
{
   if (generic_num >= lower_bound && generic_num <= upper_bound) {
      set_num = generic_num;
      return true;
   } else {
      return false;
   }
}

//-----------------------------------------------------------------------------
bool cGameData::Set_Max_Players(int max)
{
   return Set_Generic_Num(max, 0, MAX_PLAYERS, MaxPlayers);
}

//-----------------------------------------------------------------------------
bool cGameData::Set_Time_Limit_Minutes(int time_limit_minutes)
{
   return Set_Generic_Num(time_limit_minutes, 0, MAX_TIME_LIMIT, TimeLimitMinutes);
}

//-----------------------------------------------------------------------------
void cGameData::Set_Radar_Mode(RadarModeEnum mode)
{
   RadarMode = mode;
}

//-----------------------------------------------------------------------------
void cGameData::Set_Intermission_Time_Seconds(int time)
{
	WWASSERT(time >= 0);
	IntermissionTimeSeconds = time;
}

//-----------------------------------------------------------------------------
void cGameData::Set_Motd(const WCHAR * motd)
{
	WWASSERT(motd != NULL);

	Motd = motd;
}


//-----------------------------------------------------------------------------
void cGameData::Set_Mod_Name(const StringClass &mod_name)
{
	ModName = mod_name;
	ModPackageMgrClass::Set_Current_Package (ModName);
	return ;
}

//-----------------------------------------------------------------------------
void cGameData::Set_Map_Name(const StringClass & map_name)
{
#ifdef MULTIPLAYERDEMO
	MapName.Format("C&C_Under.mix");
#else
	MapName = map_name;
#endif
}

//-----------------------------------------------------------------------------
bool cGameData::Set_Current_Players(int current)
{
   return Set_Generic_Num(current, 0, MAX_PLAYERS, CurrentPlayers);
}

//-----------------------------------------------------------------------------
void cGameData::Set_Owner(WideStringClass & owner)
{
   Owner = owner;
}

//-----------------------------------------------------------------------------
void cGameData::Set_Ip_Address(ULONG ip_address)
{
	IpAddress = ip_address;
}

//-----------------------------------------------------------------------------
void cGameData::Set_Port(int port)
{
   WWASSERT(port >= MIN_SERVER_PORT && port <= MAX_SERVER_PORT);
	Port = port;
}

//-----------------------------------------------------------------------------
bool cGameData::Is_Limited(void) const
{
	return Is_Time_Limit();
}

//-----------------------------------------------------------------------------
bool cGameData::Is_Map_Valid(char **out_filename)
{
	bool map_exists = false;
	if (ModName.Is_Empty () == false) {
		if (out_filename) {
			*out_filename = ModName.Peek_Buffer();
		}
		map_exists = cMiscUtil::File_Exists (ModName);
	} else {
		if (out_filename) {
			*out_filename = MapName.Peek_Buffer();
		}
		map_exists = cMiscUtil::File_Exists (MapName);
	}

	return(map_exists);
}


#define PRINT_CONFIG_ERROR	ConsoleBox.Print("File %s - Error:\r\n\t ", Get_Ini_Filename());

//-----------------------------------------------------------------------------
bool cGameData::Is_Valid_Settings(WideStringClass& outMsg, bool check_as_server)
{
	if (MapName.Is_Empty()) {
		//cHelpText::Set(TRANSLATION(IDS_MP_NO_LEVEL_SELECTED));
		Debug_Say(( "cGameData::No level file selected.\n" ));
		PRINT_CONFIG_ERROR;
		ConsoleBox.Print("No initial level file selected\n\n");
		outMsg = TRANSLATE(IDS_HOPTERR_NO_LEVEL);
		return false;
	} else {

		//
		//	Test to see whether or not the map exists
		//
		char *filename = NULL;
		if (!Is_Map_Valid(&filename)) {
			Debug_Say(("cGameData::Is_Valid_Settings: Level \"%s\" not found\n", filename));
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("Map file '%s' not found\n\n", filename);
			outMsg.Format(TRANSLATE(IDS_HOPTERR_MAP_NOTFOUND), filename);
			return(false);
		}
	}

	if (IsPassworded.Is_True() && Password.Get_Length () == 0) {
		//cHelpText::Set(TRANSLATION(IDS_MP_BLANK_PASSWORD_ILLEGAL));
		Debug_Say(( "cGameData::Is_Valid_Settings: You can not use a blank password.\n" ));
		PRINT_CONFIG_ERROR;
		ConsoleBox.Print("Blank passwords are not allowed\n\n");
		outMsg = TRANSLATE(IDS_HOPTERR_BLANK_PASS);
		return false;
	}

#ifndef FREEDEDICATEDSERVER
	if (GameModeManager::Find("WOL")->Is_Active()) {
#endif //FREEDEDICATEDSERVER
		if (IsPassworded.Is_True() && Is_QuickMatch_Server()) {
			Debug_Say(("cGameData::Is_Valid_Settings: Quickmatch can not have passwords.\n" ));
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("A password cannot be used on a Quickmatch server\n\n");
			outMsg = TRANSLATE(IDS_HOPTERR_QM_HASPASS);
			return false;
		}

		if (Is_QuickMatch_Server() && IsLaddered.Is_False()) {
			Debug_Say(("cGameData::Is_Valid_Settings: Quickmatch must be laddered.\n" ));
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("A Quickmatch server must also be laddered\n\n");
			outMsg = TRANSLATE(IDS_HOPTERR_QM_NOLADDER);
			return false;
		}

		if (Is_QuickMatch_Server() && IsClanGame.Is_True()) {
			Debug_Say(("cGameData::Is_Valid_Settings: Quickmatch cannot be clanned.\n" ));
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("Clans are not allowed in a quickmatch server\n\n");
			outMsg = TRANSLATE(IDS_HOPTERR_QM_NOCLANS);
			return false;
		}

		if (IsLaddered.Is_True() && IsTeamChangingAllowed.Is_True()) {
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("Team changing not allowed on a laddered server\n\n");
			outMsg = TRANSLATE(IDS_HOPTERR_NO_TEAMCHANGE);
			return false;
		}

		if (IsClanGame.Is_True() && (IsTeamChangingAllowed.Is_True() || RemixTeams.Is_True())) {
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("Team changing or Team Remix not allowed on a clan game server.\n");
			outMsg = "Team changing or Team Remix not allowed on a clan game server."; //TRANSLATE(IDS_HOPTERR_NO_TEAMCHANGE);
			return false;
		}
#ifndef FREEDEDICATEDSERVER
	}
#endif //FREEDEDICATEDSERVER

#ifdef FREEDEDICATEDSERVER
	if (Get_Max_Players() == 0 && ServerSettingsClass::Get_Game_Mode() !=
		ServerSettingsClass::MODE_WOL) {
#else
	if (Get_Max_Players() == 0) {
#endif
		PRINT_CONFIG_ERROR;
		ConsoleBox.Print("Max player count must be greater than 0\n\n");
		outMsg = TRANSLATE(IDS_HOPTERR_MAXPLAYER_0);
		return(false);
	}

	// Check server only parameters
	if (check_as_server || ConsoleBox.Is_Exclusive()) {
		if (Get_Max_Players() > NetworkObjectClass::MAX_CLIENT_COUNT-1) {
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("Max player count must be less than %d\n\n", NetworkObjectClass::MAX_CLIENT_COUNT);
			outMsg.Format(TRANSLATE(IDS_HOPTERR_MAXPLAYER_TOOBIG), NetworkObjectClass::MAX_CLIENT_COUNT);
			return(false);
		}

		if (DoMapsLoop && Get_Map_Cycle(0).Get_Length() == 0) {
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("Map cycling is enabled but there are no maps in the cycle\n\n");
			outMsg = TRANSLATE(IDS_HOPTERR_NO_MAPCYCLE);
			return(false);
		}

		/*
		** Make sure all the maps exist.
		*/
		if (DoMapsLoop && ModName.Is_Empty ()) {
			for (int i=0 ; i<MAX_MAPS ; i++) {
				StringClass map_name = Get_Map_Cycle(i);
				if (map_name.Get_Length()) {
					char filename[_MAX_PATH];
					sprintf(filename, "data\\%s", map_name.Peek_Buffer());
					RawFileClass file(filename);
					if (!file.Is_Available()) {
						PRINT_CONFIG_ERROR;
						ConsoleBox.Print("Map file '%s' not found\n\n", filename);
						outMsg.Format(TRANSLATE(IDS_HOPTERR_MAP_NOTFOUND), filename);
						return(false);
					}
				}
			}
		}

		/*
		** Check the game end conditions.
		*/
		if (!Is_Limited()) {
			//cHelpText::Set(TRANSLATION(IDS_MP_GAMEMODE_NEEDS_LIMIT));
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("Game must end by time limit or base destruction\n\n");
			outMsg = TRANSLATE(IDS_HOPTERR_NO_GAMEEND);
			return false;
		}

		/*
		** Check the Message of the day for excessive length.
		*/
		if (Motd.Get_Length() > MAX_MOTD_LENGTH) {
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("Message of the day cannot exceed %d characters\n\n", MAX_MOTD_LENGTH);
			outMsg.Format(TRANSLATE(IDS_HOPTERR_MOTD_TOOBIG), MAX_MOTD_LENGTH);
			return false;
		}

		/*
		** Check the game title for length. The buffers are pretty much all 256 bytes but 100 seems a more reasonable figure.
		*/
		if (GameTitle.Get_Length() > 100) {
			PRINT_CONFIG_ERROR;
			ConsoleBox.Print("Game title cannot exceed %d characters\n\n", 100);
			outMsg.Format(TRANSLATE(IDS_HOPTERR_TITLE_TOOBIG), 100);
			return false;
		}
	}

	return true; // it's valid !
}


//-----------------------------------------------------------------------------
//
// Server options are exported in 2 tiers. Tier 1 is the basic parameters
// that show up in the list of games. Tier 2 is the remainder of the full
// set of server settings.
//
void cGameData::Export_Tier_1_Data(cPacket & packet)
{
	packet.Add(IpAddress);
	packet.Add_Wide_Terminated_String(Owner);
	packet.Add_Wide_Terminated_String(GameTitle, true);

	packet.Add(Port);
	packet.Add(CurrentPlayers);
   packet.Add(MaxPlayers);
	packet.Add(VersionNumber);

	//
	//	Send the individual CRC's (for internal use)
	//
	packet.Add(cNetwork::Get_Exe_CRC ());
	packet.Add(cNetwork::Get_Strings_CRC ());

	packet.Add(IsDedicated.Get());
	packet.Add(IsTeamChangingAllowed.Get());
	packet.Add(IsPassworded.Get());
	packet.Add(IsLaddered.Get());
	packet.Add(IsClanGame.Get());

#ifndef MULTIPLAYERDEMO
	packet.Add(::CRC_Stringi(MapName));
	packet.Add(::CRC_Stringi(ModName));
#endif // MULTIPLAYERDEMO
}

//-----------------------------------------------------------------------------
void cGameData::Import_Tier_1_Data(cPacket & packet)
{
	ULONG ip_address = packet.Get(ip_address);
   Set_Ip_Address(ip_address);

	WideStringClass owner;
	packet.Get_Wide_Terminated_String(owner.Get_Buffer(256), 256);
   Set_Owner(owner);

	WideStringClass title;
	packet.Get_Wide_Terminated_String(title.Get_Buffer(256), 256, true);
   Set_Game_Title(title);

	int	i_placeholder;
	bool	b_placeholder;

   Set_Port(						packet.Get(i_placeholder));
   Set_Current_Players(			packet.Get(i_placeholder));
   Set_Max_Players(				packet.Get(i_placeholder));
   Set_Version_Number(			packet.Get(i_placeholder));

	//
	//	Compare the individual CRC's against our own
	//
	DoExeVersionsMatch		= (packet.Get(i_placeholder) == cNetwork::Get_Exe_CRC ());
	DoStringVersionsMatch	= (packet.Get(i_placeholder) == cNetwork::Get_Strings_CRC ());

	IsDedicated.Set(				packet.Get(b_placeholder));
	IsTeamChangingAllowed.Set(	packet.Get(b_placeholder));
	IsPassworded.Set(				packet.Get(b_placeholder));
	IsLaddered.Set(				packet.Get(b_placeholder));
	IsClanGame.Set(				packet.Get(b_placeholder));

#ifndef MULTIPLAYERDEMO
	//
	//	Get the CRC of the map and the mod
	//
	ULONG map_name_crc =		packet.Get(map_name_crc);
	ULONG mod_name_crc =		packet.Get(mod_name_crc);


	//
	//	Determine what the name of the mod and the map are from their CRC's
	//
	StringClass mod_name(0,true);
	StringClass map_name(0,true);
	if (ModPackageMgrClass::Get_Mod_Map_Name_From_CRC (mod_name_crc, map_name_crc, &mod_name, &map_name)) {
		Set_Mod_Name (mod_name);
		Set_Map_Name (map_name);
	} else {
		ModName = L"";
		MapName = L"";
	}

#endif // MULTIPLAYERDEMO

	return ;
}


//-----------------------------------------------------------------------------
bool cGameData::Does_Map_Exist (void)
{
	bool retval = true;

	//
	//	If the map and mod names are empty then it is due to the fact
	// that we couldn't find the map and/or mod when we looked
	//
	if (ModName.Is_Empty () && MapName.Is_Empty ()) {
		retval = false;
	}

	return retval;
}


//-----------------------------------------------------------------------------
void cGameData::Import_Tier_1_Data(const WOLGameInfo& gameInfo)
{
	Set_Map_Name(gameInfo.MapName());
	Set_Mod_Name(gameInfo.ModName());

	WideStringClass title(0, true);
	title.Convert_From(gameInfo.Title());
	Set_Game_Title(title);

	Set_Max_Players(gameInfo.MaxPlayers());
	Set_Current_Players(gameInfo.NumPlayers());

	// Set game flags
	IsDedicated.Set(gameInfo.IsDedicated());
	IsPassworded.Set(gameInfo.IsPassworded());
	IsLaddered.Set(gameInfo.IsLaddered());
	IsFriendlyFirePermitted.Set(gameInfo.IsFriendlyFire());
	IsFreeWeapons.Set(gameInfo.IsFreeWeapons());
	RemixTeams.Set(gameInfo.IsTeamRemix());
	IsTeamChangingAllowed.Set(gameInfo.IsTeamChange());
	IsClanGame.Set(gameInfo.IsClanGame());
	IsQuickMatchServer = gameInfo.IsQuickmatch();

	CanRepairBuildings.Set(gameInfo.IsRepairBuildings());
	DriverIsAlwaysGunner.Set(gameInfo.IsDriverGunner());
	SpawnWeapons.Set(gameInfo.IsSpawnWeapons());

	Clear_Clans();
	mClanSlots[0] = gameInfo.ClanID1();
	mClanSlots[1] = gameInfo.ClanID2();
}


//-----------------------------------------------------------------------------
void cGameData::Export_Tier_2_Data(cPacket & packet)
{
   packet.Add(TimeLimitMinutes);
   packet.Add((int) RadarMode);
   packet.Add(IntermissionTimeSeconds);
   packet.Add(MinQualifyingTimeMinutes);

	packet.Add(IsFriendlyFirePermitted.Get());
	packet.Add(IsFreeWeapons.Get());
	packet.Add(IsClientTrusted.Get());
	packet.Add(RemixTeams.Get());

	if (Is_Cnc()) {
		packet.Add(CanRepairBuildings.Get());
		packet.Add(DriverIsAlwaysGunner.Get());
		packet.Add(SpawnWeapons.Get());
	}

	packet.Add_Wide_Terminated_String(Motd, true);
}

//-----------------------------------------------------------------------------
void cGameData::Import_Tier_2_Data(cPacket & packet)
{
   int	i_placeholder = 0;
   bool	b_placeholder = false;

   Set_Time_Limit_Minutes(						packet.Get(i_placeholder));
   Set_Radar_Mode((RadarModeEnum)			packet.Get(i_placeholder));
   Set_Intermission_Time_Seconds(			packet.Get(i_placeholder));
   Set_Min_Qualifying_Time_Minutes(			packet.Get(i_placeholder));

	IsFriendlyFirePermitted.Set(				packet.Get(b_placeholder));
	IsFreeWeapons.Set(							packet.Get(b_placeholder));
	IsClientTrusted.Set(							packet.Get(b_placeholder));
	RemixTeams.Set(								packet.Get(b_placeholder));

	if (Is_Cnc()) {
		CanRepairBuildings.Set(					packet.Get(b_placeholder));
		DriverIsAlwaysGunner.Set(				packet.Get(b_placeholder));
		SpawnWeapons.Set(							packet.Get(b_placeholder));
	}

	WideStringClass motd;
	packet.Get_Wide_Terminated_String(motd.Get_Buffer(MAX_MOTD_LENGTH), MAX_MOTD_LENGTH, true);
	Set_Motd(motd);
}

//-----------------------------------------------------------------------------
void cGameData::Load_From_Server_Config(LPCSTR config_file)
{
	WWASSERT(config_file != NULL);
   WWASSERT(cMiscUtil::Is_String_Different(config_file, ""));

   INIClass * p_ini = Get_INI(config_file);
	StringClass full_filename(config_file, true);

	if (p_ini == NULL) {
      full_filename.Format("data\\%s", config_file);
      FILE * file = fopen(full_filename, "w");
	   fclose(file);

		p_ini = Get_INI(config_file);
   }
	WWASSERT(p_ini != NULL);

	LastServerConfigModTime = Get_Config_File_Mod_Time();

	char		map_name[MAX_MAPNAME_SIZE];
	char		mod_name[MAX_MAPNAME_SIZE];
	char		string8[256];
   bool		b;
   int		i;
   //float		f;


	//p_ini->Get_String(	INI_SECTION_NAME, "Password", Get_Password(), password, sizeof(password));
	//Set_Password(password);

   p_ini->Get_String(	INI_SECTION_NAME, "MapName", "", map_name, sizeof(map_name));
	StringClass map(map_name,true);
   Set_Map_Name(map);

   p_ini->Get_String(	INI_SECTION_NAME, "ModName", "", mod_name, sizeof(mod_name));
	StringClass mod(mod_name,true);
   Set_Mod_Name(mod);

	i = p_ini->Get_Int(	INI_SECTION_NAME, "TimeLimitMinutes",			Get_Time_Limit_Minutes());
	Set_Time_Limit_Minutes(i);

   i = p_ini->Get_Int(	INI_SECTION_NAME, "RadarMode",					Get_Radar_Mode());
	Set_Radar_Mode((RadarModeEnum) i);

   //i = p_ini->Get_Int(	INI_SECTION_NAME, "IntermissionTimeSeconds",	Get_Intermission_Time_Seconds());
	//Set_Intermission_Time_Seconds(i);
#ifndef FREEDEDICATEDSERVER
	i = p_ini->Get_Int(	INI_SECTION_NAME, "Port",							Get_Port());
	Set_Port(i);

	b = p_ini->Get_Bool(	INI_SECTION_NAME, "IsDedicated",					IsDedicated.Get());
	IsDedicated.Set(b);
#else ////FREEDEDICATEDSERVER
	IsDedicated.Set(true);
#endif //FREEDEDICATEDSERVER

	b = p_ini->Get_Bool(	INI_SECTION_NAME, "IsAutoRestart",				IsAutoRestart.Get());
	IsAutoRestart.Set(b);

	b = p_ini->Get_Bool(	INI_SECTION_NAME, "IsPassworded",				IsPassworded.Get());
	IsPassworded.Set(b);

	b = p_ini->Get_Bool(	INI_SECTION_NAME, "IsQuickMatch",				IsQuickMatchServer.Get());
	IsQuickMatchServer.Set(b);

	b = p_ini->Get_Bool(	INI_SECTION_NAME, "IsLaddered",					IsLaddered.Get());
#ifndef FREEDEDICATEDSERVER
	b = ((IsQuickMatchServer.Get() == true) ? true : b);
#endif //FREEDEDICATEDSERVER
	IsLaddered.Set(b);

	b = p_ini->Get_Bool(	INI_SECTION_NAME, "IsClanGame",					IsClanGame.Get());
#ifndef FREEDEDICATEDSERVER
	b = ((IsQuickMatchServer.Get() == true) ? false : b);
#endif /FREEDEDICATEDSERVER
	IsClanGame.Set(b);

	b = p_ini->Get_Bool(	INI_SECTION_NAME, "RemixTeams",					RemixTeams.Get());
	RemixTeams.Set(b);

	b = p_ini->Get_Bool(	INI_SECTION_NAME, "CanRepairBuildings",		CanRepairBuildings.Get());
	CanRepairBuildings.Set(b);

	b = p_ini->Get_Bool(	INI_SECTION_NAME, "DriverIsAlwaysGunner",		DriverIsAlwaysGunner.Get());
	DriverIsAlwaysGunner.Set(b);

	b = p_ini->Get_Bool(	INI_SECTION_NAME, "SpawnWeapons",				SpawnWeapons.Get());
	SpawnWeapons.Set(b);

	//
	// Disguise "IsClientTrusted" with something that sounds like you need it but
	// that doesn't scream "hack me"
	//
	//b = p_ini->Get_Bool(	INI_SECTION_NAME, "IsClientTrusted",			IsClientTrusted.Get());
	b = p_ini->Get_Bool(	INI_SECTION_NAME, "UseLagReduction",			IsClientTrusted.Get());
	IsClientTrusted.Set(b);

	// Read the 8 bit versions of the strings first and use those as defaults.
   p_ini->Get_String(INI_SECTION_NAME, "bGameTitle", "", string8, sizeof(string8));
	Set_Game_Title(WideStringClass(string8, true));

   p_ini->Get_String(INI_SECTION_NAME, "bMotd", "", string8, sizeof(string8));
	Set_Motd(WideStringClass(string8, true));

   p_ini->Get_String(INI_SECTION_NAME, "bPassword", "", string8, sizeof(string8));
	Set_Password(WideStringClass(string8, true));

	p_ini->Get_String(INI_SECTION_NAME, "bConfigName", "", string8, sizeof(string8));
	Set_Settings_Description(WideStringClass(string8, true));

	// Now read the 16 bit versions as overrides.
	WideStringClass wide_string(Get_Game_Title(), true);
	p_ini->Get_Wide_String(wide_string, INI_SECTION_NAME, "wGameTitle", wide_string.Peek_Buffer());
	Set_Game_Title(wide_string);

	wide_string = Get_Password();
	p_ini->Get_Wide_String(wide_string, INI_SECTION_NAME, "wPassword", wide_string.Peek_Buffer());
	Set_Password(wide_string);

	wide_string = Get_Motd();
	p_ini->Get_Wide_String(wide_string, INI_SECTION_NAME, "wMotd", wide_string.Peek_Buffer());
	Set_Motd(wide_string);

	wide_string = Get_Settings_Description();
	p_ini->Get_Wide_String(wide_string, INI_SECTION_NAME, "wConfigName", wide_string.Peek_Buffer());
	Set_Settings_Description(wide_string);

	for (int j = 0; j < MAX_MAPS; j++) {
		StringClass item_name(0,true);
		item_name.Format("MapName%02d", j);
		p_ini->Get_String(	INI_SECTION_NAME, item_name, "", map_name, sizeof(map_name));
		StringClass map(map_name,true);
		MapCycle[j] = map;
	}

	//char motd[2 * MAX_MOTD_LENGTH];
	//p_ini->Get_String(	INI_SECTION_NAME, "Motd", "", motd, sizeof(motd));
	//if (::strlen(motd) > 0) {
	//	Motd.Convert_From(motd);
	//}

   Release_INI(p_ini);
}

//-----------------------------------------------------------------------------
void cGameData::Save_To_Server_Config(LPCSTR config_file)
{
	WWASSERT(config_file != NULL);
   WWASSERT(cMiscUtil::Is_String_Different(config_file, ""));

   INIClass * p_ini = Get_INI(config_file);
   WWASSERT(p_ini != NULL);


	//
	// We can't overwrite entries, so clear them out first.
	//
	p_ini->Clear(INI_SECTION_NAME);
	p_ini->Put_Wide_String(	INI_SECTION_NAME, "wConfigName",					Get_Settings_Description());
   p_ini->Put_Wide_String(	INI_SECTION_NAME, "wPassword",					Get_Password());
	p_ini->Put_String(		INI_SECTION_NAME, "MapName",						Get_Map_Name());
	p_ini->Put_String(		INI_SECTION_NAME, "ModName",						Get_Mod_Name());
   p_ini->Put_Int(			INI_SECTION_NAME, "TimeLimitMinutes",			Get_Time_Limit_Minutes());
   p_ini->Put_Int(	   	INI_SECTION_NAME, "RadarMode",					Get_Radar_Mode());
   //p_ini->Put_Int(	   	INI_SECTION_NAME, "IntermissionTimeSeconds",	Get_Intermission_Time_Seconds());
   p_ini->Put_Int(			INI_SECTION_NAME, "Port",							Get_Port());
	p_ini->Put_Bool(			INI_SECTION_NAME, "IsDedicated",					IsDedicated.Get());
	p_ini->Put_Bool(			INI_SECTION_NAME, "IsAutoRestart",				IsAutoRestart.Get());
	p_ini->Put_Bool(			INI_SECTION_NAME, "IsPassworded",				IsPassworded.Get());
	p_ini->Put_Bool(			INI_SECTION_NAME, "IsQuickMatch",				IsQuickMatchServer.Get());
	p_ini->Put_Bool(			INI_SECTION_NAME, "IsLaddered",					IsLaddered.Get());
	p_ini->Put_Bool(			INI_SECTION_NAME, "IsClanGame",					IsClanGame.Get());
	p_ini->Put_Bool(			INI_SECTION_NAME, "RemixTeams",					RemixTeams.Get());
	p_ini->Put_Bool(			INI_SECTION_NAME, "CanRepairBuildings",		CanRepairBuildings.Get());
	p_ini->Put_Bool(			INI_SECTION_NAME, "DriverIsAlwaysGunner",		DriverIsAlwaysGunner.Get());
	p_ini->Put_Bool(			INI_SECTION_NAME, "SpawnWeapons",				SpawnWeapons.Get());
	//p_ini->Put_Bool(			INI_SECTION_NAME, "IsClientTrusted",			IsClientTrusted.Get());
	p_ini->Put_Bool(			INI_SECTION_NAME, "UseLagReduction",			IsClientTrusted.Get());
   p_ini->Put_Wide_String(	INI_SECTION_NAME, "wGameTitle",					Get_Game_Title());
   p_ini->Put_Wide_String(	INI_SECTION_NAME, "wMOTD",							Get_Motd());
#if (0)
	// Save out 8 bit string versions too.
	StringClass string8(256, true);
	WideStringClass(Get_Game_Title(), true).Convert_To(string8);
   p_ini->Put_String(		INI_SECTION_NAME, "bGameTitle",					string8);
	WideStringClass(Get_Motd(), true).Convert_To(string8);
   p_ini->Put_String(		INI_SECTION_NAME, "bMOTD",							string8);
	WideStringClass(Get_Password(), true).Convert_To(string8);
   p_ini->Put_String(		INI_SECTION_NAME, "bPassword",						string8);
#endif //(0)

	for (int j = 0; j < MAX_MAPS; j++) {
		StringClass item_name(0,true);
		item_name.Format("MapName%02d", j);
		p_ini->Put_String(	INI_SECTION_NAME, item_name,					MapCycle[j]);
	}

	if (!Motd.Is_Empty()) {
		StringClass motd;
		Motd.Convert_To(motd);
		p_ini->Put_String(	INI_SECTION_NAME, "Motd", motd.Peek_Buffer());
	}

	Save_INI(p_ini, config_file);
   Release_INI(p_ini);
}

//-----------------------------------------------------------------------------
void cGameData::Soldier_Added(SoldierGameObj * p_soldier)
{
	WWASSERT(p_soldier != NULL);

	if (IsFreeWeapons.Is_True() &&
		 p_soldier->Get_Player_Type() != PLAYERTYPE_NEUTRAL) {

		p_soldier->Give_All_Weapons();
		int weapon = 0;

		if (p_soldier->Is_Human_Controlled()) {
			weapon = 1;
		} else {
			weapon = 1 + rand() % 7;
		}

		WWASSERT(p_soldier->Get_Weapon_Bag() != NULL);
		p_soldier->Get_Weapon_Bag()->Select_Key_Number(weapon);
	}
}

//-----------------------------------------------------------------------------
void cGameData::Begin_Intermission(void)
{
	// If we are transitioning from one game to another we must first end the
	// previous game.
	GameModeClass* gameMode = GameModeManager::Find("WOL");

	if (gameMode && gameMode->Is_Active()) {
		WolGameModeClass* wolGame = reinterpret_cast<WolGameModeClass*>(gameMode);
		wolGame->End_Game();
	}

	IsIntermission.Set(true);
   IntermissionTimeRemaining = Get_Intermission_Time_Seconds();

	//
	//	Display a dialog with the win information on it
	//
	if (!ConsoleBox.Is_Exclusive()) {
		START_DIALOG (CNCWinScreenMenuClass);
	}
	return ;
}

//-----------------------------------------------------------------------------
void cGameData::Set_Maximum_World_Distance(float distance)
{
	//
	//	You will typically get this assert if you don't have the level file.
	//
	WWASSERT(distance > 0);
	MaximumWorldDistance = distance;
}

//-----------------------------------------------------------------------------
void cGameData::Set_Min_Qualifying_Time_Minutes(int minutes)
{
	WWASSERT(minutes >= 0);
	MinQualifyingTimeMinutes = minutes;
}

//-----------------------------------------------------------------------------
int cGameData::Get_Duration_Seconds(void)
{
	return (int)(TimeLimitMinutes * 60 - TimeRemainingSeconds);
}

//-----------------------------------------------------------------------------
WideStringClass cGameData::Get_Team_Word(void)
{
	//
	// In clan games, the work "Clan" is used instead of "Team"
	//

	if (IsClanGame.Is_True()) {
		return TRANSLATION(IDS_MP_CLAN);
	} else {
		return TRANSLATION(IDS_MP_TEAM);
	}
}

//-----------------------------------------------------------------------------
void cGameData::Set_Time_Remaining_Seconds(float time_remaining_seconds)
{
   WWASSERT(time_remaining_seconds >= 0);

	TimeRemainingSeconds = time_remaining_seconds;
}

//-----------------------------------------------------------------------------
void cGameData::Reset_Time_Remaining_Seconds(void)
{
	TimeRemainingSeconds = 60 * TimeLimitMinutes;
}

//-----------------------------------------------------------------------------
float cGameData::Get_Time_Remaining_Seconds(void)
{
	return TimeRemainingSeconds;
}

//-----------------------------------------------------------------------------
bool cGameData::Is_Valid_Player_Type(int player_type)
{
	bool is_valid = true;

	if (!Is_Single_Player()) {
		//if (player_type == PLAYERTYPE_RENEGADE &&
		//	 Is_Team_Game()) {
		if (player_type == PLAYERTYPE_RENEGADE) {
			is_valid = false;
		}

		/*
		if ((player_type == PLAYERTYPE_NOD || player_type == PLAYERTYPE_GDI) &&
			 !Is_Team_Game()) {
			is_valid = false;
		}
		*/
	}

	return is_valid;
}

//-----------------------------------------------------------------------------
int cGameData::Choose_Player_Type(cPlayer* player, int team_choice, bool is_grunt)
{
	if (IsClanGame.Is_True() && !is_grunt) {
		// Bots are assigned to the smallest team.
		if (player && !player->Is_Human()) {
			int team = Choose_Smallest_Team();

			WWDEBUG_SAY(("CLANS: Bot '%S' assigned to smallest team (%d)\n",
				(const WCHAR*)player->Get_Name(), team));

			return team;
		}

		// If there are other players from the same clan then team up them.
		// Otherwise we must be the first of our clan so select the next available team.
		cPlayer* clanMate = cPlayerManager::Find_Clan_Mate(player);

		if (clanMate) {
			int team = clanMate->Get_Player_Type();

			WWDEBUG_SAY(("CLANS: Player '%S' assigned to team with clanmates (%d)\n",
				(const WCHAR*)player->Get_Name(), team));

			return team;
		} else {
			int team = Choose_Available_Team(team_choice);

			WWDEBUG_SAY(("CLANS: Player '%S' assigned to available team (%d)\n",
				(const WCHAR*)player->Get_Name(), team));

			return team;
		}

		WWDEBUG_SAY(("CLANS: ERROR - Player not assigned to team\n"));
		WWASSERT("ERROR: Player not assigned to team");
	} else {
		if (PLAYERTYPE_RENEGADE == team_choice || IsTeamChangingAllowed.Is_False()) {
			return Choose_Smallest_Team();
		}
	}

	return team_choice;
}


int cGameData::Choose_Available_Team(int preference)
{
	// If the perferred team is available then use it.
	if (preference != -1) {
		cTeam* team = cTeamManager::Find_Team(preference);

		if (team && (team->Tally_Size() == 0)) {
			return team->Get_Id();
		}
	}

	// Find the next available team
	cTeam* team = cTeamManager::Find_Empty_Team();

	if (team) {
		return team->Get_Id();
	}

	WWDEBUG_SAY(("ERROR: No available team found\n"));
	WWASSERT("ERROR: No available team found");
	return -1;
}


//-----------------------------------------------------------------------------
int cGameData::Choose_Smallest_Team(void)
{
	//WWASSERT(Is_Team_Game());

	cTeam * p_team_0 = cTeamManager::Find_Team(0);
	WWASSERT(p_team_0 != NULL);
	cTeam * p_team_1 = cTeamManager::Find_Team(1);
	WWASSERT(p_team_1 != NULL);

	int team = -999;

	int size_team_0 = p_team_0->Tally_Size();
	int size_team_1 = p_team_1->Tally_Size();

	if (size_team_0 < size_team_1) {
		team = p_team_0->Get_Id();
	} else if (size_team_1 < size_team_0) {
		team = p_team_1->Get_Id();
	} else {
		//
		// Both teams are equal in size, so choose the losing team.
		//
		if (p_team_0->Get_Score() < p_team_1->Get_Score()) {
			team = p_team_0->Get_Id();
		} else if (p_team_0->Get_Score() > p_team_1->Get_Score()) {
			team = p_team_1->Get_Id();
		} else {
			//
			// Both teams have the same score. Choose Randomly.
			//
			if (rand() % 2 == 0) {
				team = p_team_0->Get_Id();
			} else {
				team = p_team_1->Get_Id();
			}
		}
	}

	WWASSERT(team != -999);

	return team;
}

//-----------------------------------------------------------------------------
bool cGameData::Is_Game_Over(void)
{
	WWASSERT(cNetwork::I_Am_Server());

	bool is_game_over = false;

   //
	// Games may be manually ended with the "gameover" command.
	//
	if (IsManualRestart) {
      IsManualRestart = false;
		WinType = WIN_TYPE_FORCED;
      is_game_over = true;
   }

	//
	// Games may be ended with the "quit" command.
	//
	if (!is_game_over && IsManualExit) {
		WinType = WIN_TYPE_FORCED;
      is_game_over = true;
	}

   //
   // Time expired?
   //
   if (!is_game_over && Is_Time_Limit() && TimeRemainingSeconds <= WWMATH_EPSILON)  {
		WinType = WIN_TYPE_TIME;
      is_game_over = true;
   }

	//
	// If game time has passed but gameplay now pends, end the game
	//
   if (!is_game_over && Get_Game_Duration_S() > 0 && !Is_Gameplay_Permitted())  {
		WinType = WIN_TYPE_FORCED;
      is_game_over = true;
   }

	/*
	//
	// THIS NEEDS REFINEMENT
	//
	// The following test will end a non-mission game when your opponents
	// abandon you. To determine whether any combat has occurred we simply
	// examine the IsBloodShed flag.
	//
	if (!Is_Mission() && IsBloodShed.Is_True() &&
      ((Is_Team_Game() && cTeamManager::Get_Living_Count() <= 1) ||
       (!Is_Team_Game() && cPlayerManager::Get_Living_Count() <= 1))) {
      return true;
   }
	*/

	if (is_game_over)
	{
		TimeRemainingSeconds = 0;
	}

	return is_game_over;
}




//-----------------------------------------------------------------------------
bool cGameData::Has_Config_File_Changed(void)
{
	unsigned long mod_time = Get_Config_File_Mod_Time();

	if (LastServerConfigModTime != mod_time) {
		return(true);
	}
	return(false);
}



//-----------------------------------------------------------------------------
unsigned long cGameData::Get_Config_File_Mod_Time(void)
{
	StringClass full_filename(IniFilename, true);
	RawFileClass file(full_filename);

	if (!file.Is_Available()) {
      full_filename.Format("data\\%s", IniFilename);
		file.Set_Name(full_filename);
   }

	if (file.Is_Available()) {
		file.Open();
		unsigned long mod_time = file.Get_Date_Time();
		file.Close();
		return(mod_time);
	}
	return(0);
}




//-----------------------------------------------------------------------------
void cGameData::Game_Over_Processing(void)
{
   WWDEBUG_SAY(("cGameData::Game_Over_Processing\n"));

	WWASSERT(cNetwork::I_Am_Server());

   int winning_team = cTeamManager::Get_Leaders_Id();

	//
	// Reload the game settings if they changed on disk.
	//
	if (Has_Config_File_Changed()) {
		//
		// Verify that the new settings are good.
		//
		cGameDataCnc *test_game_settings = (cGameDataCnc*) Create_Game_Of_Type(cGameData::GAME_TYPE_CNC);
		WWASSERT(test_game_settings != NULL);

		bool ok = false;

		if (test_game_settings) {
			test_game_settings->Set_Ini_Filename(IniFilename);
			test_game_settings->Load_From_Server_Config();

			WideStringClass outMsg;
			ok = test_game_settings->Is_Valid_Settings(outMsg, true);
			delete test_game_settings;
		}

		//
		// If they are good settings then reload them.
		//
		if (ok) {
			ConsoleBox.Print("Game settings changed - realoading settings\n");
			Load_From_Server_Config();
		}
	}

	Rotate_Map();

	cPlayerManager::Increment_Player_Times();

	//
	// Compute the game duration
	//
	DWORD duration_s = (int)((TIMEGETTIME() - GameStartTimeMs) / 1000.0f);
	Set_Game_Duration_S(duration_s);

	//
	// Record the MVP name and track the number of consecutives
	//
	WideStringClass mvp_name = cPlayerManager::Determine_Mvp_Name();
	if (mvp_name.Compare_No_Case(MvpName)) {
		Set_Mvp_Count(1);
		Set_Mvp_Name(cPlayerManager::Determine_Mvp_Name());
	} else {
		Increment_Mvp_Count();
	}

	mWinnerID = winning_team;

	cWinEvent * p_win = new cWinEvent;
	p_win->Init(mWinnerID, PLAYER_ID_UNKNOWN, IsMapCycleOver);

	cPlayerManager::Compute_Ladder_Points(winning_team);

	cTeamManager::Log_Team_List();
	cPlayerManager::Log_Player_List();

	//
	// Results logs wrap back to 1 after 100, and overwrite.
	//
	int log_num = cUserOptions::ResultsLogNumber.Get();
	log_num++;
	if (log_num > 100)
	{
		log_num = 1;
	}
	cUserOptions::ResultsLogNumber.Set(log_num);


	Begin_Intermission();
}

//-----------------------------------------------------------------------------
bool cGameData::Is_Gameplay_Permitted(void)
{
	bool permitted = true;

	if (IsIntermission.Is_True()) {
		permitted = false;
	}

	if (!GameModeManager::Find ("Combat")->Is_Active ()) {
		permitted = false;
	}

	return permitted;
}

void cGameData::Set_Clan(int slot, unsigned long clanID)
{
	WWASSERT(slot >= 0 && slot < MAX_CLAN_SLOTS);
	mClanSlots[slot] = clanID;
}


unsigned long cGameData::Get_Clan(int slot) const
{
	WWASSERT(slot >= 0 && slot < MAX_CLAN_SLOTS);
	return mClanSlots[slot];
}


void cGameData::Clear_Clans(void)
{
	for (int slot = 0; slot < MAX_CLAN_SLOTS; ++slot) {
		mClanSlots[slot] = 0;
	}
}


int cGameData::Find_Free_Clan_Slot(void) const
{
	if (IsClanGame.Is_True()) {
		for (int slot = 0; slot < MAX_CLAN_SLOTS; ++slot) {
			if (mClanSlots[slot] == 0) {
				return slot;
			}
		}
	}

	return -1;
}


bool cGameData::Is_Clan_Competing(unsigned long clanID) const
{
	if (IsClanGame.Is_True() && (clanID != 0)) {
		for (int slot = 0; slot < MAX_CLAN_SLOTS; ++slot) {
			if (mClanSlots[slot] == clanID) {
				return true;
			}
		}
	}

	return false;
}

//-----------------------------------------------------------------------------
bool cGameData::Is_Clan_Game_Open(void) const
{
	if (IsClanGame.Is_True()) {
		for (int slot = 0; slot < MAX_CLAN_SLOTS; ++slot) {
			if (mClanSlots[slot] == 0) {
				return true;
			}
		}
	}

	return false;
}

//-----------------------------------------------------------------------------
cGameData * cGameData::Create_Game_Of_Type(GameTypeEnum game_type)
{
	/*
	cGameData * p_game_data = NULL;

	switch (game_type) {
		//case GAME_TYPE_DEATHMATCH:			p_game_data = new cGameDataDeathMatch;			break;
		//case GAME_TYPE_TEAM_DEATHMATCH:	p_game_data = new cGameDataTeamDeathMatch;	break;
		case GAME_TYPE_CNC:					p_game_data = new cGameDataCnc;					break;
		default:									DIE;														break;
	}

	return p_game_data;
	*/

	WWASSERT(game_type == GAME_TYPE_CNC);

	return new cGameDataCnc;
}

/*
//-----------------------------------------------------------------------------
const char* cGameData::Get_Game_Type_Name(GameTypeEnum type)
{
	static const char* _modeNames[] = {"SP", "SK", "CNC"};
	return _modeNames[type];
}
*/

//-----------------------------------------------------------------------------
const char * cGameData::Get_Game_Type_Name(void) const
{
	WideStringClass wide_name;
	wide_name.Format(L"%s", Get_Game_Name());
	StringClass name;
	wide_name.Convert_To(name);

	return name.Peek_Buffer();
}

//-----------------------------------------------------------------------------
void cGameData::Add_Bottom_Text(WideStringClass & text)
{
	//WWASSERT(text != NULL);

/* 	WWASSERT(PFont != NULL);
	float x = Render2DClass::Get_Screen_Resolution().Center().X -
		PFont->String_Width(text) / 2.0f;

	float y = MultiHUDClass::Get_Bottom_Text_Y_Pos();
	y -= 1.2 * PFont->Char_Height();
	MultiHUDClass::Set_Bottom_Text_Y_Pos(y);

   WWASSERT(PTextRenderer != NULL);
	PTextRenderer->Set_Location(Vector2(cMathUtil::Round(x), cMathUtil::Round(y)));
	PTextRenderer->Draw_Text(text);
*/
	BottomText.Add(text);
}

//-----------------------------------------------------------------------------
void cGameData::Show_Game_Settings_Limits(void)
{
	if (PTextRenderer == NULL) {
		return;
	}

	bool changed=false;

	WideStringClass text(0,true);
	unsigned color;

	Get_Time_Limit_Text(text);
	if (text!=renderer_time_text) {
		renderer_time_text=text;
		changed=true;
	}

	// Dedicated server label
	if (cNetwork::I_Am_Only_Server()) {
		Get_Dedicated_Server_Label(text,color);
	}
	else {
		text="";
		color=0;
	}
	if (text!=renderer_dedicated_server_label) {
		renderer_dedicated_server_label=text;
		changed=true;
	}
	if (color!=renderer_dedicated_server_color) {
		renderer_dedicated_server_color=color;
		changed=true;
	}

	// Gameplay pending label -
	bool show_gameplay_label = false;
	if (!Is_Gameplay_Permitted() || (COMBAT_STAR == NULL))	{
		show_gameplay_label = true;
	}
	//if (IsIntermission.Is_False() && (!Is_Gameplay_Permitted() || (COMBAT_STAR == NULL))) {
	if (show_gameplay_label) {
		Get_Gameplay_Not_Permitted_Label(text,color);
	} else {
		text="";
		color=0;
	}
	if (text!=renderer_gameplay_label) {
		renderer_gameplay_label=text;
		changed=true;
	}
	if (color!=renderer_gameplay_color) {
		renderer_gameplay_color=color;
		changed=true;
	}

	if (!changed) {
		if (BottomText.Count()!=OldBottomText.Count()) {
			changed=true;
		}
		else {
			for (int j=0;j<BottomText.Count();++j) {
				if (BottomText[j]!=OldBottomText[j]) {
					changed=true;
					break;
				}
			}
		}
	}


	// Render if needed  -------------------------------------------------------
	float charHeight = PTextRenderer->Peek_Font()->Get_Char_Height();

	if (!changed) {
		// Proceed the multihud by one line (as we are caching the time render which would usually do this)
		float y = MultiHUDClass::Get_Bottom_Text_Y_Pos();
		y -= 1.2 * charHeight;
		MultiHUDClass::Set_Bottom_Text_Y_Pos(y);
		BottomText.Reset_Active();
		return;
	}

	// Re-compose the OldBottomText vector

	OldBottomText.Reset_Active();
	for (int j=0;j<BottomText.Count();++j) {
		OldBottomText.Add(BottomText[j]);
	}
	BottomText.Reset_Active();

	PTextRenderer->Reset();

	float x,y;

	// Render bottom text (start with time text, which is to be first)
	Vector2 textExtent = PTextRenderer->Get_Text_Extents(text);
	x = Render2DClass::Get_Screen_Resolution().Center().X -  textExtent.X / 2.0f;

	//Always draw on the bottom
	MultiHUDClass::Set_Bottom_Text_Y_Pos( Render2DClass::Get_Screen_Resolution().Bottom - 15 );

	y = MultiHUDClass::Get_Bottom_Text_Y_Pos();
	y -= 1.2 * charHeight;
	Vector2 loc(cMathUtil::Round(x), cMathUtil::Round(y));
	PTextRenderer->Set_Location(loc);
	PTextRenderer->Build_Sentence(renderer_time_text);
	PTextRenderer->Draw_Sentence();

	for (j=0;j<OldBottomText.Count();++j) {
		y -= 1.2 * charHeight;
		loc[1]=cMathUtil::Round(y);
		PTextRenderer->Set_Location(loc);
		PTextRenderer->Build_Sentence(OldBottomText[j]);
		PTextRenderer->Draw_Sentence();
	}
	MultiHUDClass::Set_Bottom_Text_Y_Pos(y);



	// Dedicated server label
	if (cNetwork::I_Am_Only_Server()) {
		Vector2 extent = PTextRenderer->Get_Text_Extents(renderer_dedicated_server_label);
		x = Render2DClass::Get_Screen_Resolution().Center().X - extent.X / 2.0f;

		y = Render2DClass::Get_Screen_Resolution().Center().Y;
		PTextRenderer->Set_Location(Vector2(cMathUtil::Round(x), cMathUtil::Round(y)));
		PTextRenderer->Build_Sentence(renderer_dedicated_server_label);
		PTextRenderer->Draw_Sentence(renderer_dedicated_server_color);
	}

	// Gameplay pending label
	if (show_gameplay_label) {
		Vector2 extent = PTextRenderer->Get_Text_Extents(renderer_gameplay_label);
		x = Render2DClass::Get_Screen_Resolution().Center().X -  extent.X / 2.0f;
		y = Render2DClass::Get_Screen_Resolution().Center().Y + 1.2 * charHeight;
		PTextRenderer->Set_Location(Vector2(cMathUtil::Round(x), cMathUtil::Round(y)));
		PTextRenderer->Build_Sentence(renderer_gameplay_label);
		PTextRenderer->Draw_Sentence(color);
	}
}

//-----------------------------------------------------------------------------
void cGameData::Get_Dedicated_Server_Label(WideStringClass& text, unsigned& color)
{
	color = 0xFFFFFFFF; // white
	if (cMathUtil::Round(TimeManager::Get_Seconds()) % 2 != 0) {
		color = 0xFF00FF00;	// green
	}

	text = TRANSLATION(IDS_MP_DEDICATED_SERVER);
}

//-----------------------------------------------------------------------------
void cGameData::Get_Gameplay_Not_Permitted_Label(WideStringClass& text, unsigned& color)
{
	color = 0xFFFFFFFF; // white
	if (cMathUtil::Round(TimeManager::Get_Seconds()) % 2 != 0) {
		color = 0xFF00FF00;	// green
	}

	text = TRANSLATION(IDS_MP_GAMEPLAY_PENDING);
}


//-----------------------------------------------------------------------------
void cGameData::Get_Time_Limit_Text(WideStringClass& text)
{
   if (IsIntermission.Is_True()) {

      text.Format(
			L"%s: %d",
			TRANSLATION(IDS_MP_NEXTGAME_COUNTDOWN),
			cMathUtil::Round(Get_Intermission_Time_Remaining()));

	} else if (Is_Time_Limit()) {

      int hours	= 0;
      int mins		= 0;
      int seconds	= 0;

		cMiscUtil::Seconds_To_Hms(TimeRemainingSeconds, hours, mins, seconds);

      WideStringClass time_string(0, true);
      time_string.Format(L"%02d:%02d:%02d", hours, mins, seconds);

		text.Format(L"%s: %s", TRANSLATION(IDS_MP_TIME_REMAINING), time_string);
   }

}

//-----------------------------------------------------------------------------
void cGameData::Think(void)
{
	if (IsIntermission.Is_True()) {
		IntermissionTimeRemaining -= TimeManager::Get_Frame_Real_Seconds();
		if (IntermissionTimeRemaining < 0) {

			IntermissionTimeRemaining = 0;
		}
	} else if (Is_Gameplay_Permitted()) {
		TimeRemainingSeconds -= TimeManager::Get_Frame_Real_Seconds();
		if (TimeRemainingSeconds < 0) {
			TimeRemainingSeconds = 0;
		}

		DWORD duration_s = (int)((TIMEGETTIME() - GameStartTimeMs) / 1000.0f);
		Set_Game_Duration_S(duration_s);
	}

	if (cNetwork::I_Am_Server()) {
		//
		// Update CurrentPlayers
		//
		int max = (int) WWMath::Max(
			cPlayerManager::Count(),
			cNetwork::PServerConnection->Get_Num_RHosts());
		Set_Current_Players(max);
	}

	DEMO_SECURITY_CHECK;

	FrameCount++;
}

//-----------------------------------------------------------------------------
void cGameData::Render(void)
{
	Show_Game_Settings_Limits();

	if (PTextRenderer != NULL) {
		WWPROFILE("cGameData::Render");
		PTextRenderer->Render();
	}
}

//-----------------------------------------------------------------------------
void cGameData::On_Game_Begin(void)
{
	//
	// Note, On_Game_Begin happens after load.
	//

   WWDEBUG_SAY(("cGameData::On_Game_Begin\n"));

	GetSystemTime(&GameStartTime);
	FrameCount = 0;

	GameStartTimeMs = TIMEGETTIME();

	//
	// Clear the MVP name
	// Let MVP carry over into next game.
	//
	//MvpName.Format(L"");
	//MvpCount = 0;

	//
	// Reset the game duration
	//
	GameDurationS = 0;

	//
	// Notify the combat layer about various server options
	//
	cCsDamageEvent::Set_Are_Clients_Trusted(IsClientTrusted.Get());
	BuildingGameObj::Set_Can_Repair_Buildings(CanRepairBuildings.Get());
	VehicleGameObj::Set_Default_Driver_Is_Gunner(DriverIsAlwaysGunner.Get());
	CombatManager::Set_Friendly_Fire_Permitted(IsFriendlyFirePermitted.Get());
	CombatManager::Set_Beacon_Placement_Ends_Game(false);

	//
	// Filter out unwanted spawners
	//
	if (cNetwork::I_Am_Server()) {
		Filter_Spawners();
	}

	//
	// Adjust teaming for auto-teamed games
	//
	//if (!IS_MISSION && cNetwork::I_Am_Server() && Is_Team_Game() && IsTeamChangingAllowed.Is_False()) {
	if (!IS_MISSION && cNetwork::I_Am_Server()) {

		if (IsClanGame.Is_False() && IsTeamChangingAllowed.Is_False() && RemixTeams.Is_True()) {
			//
			// Remix Teams if the server option is set.
			//
			Remix_Team_Sides();
		} else {
			//
			// In non-remixed C&C mode, we have a 50-50 chance of the teams being swapped. I prefer this
			// to simply alternating, because if you alternate and the number of maps in the
			// cycle is even, you will always play GDI on the same 50% of the maps.
			//
			if (Is_Skirmish() || (Is_Cnc() && (rand() % 2 == 0))) {
				Swap_Team_Sides();
			}
		}

		if (IsClanGame.Is_False() && IsTeamChangingAllowed.Is_False())
		{
			//
			// We always rebalance the teams numerically
			//
			Rebalance_Team_Sides();
		}
	}

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

	if (gameMode && gameMode->Is_Active()) {
		WolGameModeClass* wolGame = reinterpret_cast<WolGameModeClass*>(gameMode);
		WWASSERT(wolGame != NULL);
		wolGame->Start_Game(this);
	}
}

//-----------------------------------------------------------------------------
void cGameData::On_Game_End(void)
{
   WWDEBUG_SAY(("cGameData::On_Game_End\n"));

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

	if (gameMode && gameMode->Is_Active()) {
		WolGameModeClass* wolGame = reinterpret_cast<WolGameModeClass*>(gameMode);
		wolGame->End_Game();
	}
}

//-----------------------------------------------------------------------------
int	cGameData::Get_Mission_Number_From_Map_Name( const char * map_name )
{
	// If tutorial, return tutorial number
	if ( ::strnicmp( map_name, "M00_T", 5 ) == 0 ) {
		#define	TUTORIAL_LOAD_MENU_NUMBER		90
		return TUTORIAL_LOAD_MENU_NUMBER;
	}


	// Assume the map name is in the form "mXX.mix" where xx is the level number
	if ( ::strlen( map_name ) > 0 ) {
		return ::atoi( ((const char *)map_name)+1 );
	}
	return 0;
}

//-----------------------------------------------------------------------------
const StringClass& cGameData::Get_Map_Cycle(int map)
{
	WWASSERT(map >= 0 && map < MAX_MAPS);

#ifdef MULTIPLAYERDEMO
	MapCycle[map].Format("C&C_Under.mix");
#endif // MULTIPLAYERDEMO

	return MapCycle[map];
}

//-----------------------------------------------------------------------------
void
cGameData::Set_Map_Cycle(int map, const StringClass & map_name)
{
	WWASSERT(map >= 0 && map < MAX_MAPS);

	MapCycle[map] = map_name;
}

//-----------------------------------------------------------------------------
void
cGameData::Clear_Map_Cycle(void)
{
	for (int i = 0; i < MAX_MAPS; i++)
	{
		MapCycle[i].Format("");
	}
}

//-----------------------------------------------------------------------------
void
cGameData::Rotate_Map(void)
{
	IsMapCycleOver = false;

	//
	// Determine the current map
	//
	int current_map = MapCycleIndex;
#if (0)
	int current_map = 0;
	for (int i = 0; i < MAX_MAPS; i++)
	{
		if (MapName == MapCycle[i]) {
			current_map = i;
			break;
		}
	}
#endif //(0)
	
	//
	// Increment it
	//
	current_map++;

	//
	// Go back to the first map if necessary
	//
	if (current_map >= MAX_MAPS || MapCycle[current_map].Is_Empty()) {
		current_map = 0;

		if (DoMapsLoop == false) {
			IsMapCycleOver = true;
		}
	}

	//
	// Set the map name
	//
#ifdef MULTIPLAYERDEMO
	MapName.Format("C&C_Under.mix");
#else
	MapName = MapCycle[current_map];
#endif

	MapCycleIndex = current_map;

	return ;
}

//-----------------------------------------------------------------------------
void cGameData::Set_Win_Text(WideStringClass & text)
{
	WinText = text;
}

//-----------------------------------------------------------------------------
void cGameData::ReceiveSignal(MPChooseTeamSignal& choice)
{
	if (choice.GetItemA() == true) {
		//
		// Do nothing if the player selected 'auto' rather than GDI or NOD.
		//
		if (choice.GetItemB() != PLAYERTYPE_RENEGADE) {
			cNetwork::SwitchTeam(choice.GetItemB());
		}
	}
}

//-----------------------------------------------------------------------------
void cGameData::Filter_Spawners(void)
{
	WWASSERT(cNetwork::I_Am_Server());

	if (SpawnWeapons.Is_False()) {
		//
		// Disable multiplay weapons spawners
		//
		DynamicVectorClass<SpawnerClass*> spawner_list = SpawnManager::Get_Spawner_List();

		for (int i = 0; i < spawner_list.Count(); i++) {

			WWASSERT(spawner_list[i] != NULL);

			if (spawner_list[i]->Get_Definition().Is_Multiplay_Weapon_Spawner()) {
				spawner_list[i]->Enable(false);
			}
		}
	}
}

//------------------------------------------------------------------------------------
void cGameData::Set_Mvp_Name(const WideStringClass name)
{
	MvpName = name;
}

//------------------------------------------------------------------------------------
void cGameData::Set_Mvp_Count(int count)
{
	WWASSERT(count >= 0);

	MvpCount = count;
}

//------------------------------------------------------------------------------------
void cGameData::Set_Win_Type(WinTypeEnum type)
{
	WinType = type;
}

//------------------------------------------------------------------------------------
void cGameData::Set_Game_Duration_S(DWORD seconds)
{
	GameDurationS = seconds;
}

//-----------------------------------------------------------------------------
void cGameData::Get_Description(WideStringClass & description)
{
	const WideStringClass delimiter	= L"\t";
	const WideStringClass newline		= L"\n";
	const WideStringClass yes			= TRANSLATE(IDS_YES);
	const WideStringClass no			= TRANSLATE(IDS_NO);

	WideStringClass attribute(0, true);
	WideStringClass value(0, true);

	//
	// Host
	//
	attribute = TRANSLATE(IDS_MP_HOST);
	value = Owner;
	description += (attribute + delimiter + value + newline);

	//
	// Game Name
	//
	attribute = TRANSLATE(IDS_MENU_TEXT292);
	value = GameTitle;
	description += (attribute + delimiter + value + newline);

	//
	// Map Name
	//
	if (ModName.Is_Empty () == false) {
		attribute = L"Mod Name:";
		value = ModName;
		description += (attribute + delimiter + value + newline);
	}

	//
	// Map Name
	//
	attribute = TRANSLATE(IDS_MP_GAME_LIST_HEADER_GAME_MAP);
	value = MapName;
	description += (attribute + delimiter + value + newline);

	//
	// Time Limit
	//
	attribute = TRANSLATE(IDS_MENU_TEXT298);
	if (Is_Time_Limit()) {
		value.Format(L"%d %s", TimeLimitMinutes, TRANSLATE(IDS_MP_MINUTES));
	} else {
		value = TRANSLATE(IDS_MP_NONE);
	}
	description += (attribute + delimiter + value + newline);

	//
	// Max. Players
	//
	attribute = TRANSLATE(IDS_MENU_TEXT294);
	value.Format(L"%d", MaxPlayers);
	description += (attribute + delimiter + value + newline);

	//
	// Radar
	//
	attribute = TRANSLATE(IDS_MENU_TEXT488);
	switch (RadarMode) {
		case RADAR_NOBODY:
		   value = TRANSLATION(IDS_MP_RADAR_MODE_NOBODY);
			break;

		case RADAR_TEAMMATES:
		   value = TRANSLATION(IDS_MP_RADAR_MODE_TEAMMATES);
			break;

		case RADAR_ALL:
		   value = TRANSLATION(IDS_MP_RADAR_MODE_ALL);
			break;

		default:
			DIE;
	}
	description += (attribute + delimiter + value + newline);

	//
	// Allow Quickmatching
	//
	attribute = TRANSLATE(IDS_MENU_TEXT584);
	value = (IsQuickMatchServer.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);

	//
	// Dedicated
	//
	attribute = TRANSLATE(IDS_MENU_TEXT309);
	value = (IsDedicated.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);

	//
	// Friendly Fire
	//
	attribute = TRANSLATE(IDS_MENU_TEXT301);
	value = (IsFriendlyFirePermitted.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);

	//
	// Manual Teaming
	//
	attribute = TRANSLATE(IDS_MENU_TEXT490);
	value = (IsTeamChangingAllowed.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);

	//
	// Passworded
	//
	attribute = TRANSLATE(IDS_MP_PASSWORDED);
	value = (IsPassworded.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);

	//
	// Laddered
	//
	attribute = TRANSLATE(IDS_MENU_TEXT496);
	value = (IsLaddered.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);

	//
	// Clan Game
	//
	attribute = TRANSLATE(IDS_MENU_TEXT497);
	value = (IsClanGame.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);

	/*
	//
	// Trust Clients
	//
	attribute = TRANSLATE(IDS_MENU_TEXT585);
	value = (IsClientTrusted.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);
	*/

	//
	// Remix Teams
	//
	attribute = TRANSLATE(IDS_MENU_TEXT587);
	value = (RemixTeams.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);

	//
	// Can Repair Buildings
	//
	attribute = TRANSLATE(IDS_MENU_TEXT590);
	value = (CanRepairBuildings.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);

	//
	// Driver Is Always Gunner
	//
	attribute = TRANSLATE(IDS_MENU_TEXT591);
	value = (DriverIsAlwaysGunner.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);

	//
	// Weapon spawning
	//
	attribute = TRANSLATE(IDS_MENU_TEXT592);
	value = (SpawnWeapons.Get() ? yes : no);
	description += (attribute + delimiter + value + newline);
}

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

//cGameData *						The_Game(void)							{WWASSERT(PTheGameData != NULL); return PTheGameData;}
cGameData *						The_Game(void)							{return PTheGameData;}

cGameDataSinglePlayer *		The_Single_Player_Game(void)		{WWASSERT(The_Game()->As_Single_Player() != NULL);		return The_Game()->As_Single_Player();}
cGameDataSkirmish *			The_Skirmish_Game(void)				{WWASSERT(The_Game()->As_Skirmish() != NULL);			return The_Game()->As_Skirmish();}
cGameDataCnc *					The_Cnc_Game(void)					{WWASSERT(The_Game()->As_Cnc() != NULL);					return The_Game()->As_Cnc();}







	/*
	if (Is_Team_Game()) {
		has_opponents =
			cPlayerManager::Get_Count(PLAYERTYPE_NOD) > 0 &&
			cPlayerManager::Get_Count(PLAYERTYPE_GDI) > 0;
	} else {
		has_opponents = (cPlayerManager::Get_Count(PLAYERTYPE_RENEGADE) > 1);
	}
	*/

/*
//-----------------------------------------------------------------------------
void cGameData::Set_Min_Game_Time_Required_Mins(int mins)
{
	WWASSERT(mins >= 0 );
	MinGameTimeRequiredMins = mins;
}
*/

/*
//-----------------------------------------------------------------------------
void cGameData::Set_Full_Score_Time_Threshold_Mins(int mins)
{
	WWASSERT(mins >= 0);
	FullScoreTimeThresholdMins = mins;
}
*/

//#include "helptext.h"
//#include "gddeathmatch.h"
//#include "gdteamdeathmatch.h"
	//MinGameTimeRequiredMins			= 10;
	//FullScoreTimeThresholdMins		= 30;
	//MinGameTimeRequiredMins			= rhs.MinGameTimeRequiredMins;
	//FullScoreTimeThresholdMins		= rhs.FullScoreTimeThresholdMins;
   //packet.Add(MinGameTimeRequiredMins);
   //packet.Add(FullScoreTimeThresholdMins);
   //Set_Min_Game_Time_Required_Mins(			packet.Get(i_placeholder));
   //Set_Full_Score_Time_Threshold_Mins(		packet.Get(i_placeholder));
//cGameDataDeathMatch *		The_Deathmatch_Game(void)			{WWASSERT(The_Game()->As_Deathmatch() != NULL);			return The_Game()->As_Deathmatch();}
//cGameDataTeamDeathMatch *	The_Team_Deathmatch_Game(void)	{WWASSERT(The_Game()->As_Team_Deathmatch() != NULL);	return The_Game()->As_Team_Deathmatch();}

	//if (Is_Team_Game()) {
	/*
	} else {
		has_opponents = (cPlayerManager::Tally_Team_Size(PLAYERTYPE_RENEGADE) > 1);
	}
	*/

	//IsBloodShed.Set(					false);
	//IsBloodShed							= rhs.IsBloodShed;

   /*
	if (cNetwork::I_Am_Server()) {
		IsBloodShed.Set(false);
   }
	*/