aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/shared/teamplayroundbased_gamerules.h
blob: 95060d3de552184a383d5138c75c4d862f344782 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
//========= 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"
	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,
#endif
};

enum stalemate_reasons_t
{
	STALEMATE_JOIN_MID,
	STALEMATE_TIMER,
	STALEMATE_SERVER_TIMELIMIT,

	NUM_STALEMATE_REASONS,
};


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

/// Info about a player in a PVE game or any other mode that we
/// might eventually decide to use the lobby system for.
struct LobbyPlayerInfo_t
{
	int m_nEntNum; //< Index of player (1...MAX_PLAYERS), or 0 if the guy is in the lobby but not yet known to us
	CUtlString m_sPlayerName; //< Player display name
	CSteamID m_steamID; //< Steam ID of the player
	int m_iTeam; //< Team selection.
	bool m_bInLobby; //< Is this guy in the lobby?
	bool m_bConnected; //< Is this a bot?
	bool m_bBot; //< Is this a bot?
	bool m_bSquadSurplus; //< Did he present a voucher to get surplus for his squad
};

#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 ){ return m_iWinningTeam; }
	int GetWinReason() { return m_iWinReason; }

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

	bool InSetup( void ){ return m_bInSetup; }
	
	void		BalanceTeams( bool bRequireSwitcheesToBeDead );

	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 ) { return m_flRestartRoundTime; }

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

	//Koth Mode
	virtual bool	IsInKothMode( void ) { 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 bFinal = false );

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

	// Get list of all the players, including those in the lobby but who have
	// not yet joined.
	void GetAllPlayersLobbyInfo( CUtlVector<LobbyPlayerInfo_t> &vecPlayers, bool bIncludeBots = false );

	// Get list of players who are on the defending team now, or are likely
	// to end up on the defending team (not yet connected or assigned a team)
	void GetPotentialPlayersLobbyPlayerInfo( CUtlVector<LobbyPlayerInfo_t> &vecLobbyPlayers, bool bIncludeBots = false );

#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( void ) { 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_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 );

	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; }

	virtual float GetWaitingForPlayersTime( void ) { return mp_waitingforplayers_time.GetFloat(); }
	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 );
	int GetRoundsPlayed( void ) { return m_nRoundsPlayed; }

	virtual void RecalculateControlPointState( void ){ return; }

	virtual bool ShouldSkipAutoScramble( void ){ return false; }

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

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 );
	bool		 CheckMaxRounds( bool bAllowEnd = true );

	void		 CheckReadyRestart( void );
#if defined(TF_CLIENT_DLL) || defined(TF_DLL)
	bool		 AreLobbyPlayersOnTeamReady( int iTeam );
	bool		 AreLobbyPlayersConnected( void );
#endif

	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 int	 CountActivePlayers( void );

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

	void PlayStartRoundVoice( void );
	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_flStateTransitionTime;	// Timer for round states

	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

	int							m_nRoundsPlayed;
	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;

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; }

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 );

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;

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