//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Teamplay game rules that manage a round based structure for you
//
//=============================================================================

#ifndef TEAMPLAYROUNDBASED_GAMERULES_H
#define TEAMPLAYROUNDBASED_GAMERULES_H
#ifdef _WIN32
#pragma once
#endif

#include "teamplay_gamerules.h"
#include "teamplay_round_timer.h"
#include "GameEventListener.h"

#ifdef GAME_DLL
#include "team_control_point.h"
#include "viewport_panel_names.h"
	extern ConVar mp_respawnwavetime;
	extern ConVar mp_showroundtransitions;
	extern ConVar mp_enableroundwaittime;
	extern ConVar mp_showcleanedupents;
	extern ConVar mp_bonusroundtime;
	extern ConVar mp_restartround;
	extern ConVar mp_winlimit;
	extern ConVar mp_maxrounds;
	extern ConVar mp_stalemate_timelimit;
	extern ConVar mp_stalemate_enable;
#else
	#define CTeamplayRoundBasedRules C_TeamplayRoundBasedRules
	#define CTeamplayRoundBasedRulesProxy C_TeamplayRoundBasedRulesProxy
#endif

extern ConVar	tf_arena_use_queue;
extern ConVar	mp_stalemate_meleeonly;
extern ConVar	mp_forceautoteam;

class CTeamplayRoundBasedRules;

//-----------------------------------------------------------------------------
// Round states
//-----------------------------------------------------------------------------
enum gamerules_roundstate_t
{
	// initialize the game, create teams
	GR_STATE_INIT = 0,

	//Before players have joined the game. Periodically checks to see if enough players are ready
	//to start a game. Also reverts to this when there are no active players
	GR_STATE_PREGAME,

	//The game is about to start, wait a bit and spawn everyone
	GR_STATE_STARTGAME,

	//All players are respawned, frozen in place
	GR_STATE_PREROUND,

	//Round is on, playing normally
	GR_STATE_RND_RUNNING,

	//Someone has won the round
	GR_STATE_TEAM_WIN,

	//Noone has won, manually restart the game, reset scores
	GR_STATE_RESTART,

	//Noone has won, restart the game
	GR_STATE_STALEMATE,

	//Game is over, showing the scoreboard etc
	GR_STATE_GAME_OVER,

	//Game is in a bonus state, transitioned to after a round ends
	GR_STATE_BONUS,

	//Game is awaiting the next wave/round of a multi round experience
	GR_STATE_BETWEEN_RNDS,

	GR_NUM_ROUND_STATES
};

enum {
	WINREASON_NONE =0,
	WINREASON_ALL_POINTS_CAPTURED,
	WINREASON_OPPONENTS_DEAD,
	WINREASON_FLAG_CAPTURE_LIMIT,
	WINREASON_DEFEND_UNTIL_TIME_LIMIT,
	WINREASON_STALEMATE,
	WINREASON_TIMELIMIT,
	WINREASON_WINLIMIT,
	WINREASON_WINDIFFLIMIT,
#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
	WINREASON_RD_REACTOR_CAPTURED,
	WINREASON_RD_CORES_COLLECTED,
	WINREASON_RD_REACTOR_RETURNED,
	WINREASON_PD_POINTS,
	WINREASON_SCORED,
	WINREASON_STOPWATCH_WATCHING_ROUNDS,
	WINREASON_STOPWATCH_WATCHING_FINAL_ROUND,
	WINREASON_STOPWATCH_PLAYING_ROUNDS,
#endif
};

enum stalemate_reasons_t
{
	STALEMATE_JOIN_MID,
	STALEMATE_TIMER,
	STALEMATE_SERVER_TIMELIMIT,

	NUM_STALEMATE_REASONS,
};


#if defined(TF_CLIENT_DLL) || defined(TF_DLL)

#ifdef STAGING_ONLY
extern ConVar tf_test_match_summary;
#endif

#endif

//-----------------------------------------------------------------------------
// Purpose: Per-state data
//-----------------------------------------------------------------------------
class CGameRulesRoundStateInfo
{
public:
	gamerules_roundstate_t	m_iRoundState;
	const char				*m_pStateName;

	void (CTeamplayRoundBasedRules::*pfnEnterState)();	// Init and deinit the state.
	void (CTeamplayRoundBasedRules::*pfnLeaveState)();
	void (CTeamplayRoundBasedRules::*pfnThink)();	// Do a PreThink() in this state.
};

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
class CTeamplayRoundBasedRulesProxy : public CGameRulesProxy
{
public:
	DECLARE_CLASS( CTeamplayRoundBasedRulesProxy, CGameRulesProxy );
	DECLARE_NETWORKCLASS();

#ifdef GAME_DLL
	DECLARE_DATADESC();
	void	InputSetStalemateOnTimelimit( inputdata_t &inputdata );
#endif

	//----------------------------------------------------------------------------------
	// Client specific
#ifdef CLIENT_DLL
	void			OnPreDataChanged( DataUpdateType_t updateType );
	void			OnDataChanged( DataUpdateType_t updateType );
#endif // CLIENT_DLL
};

//-----------------------------------------------------------------------------
// Purpose: Teamplay game rules that manage a round based structure for you
//-----------------------------------------------------------------------------
class CTeamplayRoundBasedRules : public CTeamplayRules, public CGameEventListener
{
	DECLARE_CLASS( CTeamplayRoundBasedRules, CTeamplayRules );
public:
	CTeamplayRoundBasedRules();

#ifdef CLIENT_DLL
	DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars.

	void SetRoundState( int iRoundState );
#else
	DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars.
#endif

	float GetLastRoundStateChangeTime( void ) const { return m_flLastRoundStateChangeTime; }
	float m_flLastRoundStateChangeTime;

	// Data accessors
	inline gamerules_roundstate_t State_Get( void ) { return m_iRoundState; }
	bool	IsInWaitingForPlayers( void ) { return m_bInWaitingForPlayers; }
	virtual bool InRoundRestart( void ) { return State_Get() == GR_STATE_PREROUND; }
	bool	InStalemate( void ) { return State_Get() == GR_STATE_STALEMATE; }
	bool	RoundHasBeenWon( void ) { return State_Get() == GR_STATE_TEAM_WIN; }

	virtual float GetNextRespawnWave( int iTeam, CBasePlayer *pPlayer );
	virtual bool HasPassedMinRespawnTime( CBasePlayer *pPlayer );
	virtual void	LevelInitPostEntity( void );
	virtual float	GetRespawnTimeScalar( int iTeam );
	virtual float	GetRespawnWaveMaxLength( int iTeam, bool bScaleWithNumPlayers = true );
	virtual bool	ShouldRespawnQuickly( CBasePlayer *pPlayer ) { return false; }
	float	GetMinTimeWhenPlayerMaySpawn( CBasePlayer *pPlayer );

	// Return false if players aren't allowed to cap points at this time (i.e. in WaitingForPlayers)
	virtual bool PointsMayBeCaptured( void ) { return ((State_Get() == GR_STATE_RND_RUNNING || State_Get() == GR_STATE_STALEMATE) && !IsInWaitingForPlayers()); }
	virtual void SetLastCapPointChanged( int iIndex ) { m_iLastCapPointChanged = iIndex; }
	int			 GetLastCapPointChanged( void ) { return m_iLastCapPointChanged; }

	virtual int GetWinningTeam( void )
	{
//tagES
#if defined( STAGING_ONLY ) && ( defined(TF_CLIENT_DLL) || defined(TF_DLL) ) 
		return ( tf_test_match_summary.GetBool() ? TF_TEAM_BLUE : m_iWinningTeam.Get() );
#endif		
		return m_iWinningTeam; 
	}
	int GetWinReason() { return m_iWinReason; }

	bool InOvertime( void ){ return m_bInOvertime; }
	void SetOvertime( bool bOvertime );

	bool InSetup( void ){ return m_bInSetup; }

#ifdef GAME_DLL
	virtual void BalanceTeams( bool bRequireSwitcheesToBeDead );
#endif // GAME_DLL

	bool		SwitchedTeamsThisRound( void ) { return m_bSwitchedTeamsThisRound; }

	virtual bool ShouldBalanceTeams( void );
	bool		IsInTournamentMode( void );
	bool		IsInHighlanderMode( void );
	bool		IsInPreMatch( void ) { return (IsInTournamentMode() && IsInWaitingForPlayers()); }
	bool		IsWaitingForTeams( void ) { return m_bAwaitingReadyRestart; }
	bool		IsInStopWatch( void ) { return m_bStopWatch; }
	void		SetInStopWatch( bool bState ) { m_bStopWatch = bState; }
	virtual void	StopWatchModeThink( void ) { };

	bool IsTeamReady( int iTeamNumber )
	{
		return m_bTeamReady[iTeamNumber];
	}

	bool IsPlayerReady( int iIndex )
	{
		return m_bPlayerReady[iIndex];
	}

	virtual void HandleTeamScoreModify( int iTeam, int iScore) {  };

	float GetRoundRestartTime( void ) const { return m_flRestartRoundTime; }

	//Arena Mode
	virtual bool	IsInArenaMode( void ) const { return false; }

	//Koth Mode
	virtual bool	IsInKothMode( void ) const { return false; }

	//Training Mode
	virtual bool	IsInTraining( void ) { return false; }
	virtual bool	IsInItemTestingMode( void ) { return false; }

	void SetMultipleTrains( bool bMultipleTrains ){ m_bMultipleTrains = bMultipleTrains; }
	bool HasMultipleTrains( void ){ return m_bMultipleTrains; }

	virtual int		GetBonusRoundTime( bool bGameOver = false );
	virtual int		GetPostMatchPeriod( void );
	int				GetRoundsPlayed( void ) { return m_nRoundsPlayed; }

	float GetStateTransitionTime( void ){ return m_flStateTransitionTime; }

#ifdef CLIENT_DLL
	virtual void Update( float frametime ) OVERRIDE;
#endif

	void SetAllowBetweenRounds( bool bValue ) { m_bAllowBetweenRounds = bValue; }

public: // IGameEventListener Interface
	virtual void FireGameEvent( IGameEvent * event );

	//----------------------------------------------------------------------------------
	// Server specific
#ifdef GAME_DLL
	// Derived game rules class should override these
public:
	// Override this to prevent removal of game specific entities that need to persist
	virtual bool	RoundCleanupShouldIgnore( CBaseEntity *pEnt );
	virtual bool	ShouldCreateEntity( const char *pszClassName );

	// Called when a new round is being initialized
	virtual void	SetupOnRoundStart( void ) { return; }

	// Called when a new round is off and running
	virtual void	SetupOnRoundRunning( void ) { return; }

	// Called before a new round is started (so the previous round can end)
	virtual void	PreviousRoundEnd( void ) { return; }

	// Send the team scores down to the client
	virtual void	SendTeamScoresEvent( void ) { return; }

	// Send the end of round info displayed in the win panel
	virtual void	SendWinPanelInfo( bool bGameOver ) { return; }

	// Setup spawn points for the current round before it starts
	virtual void	SetupSpawnPointsForRound( void ) { return; }

	// Called when a round has entered stalemate mode (timer has run out)
	virtual void	SetupOnStalemateStart( void ) { return; }
	virtual void	SetupOnStalemateEnd( void ) { return; }
	virtual void SetSetup( bool bSetup );

	virtual bool	ShouldGoToBonusRound( void ) { return false; }
	virtual void	SetupOnBonusStart( void ) { return; }
	virtual void	SetupOnBonusEnd( void ) { return; }
	virtual void	BonusStateThink( void ) { return; }

	virtual void	BetweenRounds_Start( void ) { return; }
	virtual void	BetweenRounds_End( void ) { return; }
	virtual void	BetweenRounds_Think( void ) { return; }

	virtual void	PreRound_Start( void ) { return; }
	virtual void	PreRound_End( void ) { return; }

	bool PrevRoundWasWaitingForPlayers() { return m_bPrevRoundWasWaitingForPlayers; }

	virtual bool ShouldScorePerRound( void ){ return true; }

	bool CheckNextLevelCvar( bool bAllowEnd = true );

	virtual bool TimerMayExpire( void );

	virtual bool IsValveMap( void ){ return false; }

	virtual		void RestartTournament( void );

	virtual		bool TournamentModeCanEndWithTimelimit( void ){ return true; }

public:
	void State_Transition( gamerules_roundstate_t newState );

	virtual void RespawnPlayers( bool bForceRespawn, bool bTeam = false, int iTeam = TEAM_UNASSIGNED );

	void SetForceMapReset( bool reset );

	void SetRoundToPlayNext( string_t strName ){ m_iszRoundToPlayNext = strName; }
	string_t GetRoundToPlayNext( void ){ return m_iszRoundToPlayNext; }
	void AddPlayedRound( string_t strName );
	bool IsPreviouslyPlayedRound ( string_t strName );
	string_t GetLastPlayedRound( void );

	virtual void SetWinningTeam( int team, int iWinReason, bool bForceMapReset = true, bool bSwitchTeams = false, bool bDontAddScore = false, bool bFinal = false ) OVERRIDE;
	virtual void SetStalemate( int iReason, bool bForceMapReset = true, bool bSwitchTeams = false );

	virtual void SetRoundOverlayDetails( void ){ return; }

	void ShouldResetScores( bool bResetTeam, bool bResetPlayer ){ m_bResetTeamScores = bResetTeam; m_bResetPlayerScores = bResetPlayer; }
	void ShouldResetRoundsPlayed( bool bResetRoundsPlayed ){ m_bResetRoundsPlayed = bResetRoundsPlayed; }

	void SetFirstRoundPlayed( string_t strName ){ m_iszFirstRoundPlayed = strName ; }
	string_t GetFirstRoundPlayed(){ return m_iszFirstRoundPlayed; }

	void SetTeamRespawnWaveTime( int iTeam, float flValue );
	void AddTeamRespawnWaveTime( int iTeam, float flValue );
	virtual void FillOutTeamplayRoundWinEvent( IGameEvent *event ) {}	// derived classes may implement to add fields to this event

	void SetStalemateOnTimelimit( bool bStalemate ) { m_bAllowStalemateAtTimelimit = bStalemate; }

	bool IsGameUnderTimeLimit( void );

	CTeamRoundTimer *GetActiveRoundTimer( void );

	void HandleTimeLimitChange( void );

	void SetTeamReadyState( bool bState, int iTeam )
	{
		m_bTeamReady.Set( iTeam, bState );
	}

	void SetPlayerReadyState( int iIndex, bool bState )
	{
		m_bPlayerReady.Set( iIndex, bState );
	}
	void ResetPlayerAndTeamReadyState( void );

	virtual void PlayTrainCaptureAlert( CTeamControlPoint *pPoint, bool bFinalPointInMap ){ return; }

	virtual void PlaySpecialCapSounds( int iCappingTeam, CTeamControlPoint *pPoint ){ return; }

	bool PlayThrottledAlert( int iTeam, const char *sound, float fDelayBeforeNext );

	void BroadcastSound( int iTeam, const char *sound, int iAdditionalSoundFlags = 0 );

	virtual void RecalculateControlPointState( void ){ return; }

	virtual bool ShouldSkipAutoScramble( void ){ return false; }

	virtual bool ShouldWaitToStartRecording( void ){ return IsInWaitingForPlayers(); }

	bool IsGameOver( void ){ return ( CheckTimeLimit( false ) || CheckWinLimit( false ) || CheckMaxRounds( false ) || CheckNextLevelCvar( false ) ); }

	virtual bool	StopWatchShouldBeTimedWin( void ) { return m_bStopWatchShouldBeTimedWin; }

protected:
	virtual void Think( void );

	virtual void CheckChatText( CBasePlayer *pPlayer, char *pText );
	void		 CheckChatForReadySignal( CBasePlayer *pPlayer, const char *chatmsg );

	// Game beginning / end handling
	virtual void GoToIntermission( void );
	void		 SetInWaitingForPlayers( bool bWaitingForPlayers );
	void		 CheckWaitingForPlayers( void );
	virtual bool AllowWaitingForPlayers( void ) { return true; }
	void		 CheckRestartRound( void );
	bool		 CheckTimeLimit( bool bAllowEnd = true );
	int			 GetTimeLeft( void );
	virtual	bool CheckWinLimit( bool bAllowEnd = true, int nAddValueWhenChecking = 0 );
	bool		 CheckMaxRounds( bool bAllowEnd = true, int nAddValueWhenChecking = 0 );

	void		 CheckReadyRestart( void );

	virtual bool CanChangelevelBecauseOfTimeLimit( void ) { return true; }
	virtual bool CanGoToStalemate( void ) { return true; }

	// State machine handling
	void State_Enter( gamerules_roundstate_t newState );	// Initialize the new state.
	void State_Leave();										// Cleanup the previous state.
	void State_Think();										// Update the current state.
	static CGameRulesRoundStateInfo* State_LookupInfo( gamerules_roundstate_t state );	// Find the state info for the specified state.

	// State Functions
	void State_Enter_INIT( void );
	void State_Think_INIT( void );

	void State_Enter_PREGAME( void );
	void State_Think_PREGAME( void );

	void State_Enter_STARTGAME( void );
	void State_Think_STARTGAME( void );

	void State_Enter_PREROUND( void );
	void State_Leave_PREROUND( void );
	void State_Think_PREROUND( void );

	void State_Enter_RND_RUNNING( void );
	void State_Think_RND_RUNNING( void );

	void State_Enter_TEAM_WIN( void );
	void State_Think_TEAM_WIN( void );

	void State_Enter_RESTART( void );
	void State_Think_RESTART( void );

	void State_Enter_STALEMATE( void );
	void State_Think_STALEMATE( void );
	void State_Leave_STALEMATE( void );

	void State_Enter_BONUS( void );
	void State_Think_BONUS( void );
	void State_Leave_BONUS( void );

	void State_Enter_BETWEEN_RNDS( void );
	void State_Leave_BETWEEN_RNDS( void );
	void State_Think_BETWEEN_RNDS( void );

	// mp_scrambleteams_auto
	void ResetTeamsRoundWinTracking( void );

protected:
	virtual void InitTeams( void );
	virtual bool BHavePlayers( void );

	virtual void RoundRespawn( void );
	virtual void CleanUpMap( void );
	virtual void CheckRespawnWaves( void );
	void ResetScores( void );
	void ResetMapTime( void );

	void PlayStartRoundVoice( void );
	virtual void PlayWinSong( int team );
	void PlayStalemateSong( void );
	void PlaySuddenDeathSong( void );

	virtual const char* GetStalemateSong( int nTeam ) { return "Game.Stalemate"; }
	virtual const char* WinSongName( int nTeam ) { return "Game.YourTeamWon"; }
	virtual const char* LoseSongName( int nTeam ) { return "Game.YourTeamLost"; }
	
	virtual void RespawnTeam( int iTeam ) { RespawnPlayers( false, true, iTeam ); }

	void HideActiveTimer( void );
	virtual void RestoreActiveTimer( void );

	virtual void InternalHandleTeamWin( int iWinningTeam ){ return; }

	bool MapHasActiveTimer( void );
	void CreateTimeLimitTimer( void );

	virtual float GetLastMajorEventTime( void ) OVERRIDE { return m_flLastTeamWin; }

protected:
	CGameRulesRoundStateInfo	*m_pCurStateInfo;			// Per-state data 

	float						m_flWaitingForPlayersTimeEnds;
	CHandle<CTeamRoundTimer>	m_hWaitingForPlayersTimer;

	float						m_flNextPeriodicThink;
	bool						m_bChangeLevelOnRoundEnd;

	bool						m_bResetTeamScores;
	bool						m_bResetPlayerScores;
	bool						m_bResetRoundsPlayed;

	// Stalemate
	EHANDLE						m_hPreviousActiveTimer;
	CHandle<CTeamRoundTimer>	m_hStalemateTimer;
	float						m_flStalemateStartTime;

	CHandle<CTeamRoundTimer>	m_hTimeLimitTimer;

	bool						m_bForceMapReset; // should the map be reset when a team wins and the round is restarted?
	bool						m_bPrevRoundWasWaitingForPlayers;	// was the previous map reset after a waiting for players period
	bool						m_bInitialSpawn;

	string_t					m_iszRoundToPlayNext;
	CUtlVector<string_t>		m_iszPreviousRounds; // we'll store the two previous rounds so we won't play them again right away if there are other rounds that can be played first
	string_t					m_iszFirstRoundPlayed; // store the first round played after a full restart so we can pick a different one next time if we have other options

	float						m_flOriginalTeamRespawnWaveTime[ MAX_TEAMS ];

	bool						m_bAllowStalemateAtTimelimit;
	bool						m_bChangelevelAfterStalemate;

	float						m_flRoundStartTime;		// time the current round started
	float						m_flNewThrottledAlertTime;		// time that we can play another throttled alert

	bool						m_bUseAddScoreAnim;

	gamerules_roundstate_t		m_prevState;

	bool						m_bPlayerReadyBefore[MAX_PLAYERS+1];	// Test to see if a player has hit ready before

	float						m_flLastTeamWin;

	bool						m_bStopWatchShouldBeTimedWin;

private:

	CUtlMap < int, int >	m_GameTeams;  // Team index, Score
#endif
	// End server specific
	//----------------------------------------------------------------------------------

	//----------------------------------------------------------------------------------
	// Client specific
#ifdef CLIENT_DLL
public:
	virtual void	OnPreDataChanged( DataUpdateType_t updateType );
	virtual void	OnDataChanged( DataUpdateType_t updateType );
	virtual void	HandleOvertimeBegin(){}
	virtual void	GetTeamGlowColor( int nTeam, float &r, float &g, float &b ){ r = 0.76f; g = 0.76f; b = 0.76f; }

private:
	bool			m_bOldInWaitingForPlayers;
	bool			m_bOldInOvertime;
	bool			m_bOldInSetup;
#endif // CLIENT_DLL

public:
	bool WouldChangeUnbalanceTeams( int iNewTeam, int iCurrentTeam  );
	bool AreTeamsUnbalanced( int &iHeaviestTeam, int &iLightestTeam );
	virtual bool HaveCheatsBeenEnabledDuringLevel( void ) { return m_bCheatsEnabledDuringLevel; }

	float GetPreroundCountdownTime( void ){ return m_flCountdownTime; }

protected:
	CNetworkVar( gamerules_roundstate_t, m_iRoundState );
	CNetworkVar( bool, m_bInOvertime ); // Are we currently in overtime?
	CNetworkVar( bool, m_bInSetup ); // Are we currently in setup?
	CNetworkVar( bool, m_bSwitchedTeamsThisRound );

protected:
	CNetworkVar( int,			m_iWinningTeam );				// Set before entering GR_STATE_TEAM_WIN
	CNetworkVar( int,			m_iWinReason );
	CNetworkVar( bool,			m_bInWaitingForPlayers );
	CNetworkVar( bool,			m_bAwaitingReadyRestart );
	CNetworkVar( float,			m_flRestartRoundTime );
	CNetworkVar( float,			m_flMapResetTime );						// Time that the map was reset
	CNetworkArray( float,		m_flNextRespawnWave, MAX_TEAMS );		// Minor waste, but cleaner code
	CNetworkArray( bool,		m_bTeamReady, MAX_TEAMS );
	CNetworkVar( bool,			m_bStopWatch );
	CNetworkVar( bool,			m_bMultipleTrains ); // two trains in this map?
	CNetworkArray( bool,		m_bPlayerReady, MAX_PLAYERS );
	CNetworkVar( bool,			m_bCheatsEnabledDuringLevel );
	CNetworkVar( int, 			m_nRoundsPlayed );
	CNetworkVar( float,			m_flCountdownTime );
	CNetworkVar( float,			m_flStateTransitionTime );	// Timer for round states
public:
	CNetworkArray( float,		m_TeamRespawnWaveTimes, MAX_TEAMS );	// Time between each team's respawn wave

private:
	float m_flStartBalancingTeamsAt;
	float m_flNextBalanceTeamsTime;
	bool m_bPrintedUnbalanceWarning;
	float m_flFoundUnbalancedTeamsTime;

	float	m_flAutoBalanceQueueTimeEnd;
	int		m_nAutoBalanceQueuePlayerIndex;
	int		m_nAutoBalanceQueuePlayerScore;

	int		m_nLastEventFiredTime;
protected:
	bool	m_bAllowBetweenRounds;

public:

	float	m_flStopWatchTotalTime;
	int		m_iLastCapPointChanged;
};

// Utility function
bool FindInList( const char **pStrings, const char *pToFind );

inline CTeamplayRoundBasedRules* TeamplayRoundBasedRules()
{
	return static_cast<CTeamplayRoundBasedRules*>(g_pGameRules);
}

#endif // TEAMPLAYROUNDBASED_GAMERULES_H