//========= 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"
# 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 ,
} ;
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
{
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 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 ( void ) ;
# 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 GetMvMPotentialDefendersLobbyPlayerInfo ( CUtlVector < LobbyPlayerInfo_t > & vecMvmDefenders , bool bIncludeBots = false ) ;
# endif
//----------------------------------------------------------------------------------
// 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 ( void ) ;
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 ) ;
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 ) ;
}
virtual void PlayTrainCaptureAlert ( CTeamControlPoint * pPoint , bool bFinalPointInMap ) { return ; }
virtual void PlaySpecialCapSounds ( int iCappingTeam ) { 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 ( void ) ;
int GetTimeLeft ( void ) ;
virtual bool CheckWinLimit ( void ) ;
bool CheckMaxRounds ( void ) ;
void CheckReadyRestart ( void ) ;
# if defined(TF_CLIENT_DLL) || defined(TF_DLL)
bool AreDefendingPlayersReady ( ) ;
# 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 ) ;
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 ;
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 ) ;
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 ) ;
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 ;
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