// RAVEN BEGIN
// ddynerman: note that this file is no longer merged with Doom3 updates
//
// MERGE_DATE 09/30/2004

#ifndef __MULTIPLAYERGAME_H__
#define	__MULTIPLAYERGAME_H__

/*
===============================================================================

	Quake IV multiplayer

===============================================================================
*/

#include "mp/Buying.h"
class idPlayer;
class rvCTF_AssaultPoint;
class rvItemCTFFlag;

// jmarshall - the engine file system interface only exposes map decls by
// index; this resolves a map path the way the old GetMapDecl( name ) did.
const idDict *MultiplayerResolveMapDecl( const char *mapPath );
typedef enum {
	GAME_SP,
	GAME_DM,
	GAME_TOURNEY,
	GAME_TDM,
	// bdube: added ctf
	GAME_CTF,
	// ddynerman: new gametypes
	GAME_1F_CTF,
	GAME_ARENA_CTF,
	GAME_ARENA_1F_CTF,	// is not used, but leaving it in the list so I don't offset GAME_DEADZONE

// RITUAL BEGIN
// squirrel: added DeadZone multiplayer mode
	GAME_DEADZONE,
	NUM_GAME_TYPES,
// RITUAL END
} gameType_t;


// ddynerman: teams
typedef enum {
	TEAM_NONE = -1,
	TEAM_MARINE,
	TEAM_STROGG,
	TEAM_MAX,
} team_t;

// shouchard:  server admin command types
typedef enum {
	SERVER_ADMIN_KICK,
	SERVER_ADMIN_BAN,
	SERVER_ADMIN_REMOVE_BAN,
	SERVER_ADMIN_FORCE_SWITCH,
} serverAdmin_t;

// shouchard:  vote struct for packing up interface values to be handled later
//             note that we have two mechanisms for dealing with vote data that
//             should be consolidated:  this one that handles the interface and
//             multi-field votes and the one that handles console commands and
//             single line votes.  
struct voteStruct_t {
	int				m_fieldFlags;	// flags for which fields are valid
	int				m_kick;			// id of the player
	idStr			m_map;			// name of the map
	int				m_gameType;		// game type enum
	int				m_timeLimit;
	int				m_fragLimit;
	int				m_tourneyLimit;
	int				m_captureLimit;
	int				m_buying;
	int				m_teamBalance;
	int				m_controlTime;
	// restart is a flag only
	// nextmap is a flag only but we don't technically support it (but doom had it so it's here)
};

typedef enum {
	VOTEFLAG_RESTART		= 0x0001,
	VOTEFLAG_BUYING			= 0x0002,
	VOTEFLAG_TEAMBALANCE	= 0x0004,
	VOTEFLAG_SHUFFLE		= 0x0008,
	VOTEFLAG_KICK			= 0x0010,
	VOTEFLAG_MAP			= 0x0020,
	VOTEFLAG_GAMETYPE		= 0x0040,
	VOTEFLAG_TIMELIMIT		= 0x0080,
	VOTEFLAG_TOURNEYLIMIT	= 0x0100,
	VOTEFLAG_CAPTURELIMIT	= 0x0200,
	VOTEFLAG_FRAGLIMIT		= 0x0400,
	VOTEFLAG_CONTROLTIME	= 0x0800,
} voteFlag_t;

#define NUM_VOTES			11
#define MAX_PRINT_LEN 128

// more compact than a chat line
typedef enum {
	MSG_SUICIDE = 0,
	MSG_KILLED,
	MSG_KILLEDTEAM,
	MSG_DIED,
	MSG_VOTE,
	MSG_VOTEPASSED,
	MSG_VOTEFAILED,
	MSG_SUDDENDEATH,
	MSG_FORCEREADY,
	MSG_JOINEDSPEC,
	MSG_TIMELIMIT,
	MSG_FRAGLIMIT,
	MSG_CAPTURELIMIT,
	MSG_TELEFRAGGED,
	MSG_JOINTEAM,
	MSG_HOLYSHIT,
	MSG_COUNT
} msg_evt_t;

typedef enum {
	PLAYER_VOTE_NONE,
	PLAYER_VOTE_NO,
	PLAYER_VOTE_YES,
	PLAYER_VOTE_WAIT	// mark a player allowed to vote
} playerVote_t;

typedef enum {
	PRM_AUTO,
	PRM_SCORE,
	PRM_TEAM_SCORE,
	PRM_TEAM_SCORE_PLUS_SCORE,
	PRM_WINS
} playerRankMode_t;

enum announcerSound_t {
	// General announcements
	AS_GENERAL_ONE,
	AS_GENERAL_TWO,
	AS_GENERAL_THREE,
	AS_GENERAL_YOU_WIN,
	AS_GENERAL_YOU_LOSE,
	AS_GENERAL_FIGHT,
	AS_GENERAL_SUDDEN_DEATH,
	AS_GENERAL_VOTE_FAILED,
	AS_GENERAL_VOTE_PASSED,
	AS_GENERAL_VOTE_NOW,
	AS_GENERAL_ONE_FRAG,
	AS_GENERAL_TWO_FRAGS,
	AS_GENERAL_THREE_FRAGS,
	AS_GENERAL_ONE_MINUTE,
	AS_GENERAL_FIVE_MINUTE,
	AS_GENERAL_PREPARE_TO_FIGHT,
	AS_GENERAL_QUAD_DAMAGE,
	AS_GENERAL_REGENERATION,
	AS_GENERAL_HASTE,
	AS_GENERAL_INVISIBILITY,
	// DM announcements
	AS_DM_YOU_TIED_LEAD,
	AS_DM_YOU_HAVE_TAKEN_LEAD,
	AS_DM_YOU_LOST_LEAD,
	// Team announcements
	AS_TEAM_ENEMY_SCORES,
	AS_TEAM_YOU_SCORE,
	AS_TEAM_TEAMS_TIED,
	AS_TEAM_STROGG_LEAD,
	AS_TEAM_MARINES_LEAD,
	AS_TEAM_JOIN_MARINE,
	AS_TEAM_JOIN_STROGG,
	// CTF announcements
	AS_CTF_YOU_HAVE_FLAG,
	AS_CTF_YOUR_TEAM_HAS_FLAG,
	AS_CTF_ENEMY_HAS_FLAG,
	AS_CTF_YOUR_TEAM_DROPS_FLAG,
	AS_CTF_ENEMY_DROPS_FLAG,
	AS_CTF_YOUR_FLAG_RETURNED,
	AS_CTF_ENEMY_RETURNS_FLAG,
	// Tourney announcements
	AS_TOURNEY_ADVANCE,
	AS_TOURNEY_JOIN_ARENA_ONE,
	AS_TOURNEY_JOIN_ARENA_TWO,
	AS_TOURNEY_JOIN_ARENA_THREE,
	AS_TOURNEY_JOIN_ARENA_FOUR,
	AS_TOURNEY_JOIN_ARENA_FIVE,
	AS_TOURNEY_JOIN_ARENA_SIX,
	AS_TOURNEY_JOIN_ARENA_SEVEN,
	AS_TOURNEY_JOIN_ARENA_EIGHT,
	AS_TOURNEY_JOIN_ARENA_WAITING,
	AS_TOURNEY_DONE,
	AS_TOURNEY_START,
	AS_TOURNEY_ELIMINATED,
	AS_TOURNEY_WON,
	AS_TOURNEY_PRELIMS,
	AS_TOURNEY_QUARTER_FINALS,
	AS_TOURNEY_SEMI_FINALS,
	AS_TOURNEY_FINAL_MATCH,
	AS_GENERAL_TEAM_AMMOREGEN,
	AS_GENERAL_TEAM_DOUBLER,
	AS_NUM_SOUNDS
};

const int VOTEMAPS_WAITING_MAPLIST		= (1<<0);
const int VOTEMAPS_WAITING_SAMAPLIST	= (1<<1);
const int VOTEMAPS_WAITING_LISTMAPS		= (1<<2);

typedef struct mpPlayerState_s {
	int				ping;			// player ping
	int				fragCount;		// kills
	int				teamFragCount;	// teamplay awards
	int				deadZoneScore;  // Score in dead zone
	int				wins;
	playerVote_t	vote;			// player's vote
	bool			scoreBoardUp;	// toggle based on player scoreboard button, used to activate de-activate the scoreboard gui
	bool			ingame;
} mpPlayerState_t;

const int MAX_INSTANCES = 8;

const int NUM_CHAT_NOTIFY	= 5;
const int CHAT_FADE_TIME	= 400;
const int FRAGLIMIT_DELAY	= 2000;
const int CAPTURELIMIT_DELAY = 750;

const int MP_PLAYER_MINFRAGS = -100;
const int MP_PLAYER_MAXFRAGS = 999;
const int MP_PLAYER_MAXWINS	= 100;
const int MP_PLAYER_MAXPING	= 999;

const int MP_PLAYER_MAXKILLS = 999;
const int MP_PLAYER_MAXDEATHS = 999;

const int MAX_AP = 5;

const int CHAT_HISTORY_SIZE = 2048;
const int RCON_HISTORY_SIZE = 4096;

const int KILL_NOTIFICATION_LEN = 256;
//RAVEN BEGIN
//asalmon: update stats for Xenon
#ifdef _XENON
const int XENON_STAT_UPDATE_INTERVAL = 1000;
#endif

const int ASYNC_PLAYER_FRAG_BITS = -idMath::BitsForInteger( MP_PLAYER_MAXFRAGS - MP_PLAYER_MINFRAGS );	// player can have negative frags
const int ASYNC_PLAYER_WINS_BITS = idMath::BitsForInteger( MP_PLAYER_MAXWINS );
const int ASYNC_PLAYER_PING_BITS = idMath::BitsForInteger( MP_PLAYER_MAXPING );
const int ASYNC_PLAYER_INSTANCE_BITS = idMath::BitsForInteger( MAX_INSTANCES );
const int ASYNC_PLAYER_DEATH_BITS = idMath::BitsForInteger( MP_PLAYER_MAXDEATHS );
const int ASYNC_PLAYER_KILL_BITS = idMath::BitsForInteger( MP_PLAYER_MAXKILLS );
//RAVEN END
//RITUAL BEGIN
const int MAX_TEAM_POWERUPS = 5;
//RITUAL END
// ddynerman: game state
#include "mp/GameState.h"

typedef struct mpChatLine_s {
	idStr			line;
	short			fade;			// starts high and decreases, line is removed once reached 0
} mpChatLine_t;

typedef struct mpBanInfo_s {
	idStr			name;
	char			guid[ CLIENT_GUID_LENGTH ];
//	unsigned char	ip[ 15 ];
} mpBanInfo_t;

class idPhysics_Player;

class idMultiplayerGame {

	// rvGameState manages our state
	friend class rvGameState;

public:

					idMultiplayerGame();

	void			Shutdown( void );

	// resets everything and prepares for a match
	void			Reset( void );

// RAVEN BEGIN
// mwhitlock: Dynamic memory consolidation
	// Made this public so that level heap can be emptied.
	void			Clear( void );
// RAVEN END

	// setup local data for a new player
	void			SpawnPlayer( int clientNum );

	// Run the MP Game
	void			Run( void );

	// Run the local client
	void			ClientRun( void );
	void			ClientEndFrame( void );

	// Run common code (client & server)
	void			CommonRun( void );

	// draws mp hud, scoredboard, etc.. 
	bool			Draw( int clientNum );
	

	// updates a player vote
	void			PlayerVote( int clientNum, playerVote_t vote );

	// updates frag counts and potentially ends the match in sudden death
	void			PlayerDeath( idPlayer *dead, idPlayer *killer, int methodOfDeath );

	void			AddChatLine( const char *fmt, ... ) id_attribute((format(printf,2,3)));
	void			PrintChatLine( const char *message, const bool teamChat );

// RITUAL BEGIN
// squirrel: Mode-agnostic buymenus
	void			OnBuyModeTeamVictory( int winningTeam );
// squirrel: added DeadZone multiplayer mode
	void			OnDeadZoneTeamVictory( int winningTeam );
// RITUAL END

	void			UpdateMainGui( void );
// RAVEN BEGIN
// bdube: global pickup sounds (powerups, etc)
	// Global item acquire sounds 
	void			PlayGlobalItemAcquireSound ( int entityDefIndex );

	bool			CanTalk( idPlayer *from, idPlayer *to, bool echo );
	void			ReceiveAndForwardVoiceData( int clientNum, const idBitMsg &inMsg, int messageType );

#ifdef _USE_VOICECHAT
// jscott: game side voice comms
	void			XmitVoiceData( void );
	void			ReceiveAndPlayVoiceData( const idBitMsg &inMsg );
#endif

// jshepard: selects a map at random that will run with the current game type
	bool			PickMap( idStr gameType, bool checkOnly = false );
	void			ClearVote( int clientNum = -1 );
	void			ResetRconGuiStatus( void );

// RAVEN END

	idUserInterface*	StartMenu( void );
	void			ShowInitialJoinMenu( void );

	const char*		HandleGuiCommands( const char *menuCommand );

	void			WriteToSnapshot( idBitMsgDelta &msg ) const;
	void			ReadFromSnapshot( const idBitMsgDelta &msg );

	void			ShuffleTeams( void );
	void			SetGameType( void );
	void			SetMatchStartedTime( int time ) { matchStartedTime = time; }

	rvGameState*	GetGameState( void );


	void			PrintMessageEvent( int to, msg_evt_t evt, int parm1 = -1, int parm2 = -1 );
	void			PrintMessage( int to, const char* message );

	void			DisconnectClient( int clientNum );
	static void		ForceReady_f( const idCmdArgs &args );
	static void		DropWeapon_f( const idCmdArgs &args );
	static void		MessageMode_f( const idCmdArgs &args );
	static void		VoiceChat_f( const idCmdArgs &args );
	static void		VoiceChatTeam_f( const idCmdArgs &args );


// RAVEN BEGIN
// shouchard:  added console commands to mute/unmute voice chat
	static void		VoiceMute_f( const idCmdArgs &args );
	static void		VoiceUnmute_f( const idCmdArgs &args );

// jshepard: command wrappers
	static void		ForceTeamChange_f( const idCmdArgs& args );
	static void		RemoveClientFromBanList_f( const idCmdArgs& args );
	
// autobalance helper for the guis
	static void		CheckTeamBalance_f( const idCmdArgs &args );

// activates the admin console when a rcon password challenge returns.
	void			ProcessRconReturn( bool success );
// RAVEN END

	typedef enum {
		VOTE_RESTART = 0,
		VOTE_TIMELIMIT,
		VOTE_FRAGLIMIT,
		VOTE_GAMETYPE,
		VOTE_KICK,
		VOTE_MAP,
		VOTE_BUYING,
		VOTE_NEXTMAP,
// RAVEN BEGIN
// shouchard:  added capturelimit, round limit, and autobalance to vote flags
		VOTE_CAPTURELIMIT,
		VOTE_ROUNDLIMIT,
		VOTE_AUTOBALANCE,
		VOTE_MULTIFIELD,	// all the "packed" vote functions
// RAVEN END
		VOTE_CONTROLTIME,
		VOTE_COUNT,
		VOTE_NONE
	} vote_flags_t;

	typedef enum {
		VOTE_UPDATE,
		VOTE_FAILED,
		VOTE_PASSED,	// passed, but no reset yet
		VOTE_ABORTED,
		VOTE_RESET		// tell clients to reset vote state
	} vote_result_t;

// RAVEN BEGIN
// shouchard:  added enum to remove magic numbers
	typedef enum {
		VOTE_GAMETYPE_DM = 0,
		VOTE_GAMETYPE_TOURNEY,
		VOTE_GAMETYPE_TDM,
		VOTE_GAMETYPE_CTF,
		VOTE_GAMETYPE_ARENA_CTF,
//RITUAL BEGIN
//
		VOTE_GAMETYPE_DEADZONE,
//RITUAL END
		VOTE_GAMETYPE_COUNT
	} vote_gametype_t;
// RAVEN END

	void			SendMapList( int clientNum );
	void			ReadMapList( const idBitMsg &msg );

	static void		Vote_f( const idCmdArgs &args );
	static void		CallVote_f( const idCmdArgs &args );
	void			ClientCallVote( vote_flags_t voteIndex, const char *voteValue );
	void			ServerCallVote( int clientNum, const idBitMsg &msg );
	void			ClientStartVote( int clientNum, const char *voteString );
	void			ServerStartVote( int clientNum, vote_flags_t voteIndex, const char *voteValue );
// RAVEN BEGIN
// shouchard:  multiline vote support
	void			ClientUpdateVote( vote_result_t result, int yesCount, int noCount, const voteStruct_t &voteData );
// RAVEN END
	void			CastVote( int clientNum, bool vote );
	void			ExecuteVote( void );
// RAVEN BEGIN
// shouchard:  multiline vote handlers
	void			ClientCallPackedVote( const voteStruct_t &voteData );
	void			ServerCallPackedVote( int clientNum, const idBitMsg &msg );
	void			ClientStartPackedVote( int clientNum, const voteStruct_t &voteData );
	void			ServerStartPackedVote( int clientNum, const voteStruct_t &voteData );
	void			ExecutePackedVote( void );
	const char *	LocalizeGametype( void );
// RAVEN END

	void			WantKilled( int clientNum );
	int				NumActualClients( bool countSpectators, int *teamcount = NULL );
	void			DropWeapon( int clientNum );
	void			MapRestart( void );
	void			JoinTeam( const char* team );
	// called by idPlayer whenever it detects a team change (init or switch)
	void			SwitchToTeam( int clientNum, int oldteam, int newteam );
	
	bool			IsPureReady( void ) const;
	void			ProcessChatMessage( int clientNum, bool team, const char *name, const char *text, const char *sound );
	void			ProcessVoiceChat( int clientNum, bool team, int index );
// RAVEN BEGIN
// shouchard:  added commands to mute/unmute voice chat
	void			ClientVoiceMute( int clientNum, bool mute );
	int				GetClientNumFromPlayerName( const char *playerName );
	void			ServerHandleVoiceMuting( int clientSrc, int clientDest, bool mute );
// shouchard:  fixing a bug in multiplayer where round timer sounds (5 minute
//             warning, etc.) don't go away at the end of the round.
	void			ClearAnnouncerSounds( void );
// shouchard:  server admin stuff
	typedef struct 
	{
		bool		restartMap;
		idStr		mapName;
		int			gameType;
		int			captureLimit;
		int			fragLimit;
		int			tourneyLimit;
		int			timeLimit;
		int			minPlayers;
		int			controlTime;
		bool		buying;
		bool		autoBalance;
		bool		shuffleTeams;
	} serverAdminData_t;

	void			HandleServerAdminBanPlayer( int clientNum );
	void			HandleServerAdminRemoveBan( const char * info );
	void			HandleServerAdminKickPlayer( int clientNum );
	void			HandleServerAdminForceTeamSwitch( int clientNum );
	bool			HandleServerAdminCommands( serverAdminData_t &data );
// RAVEN END

// RITUAL BEGIN
	typedef struct mpTeamPowerups_s {
		int powerup;
		int time;
		bool update;
		int endTime;
	} mpTeamPowerups_t;

	mpTeamPowerups_t teamPowerups[TEAM_MAX][MAX_TEAM_POWERUPS];

	void			AddTeamPowerup(int powerup, int time, int team);
	void			UpdateTeamPowerups();
	void			SetUpdateForTeamPowerups(int team);
// RITUAL END

	void			Precache( void );
	
	// throttle UI switch rates
	void			ThrottleUserInfo( void );
	void			ToggleSpectate( void );
	void			ToggleReady( void );
	void			ToggleTeam( void );

	void			ClearFrags( int clientNum );

	void			EnterGame( int clientNum );
	bool			CanPlay( idPlayer *p );
	bool			IsInGame( int clientNum );
	bool			WantRespawn( idPlayer *p );

	void			ServerWriteInitialReliableMessages( const idMessageSender &sender, int clientNum );
	void			ClientReadStartState( const idBitMsg &msg );

	void			ServerClientConnect( int clientNum );

	void			PlayerStats( int clientNum, char *data, const int len );

	void			AddTeamScore ( int team, int amount );
	void			AddPlayerScore( idPlayer* player, int amount );
	void			AddPlayerTeamScore( idPlayer* player, int amount );
	void			AddPlayerWin( idPlayer* player, int amount );
	void			SetPlayerTeamScore( idPlayer* player, int value );
	void			SetPlayerDeadZoneScore( idPlayer* player, float value );
	void			SetPlayerScore( idPlayer* player, int value );
	void			SetPlayerWin( idPlayer* player, int value );
	void			SetHudOverlay( idUserInterface* overlay, int duration );

	void			ClearMap ( void );

	void			EnableDamage( bool enable = true );

	idPlayer*		GetRankedPlayer( int i );
	int				GetRankedPlayerScore( int i );
	int				GetNumRankedPlayers( void );
	
	idPlayer*		GetUnrankedPlayer( int i );
	int				GetNumUnrankedPlayers( void );

	int				GetScore( int i );
	int				GetScore( idPlayer* player );

	int				GetTeamScore( int i );
	int				GetTeamScore( idPlayer* player );

	int				GetWins( int i );
	int				GetWins( idPlayer* player );

	// asalmon: Get the score for a team.
	int				GetScoreForTeam( int i );
	int				GetTeamsTotalFrags( int i );
	int				GetTeamsTotalScore( int i );
	idUserInterface *GetMainGUI() {return mainGui;}

	float			GetPlayerDeadZoneScore(idPlayer* player);

	int				TeamLeader( void );

	int				GetPlayerTime( idPlayer* player );

	const char*		GetLongGametypeName( const char* gametype );
	const char*		VoteGameTypeToString( int gameTypeInt );
	int				GameTypeToVote( const char *gameType );

	void			ReceiveRemoteConsoleOutput( const char* output );

	void			ClientSetInstance( const idBitMsg& msg );
	void			ServerSetInstance( int instance );

	void			AddPrivatePlayer( int clientId );
	void			RemovePrivatePlayer( int clientId );

//RAVEN BEGIN
//asalmon: Xenon scoreboard update
#ifdef _XENON
	void			UpdateXenonScoreboard( idUserInterface *scoreBoard );
	int				lastScoreUpdate;
// mekberg: for selecting local player
	void			SelectLocalPlayer( idUserInterface *scoreBoard );
#endif
//RAVEN END
	int				VerifyTeamSwitch( int wantTeam, idPlayer *player );

	void			RemoveAnnouncerSound( int type );
	void			RemoveAnnouncerSoundRange( int startType, int endType );
	void			ScheduleAnnouncerSound ( announcerSound_t sound, float time, int instance = -1, bool allowOverride = false );
	void			ScheduleTimeAnnouncements( void );
// RAVEN END

	void			SendDeathMessage( idPlayer *attacker, idPlayer *victim, int methodOfDeath, bool quadKill );
	void			ReceiveDeathMessage( idPlayer *attacker, int attackerScore, idPlayer *victim, int victimScore, int methodOfDeath, bool quadKill );

	rvCTF_AssaultPoint*		NextAP( int team );
	int						OpposingTeam( int team );

	idList<idEntityPtr<rvCTF_AssaultPoint> > assaultPoints;

	// Buying Manager - authority for buying system game balance constants (awards,
	// costs, etc.)
	riBuyingManager	mpBuyingManager;

	idUserInterface* statSummary;			// stat summary
	rvTourneyGUI	tourneyGUI;

	void			ShowStatSummary( void );
	bool			CanCapture( int team );
	void			FlagCaptured( idPlayer *player );
	
	void			UpdatePlayerRanks( playerRankMode_t rankMode = PRM_AUTO );
	void			UpdateTeamRanks( void );
	void			UpdateHud( idUserInterface* _mphud );
	idPlayer *		FragLimitHit( void );
	idPlayer *		FragLeader( void );
	bool			TimeLimitHit( void );
	int				GetCurrentMenu( void ) { return currentMenu; }

	void			SetFlagEntity( idEntity* ent, int team );
	idEntity*		GetFlagEntity( int team );

	void			WriteNetworkInfo( idFile *file, int clientNum );
	void			ReadNetworkInfo( idFile* file, int clientNum );

	void			SetShaderParms( renderView_t *view );
	
// RITUAL BEGIN
// squirrel: added DeadZone multiplayer mode
	int				NumberOfPlayersOnTeam( int team );
	int				NumberOfAlivePlayersOnTeam( int team );
	void			ReportZoneControllingPlayer( idPlayer* player );
	void			ReportZoneController(int team, int pCount, int situation, idEntity* zoneTrigger = 0);
	bool			IsValidTeam(int team);
	void			ControlZoneStateChanged( int team );

	void			ListMaps( void );

	int				powerupCount;
	int				prevAnnouncerSnd;
	int				defaultWinner;
	int				deadZonePowerupCount;
	dzState_t		dzState[ TEAM_MAX ];
	float			marineScoreBarPulseAmount;
	float			stroggScoreBarPulseAmount;
// RITUAL END

// RITUAL BEGIN
// squirrel: Mode-agnostic buymenus
	bool			isBuyingAllowedRightNow;

	void			OpenLocalBuyMenu( void );
	void			RedrawLocalBuyMenu( void );
	void			GiveCashToTeam( int team, float cashAmount );
	bool			IsBuyingAllowedInTheCurrentGameMode( void );
	bool			IsBuyingAllowedRightNow( void );
// RITUAL END
	static const char*	teamNames[ TEAM_MAX ];

private:
	static const char	*MPGuis[];
	static const char	*ThrottleVars[];
	static const char	*ThrottleVarsInEnglish[];
	static const int	ThrottleDelay[];

	char			killNotificationMsg[ KILL_NOTIFICATION_LEN ];

	int				pingUpdateTime;			// time to update ping

	mpPlayerState_t	playerState[ MAX_CLIENTS ];

	// game state
	rvGameState*	gameState;

	// vote vars
	vote_flags_t	vote;					// active vote or VOTE_NONE
	int				voteTimeOut;			// when the current vote expires
	int				voteExecTime;			// delay between vote passed msg and execute
	int				yesVotes;				// counter for yes votes
	int				noVotes;				// and for no votes
	idStr			voteValue;				// the data voted upon ( server )
	idStr			voteString;				// the vote string ( client )
	bool			voted;					// hide vote box ( client )
	int				kickVoteMap[ MAX_CLIENTS ];
// RAVEN BEGIN
// shouchard:  names for kickVoteMap
	idStr			kickVoteMapNames[ MAX_CLIENTS ];
	voteStruct_t	currentVoteData;		// used for multi-field votes
// RAVEN END

	idStr			localisedGametype;

	idList<int>		voteMapDecls;
	int				voteMapsWaiting;

	// time related
	int				matchStartedTime;		// time current match started

	// guis
// RITUAL BEGIN
// squirrel: added DeadZone multiplayer mode
	//int				sqRoundNumber;			// round number in DeadZone; match expires when this equals "sq_numRoundsPerMatch" (cvar)
// squirrel: Mode-agnostic buymenus
	idUserInterface *buyMenu;				// buy menu
// RITUAL END
	idUserInterface *scoreBoard;			// scoreboard
	idUserInterface *mainGui;				// ready / nick / votes etc.
	idListGUI		*mapList;
	idUserInterface *msgmodeGui;			// message mode
	int				currentMenu;			// 0 - none, 1 - mainGui, 2 - msgmodeGui
	int				nextMenu;				// if 0, will do mainGui
	bool			bCurrentMenuMsg;		// send menu state updates to server


	enum {
		MPLIGHT_CTF_MARINE,
		MPLIGHT_CTF_STROGG,
		MPLIGHT_QUAD,
		MPLIGHT_HASTE,
		MPLIGHT_REGEN,
		MPLIGHT_MAX
	};

	int				lightHandles[ MPLIGHT_MAX ];
	renderLight_t	lights[ MPLIGHT_MAX ];

	// chat buffer
	idStr			chatHistory;

	// rcon buffer
	idStr			rconHistory;

//RAVEN BEGIN
//asalmon: Need to refresh stats periodically if the player is looking at stats
	int currentStatClient;
	int currentStatTeam;
//RAVEN END

public:
	// current player rankings
	idList<rvPair<idPlayer*, int> > 	rankedPlayers;
	idList<idPlayer*>		unrankedPlayers;

	rvPair<int, int>		rankedTeams[ TEAM_MAX ];

private:

	int				lastVOAnnounce;

	int				lastReadyToggleTime;
	bool			pureReady;				// defaults to false, set to true once server game is running with pure checksums
	bool			currentSoundOverride;
	int				switchThrottle[ 3 ];
	int				voiceChatThrottle;

	void			SetupBuyMenuItems();

	idList<int>		privateClientIds;
	int				privatePlayers;

	// player who's rank info we're displaying
	idEntityPtr<idPlayer>		rankTextPlayer;

	idEntityPtr<idEntity>		flagEntities[ TEAM_MAX ];	
	idEntityPtr<idPlayer>		flagCarriers[ TEAM_MAX ];

	// updates the passed gui with current score information
	void			UpdateRankColor( idUserInterface *gui, const char *mask, int i, const idVec3 &vec );	

	// bdube: test scoreboard
	void			UpdateTestScoreboard( idUserInterface *scoreBoard );
	
	// ddynerman: gametype specific scoreboard
	void			UpdateScoreboard( idUserInterface *scoreBoard );

	void			UpdateDMScoreboard( idUserInterface *scoreBoard );
	void			UpdateTeamScoreboard( idUserInterface *scoreBoard );
	void			UpdateSummaryBoard( idUserInterface *scoreBoard );

	int				GetPlayerRank( idPlayer* player, bool& isTied );
	char*			GetPlayerRankText( idPlayer* player );
	char*			GetPlayerRankText( int rank, bool tied, int score );

	const char*		BuildSummaryListString( idPlayer* player, int rankedScore );

	void			UpdatePrivatePlayerCount( void );

	typedef struct announcerSoundNode_s {
		announcerSound_t					soundShader;
		float								time;
		idLinkList<announcerSoundNode_s>	announcerSoundNode;
		int									instance;
		bool								allowOverride;
	} announcerSoundNode_t;

	idLinkList<announcerSoundNode_t>	announcerSoundQueue;
	announcerSound_t					lastAnnouncerSound;

	static const char* announcerSoundDefs[ AS_NUM_SOUNDS ];

	float			announcerPlayTime;

	void			PlayAnnouncerSounds ( void );

	int				teamScore[ TEAM_MAX ];
	int				teamDeadZoneScore[ TEAM_MAX];
	void			ClearTeamScores ( void );

	void			UpdateLeader( idPlayer* oldLeader );

	void			ClearGuis( void );
	void			DrawScoreBoard( idPlayer *player );
	void			CheckVote( void );
	bool			AllPlayersReady( idStr* reason = NULL );
	
	const char *	GameTime( void );

	bool			EnoughClientsToPlay( void );
	void			DrawStatSummary( void );
	// go through the clients, and see if they want to be respawned, and if the game allows it
	// called during normal gameplay for death -> respawn cycles
	// and for a spectator who want back in the game (see param)
	void			CheckRespawns( idPlayer *spectator = NULL );

	void			FreeLight ( int lightID );
	void			UpdateLight ( int lightID, idPlayer *player );
	void			CheckSpecialLights( void );
	void			ForceReady();
	// when clients disconnect or join spectate during game, check if we need to end the game
	void			CheckAbortGame( void );
	void			MessageMode( const idCmdArgs &args );
	void			DisableMenu( void );
	void			SetMapShot( void );
	// scores in TDM
	void			VoiceChat( const idCmdArgs &args, bool team );

// RAVEN BEGIN
// mekberg: added
	void			UpdateMPSettingsModel( idUserInterface* currentGui );
// RAVEN END

	void			WriteStartState( int clientNum, idBitMsg &msg, bool withLocalClient );

	bool			RequestVoteMaps( int flags );

	void			SetMapList( const char *listName, const char *mapName, int gameTypeInt );
	void			SetVoteMapList( void );
	void			SetSAMapList( void );
};

ID_INLINE bool idMultiplayerGame::IsPureReady( void ) const {
	return pureReady;
}

ID_INLINE void idMultiplayerGame::ClearFrags( int clientNum ) {
	playerState[ clientNum ].fragCount = 0;
}

ID_INLINE bool idMultiplayerGame::IsInGame( int clientNum ) {
	return playerState[ clientNum ].ingame;
}

ID_INLINE int idMultiplayerGame::OpposingTeam( int team ) {
	return (team == TEAM_STROGG ? TEAM_MARINE : TEAM_STROGG);
}

ID_INLINE idPlayer* idMultiplayerGame::GetRankedPlayer( int i ) {
	if( i >= 0 && i < rankedPlayers.Num() ) {
		return rankedPlayers[ i ].First();
	} else {
		return NULL;
	}
}

ID_INLINE int idMultiplayerGame::GetRankedPlayerScore( int i ) {
	if( i >= 0 && i < rankedPlayers.Num() ) {
		return rankedPlayers[ i ].Second();
	} else {
		return 0;
	}
}

ID_INLINE int idMultiplayerGame::GetNumUnrankedPlayers( void ) {
	return unrankedPlayers.Num();
}

ID_INLINE idPlayer* idMultiplayerGame::GetUnrankedPlayer( int i ) {
	if( i >= 0 && i < unrankedPlayers.Num() ) {
		return unrankedPlayers[ i ];
	} else {
		return NULL;
	}
}

ID_INLINE int idMultiplayerGame::GetNumRankedPlayers( void ) {
	return rankedPlayers.Num();
}

ID_INLINE int idMultiplayerGame::GetTeamScore( int i ) {
	return playerState[ i ].teamFragCount;
}

ID_INLINE int idMultiplayerGame::GetScore( int i ) {
	return playerState[ i ].fragCount;
}

ID_INLINE int idMultiplayerGame::GetWins( int i ) {
	return playerState[ i ].wins;
}

ID_INLINE void idMultiplayerGame::ResetRconGuiStatus( void ) {
	if( mainGui) {
		mainGui->SetStateInt( "password_valid", 0 );
	}
}

// asalmon: needed access team scores for rich presence
ID_INLINE int idMultiplayerGame::GetScoreForTeam( int i ) {
	if( i < 0 || i > TEAM_MAX ) {
		return 0;
	}
	return teamScore[ i ];
}

ID_INLINE int idMultiplayerGame::TeamLeader( void ) {
	if( teamScore[ TEAM_MARINE ] == teamScore[ TEAM_STROGG ] ) {
		return -1;
	} else {
		return ( teamScore[ TEAM_MARINE ] > teamScore[ TEAM_STROGG ] ? TEAM_MARINE : TEAM_STROGG );
	}
}

int ComparePlayersByScore( const void* left, const void* right );
int CompareTeamsByScore( const void* left, const void* right );

#endif	/* !__MULTIPLAYERGAME_H__ */

// RAVEN END
