2008-09-15 01:07:45 -05:00
//====== Copyright <20> 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
# include "cbase.h"
# include "mp_shareddefs.h"
# include "teamplayroundbased_gamerules.h"
# ifdef CLIENT_DLL
# include "iclientmode.h"
# include <vgui_controls/AnimationController.h>
# include <igameevents.h>
# include "c_team.h"
# define CTeam C_Team
# else
# include "viewport_panel_names.h"
# include "team.h"
# include "mapentities.h"
# include "gameinterface.h"
# include "eventqueue.h"
# include "team_control_point_master.h"
# include "team_train_watcher.h"
# include "serverbenchmark_base.h"
# endif
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
# ifndef CLIENT_DLL
CUtlVector < CHandle < CTeamControlPointMaster > > g_hControlPointMasters ;
extern bool IsInCommentaryMode ( void ) ;
# endif
extern ConVar spec_freeze_time ;
extern ConVar spec_freeze_traveltime ;
# ifdef CLIENT_DLL
void RecvProxy_TeamplayRoundState ( const CRecvProxyData * pData , void * pStruct , void * pOut )
{
CTeamplayRoundBasedRules * pGamerules = ( CTeamplayRoundBasedRules * ) pStruct ;
int iRoundState = pData - > m_Value . m_Int ;
pGamerules - > SetRoundState ( iRoundState ) ;
}
# endif
BEGIN_NETWORK_TABLE_NOBASE ( CTeamplayRoundBasedRules , DT_TeamplayRoundBasedRules )
# ifdef CLIENT_DLL
RecvPropInt ( RECVINFO ( m_iRoundState ) , 0 , RecvProxy_TeamplayRoundState ) ,
RecvPropBool ( RECVINFO ( m_bInWaitingForPlayers ) ) ,
RecvPropInt ( RECVINFO ( m_iWinningTeam ) ) ,
RecvPropInt ( RECVINFO ( m_bInOvertime ) ) ,
RecvPropInt ( RECVINFO ( m_bInSetup ) ) ,
RecvPropInt ( RECVINFO ( m_bSwitchedTeamsThisRound ) ) ,
RecvPropBool ( RECVINFO ( m_bAwaitingReadyRestart ) ) ,
RecvPropTime ( RECVINFO ( m_flRestartRoundTime ) ) ,
RecvPropTime ( RECVINFO ( m_flMapResetTime ) ) ,
RecvPropArray3 ( RECVINFO_ARRAY ( m_flNextRespawnWave ) , RecvPropTime ( RECVINFO ( m_flNextRespawnWave [ 0 ] ) ) ) ,
RecvPropArray3 ( RECVINFO_ARRAY ( m_TeamRespawnWaveTimes ) , RecvPropFloat ( RECVINFO ( m_TeamRespawnWaveTimes [ 0 ] ) ) ) ,
RecvPropArray3 ( RECVINFO_ARRAY ( m_bTeamReady ) , RecvPropBool ( RECVINFO ( m_bTeamReady [ 0 ] ) ) ) ,
RecvPropBool ( RECVINFO ( m_bStopWatch ) ) ,
# else
SendPropInt ( SENDINFO ( m_iRoundState ) , 5 ) ,
SendPropBool ( SENDINFO ( m_bInWaitingForPlayers ) ) ,
SendPropInt ( SENDINFO ( m_iWinningTeam ) , 3 , SPROP_UNSIGNED ) ,
SendPropBool ( SENDINFO ( m_bInOvertime ) ) ,
SendPropBool ( SENDINFO ( m_bInSetup ) ) ,
SendPropBool ( SENDINFO ( m_bSwitchedTeamsThisRound ) ) ,
SendPropBool ( SENDINFO ( m_bAwaitingReadyRestart ) ) ,
SendPropTime ( SENDINFO ( m_flRestartRoundTime ) ) ,
SendPropTime ( SENDINFO ( m_flMapResetTime ) ) ,
SendPropArray3 ( SENDINFO_ARRAY3 ( m_flNextRespawnWave ) , SendPropTime ( SENDINFO_ARRAY ( m_flNextRespawnWave ) ) ) ,
SendPropArray3 ( SENDINFO_ARRAY3 ( m_TeamRespawnWaveTimes ) , SendPropFloat ( SENDINFO_ARRAY ( m_TeamRespawnWaveTimes ) ) ) ,
SendPropArray3 ( SENDINFO_ARRAY3 ( m_bTeamReady ) , SendPropBool ( SENDINFO_ARRAY ( m_bTeamReady ) ) ) ,
SendPropBool ( SENDINFO ( m_bStopWatch ) ) ,
# endif
END_NETWORK_TABLE ( )
IMPLEMENT_NETWORKCLASS_ALIASED ( TeamplayRoundBasedRulesProxy , DT_TeamplayRoundBasedRulesProxy )
# ifdef CLIENT_DLL
void RecvProxy_TeamplayRoundBasedRules ( const RecvProp * pProp , void * * pOut , void * pData , int objectID )
{
CTeamplayRoundBasedRules * pRules = dynamic_cast < CTeamplayRoundBasedRules * > ( GameRules ( ) ) ;
Assert ( pRules ) ;
* pOut = pRules ;
}
BEGIN_RECV_TABLE ( CTeamplayRoundBasedRulesProxy , DT_TeamplayRoundBasedRulesProxy )
RecvPropDataTable ( " teamplayroundbased_gamerules_data " , 0 , 0 , & REFERENCE_RECV_TABLE ( DT_TeamplayRoundBasedRules ) , RecvProxy_TeamplayRoundBasedRules )
END_RECV_TABLE ( )
void CTeamplayRoundBasedRulesProxy : : OnPreDataChanged ( DataUpdateType_t updateType )
{
BaseClass : : OnPreDataChanged ( updateType ) ;
// Reroute data changed calls to the non-entity gamerules
CTeamplayRoundBasedRules * pRules = dynamic_cast < CTeamplayRoundBasedRules * > ( GameRules ( ) ) ;
Assert ( pRules ) ;
pRules - > OnPreDataChanged ( updateType ) ;
}
void CTeamplayRoundBasedRulesProxy : : OnDataChanged ( DataUpdateType_t updateType )
{
BaseClass : : OnDataChanged ( updateType ) ;
// Reroute data changed calls to the non-entity gamerules
CTeamplayRoundBasedRules * pRules = dynamic_cast < CTeamplayRoundBasedRules * > ( GameRules ( ) ) ;
Assert ( pRules ) ;
pRules - > OnDataChanged ( updateType ) ;
}
# else
void * SendProxy_TeamplayRoundBasedRules ( const SendProp * pProp , const void * pStructBase , const void * pData , CSendProxyRecipients * pRecipients , int objectID )
{
CTeamplayRoundBasedRules * pRules = dynamic_cast < CTeamplayRoundBasedRules * > ( GameRules ( ) ) ;
Assert ( pRules ) ;
pRecipients - > SetAllRecipients ( ) ;
return pRules ;
}
BEGIN_SEND_TABLE ( CTeamplayRoundBasedRulesProxy , DT_TeamplayRoundBasedRulesProxy )
SendPropDataTable ( " teamplayroundbased_gamerules_data " , 0 , & REFERENCE_SEND_TABLE ( DT_TeamplayRoundBasedRules ) , SendProxy_TeamplayRoundBasedRules )
END_SEND_TABLE ( )
BEGIN_DATADESC ( CTeamplayRoundBasedRulesProxy )
// Inputs.
DEFINE_INPUTFUNC ( FIELD_BOOLEAN , " SetStalemateOnTimelimit " , InputSetStalemateOnTimelimit ) ,
END_DATADESC ( )
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRulesProxy : : InputSetStalemateOnTimelimit ( inputdata_t & inputdata )
{
TeamplayRoundBasedRules ( ) - > SetStalemateOnTimelimit ( inputdata . value . Bool ( ) ) ;
}
# endif
ConVar mp_capstyle ( " mp_capstyle " , " 1 " , FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY , " Sets the style of capture points used. 0 = Fixed players required to cap. 1 = More players cap faster, but longer cap times. " ) ;
ConVar mp_blockstyle ( " mp_blockstyle " , " 1 " , FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY , " Sets the style of capture point blocking used. 0 = Blocks break captures completely. 1 = Blocks only pause captures. " ) ;
ConVar mp_respawnwavetime ( " mp_respawnwavetime " , " 10.0 " , FCVAR_NOTIFY | FCVAR_REPLICATED , " Time between respawn waves. " ) ;
ConVar mp_capdeteriorate_time ( " mp_capdeteriorate_time " , " 90.0 " , FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY , " Time it takes for a full capture point to deteriorate. " ) ;
ConVar mp_tournament ( " mp_tournament " , " 0 " , FCVAR_REPLICATED | FCVAR_NOTIFY ) ;
ConVar mp_teams_unbalance_limit ( " mp_teams_unbalance_limit " , " 1 " , FCVAR_REPLICATED | FCVAR_NOTIFY ,
" Teams are unbalanced when one team has this many more players than the other team. (0 disables check) " ,
true , 0 , // min value
true , 30 // max value
) ;
ConVar mp_maxrounds ( " mp_maxrounds " , " 0 " , FCVAR_REPLICATED | FCVAR_NOTIFY , " max number of rounds to play before server changes maps " , true , 0 , false , 0 ) ;
ConVar mp_winlimit ( " mp_winlimit " , " 0 " , FCVAR_REPLICATED | FCVAR_NOTIFY , " Max score one team can reach before server changes maps " , true , 0 , false , 0 ) ;
ConVar mp_disable_respawn_times ( " mp_disable_respawn_times " , " 0 " , FCVAR_NOTIFY | FCVAR_REPLICATED ) ;
# ifdef GAME_DLL
ConVar mp_showroundtransitions ( " mp_showroundtransitions " , " 0 " , FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY , " Show gamestate round transitions. " ) ;
ConVar mp_enableroundwaittime ( " mp_enableroundwaittime " , " 1 " , FCVAR_REPLICATED , " Enable timers to wait between rounds. " ) ;
ConVar mp_showcleanedupents ( " mp_showcleanedupents " , " 0 " , FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY , " Show entities that are removed on round respawn. " ) ;
ConVar mp_bonusroundtime ( " mp_bonusroundtime " , " 15 " , FCVAR_REPLICATED , " Time after round win until round restarts " , true , 5 , true , 15 ) ;
ConVar mp_restartround ( " mp_restartround " , " 0 " , FCVAR_GAMEDLL , " If non-zero, the current round will restart in the specified number of seconds " ) ;
ConVar mp_stalemate_timelimit ( " mp_stalemate_timelimit " , " 240 " , FCVAR_REPLICATED , " Timelimit (in seconds) of the stalemate round . " ) ;
ConVar mp_autoteambalance ( " mp_autoteambalance " , " 1 " , FCVAR_NOTIFY ) ;
ConVar mp_stalemate_enable ( " mp_stalemate_enable " , " 0 " , FCVAR_NOTIFY , " Enable/Disable stalemate mode. " ) ;
ConVar mp_stalemate_at_timelimit ( " mp_stalemate_at_timelimit " , " 0 " , FCVAR_NOTIFY , " Allow the match to end when mp_timelimit hits instead of waiting for the end of the current round. " ) ;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void cc_SwitchTeams ( const CCommand & args )
{
if ( UTIL_IsCommandIssuedByServerAdmin ( ) )
{
CTeamplayRoundBasedRules * pRules = dynamic_cast < CTeamplayRoundBasedRules * > ( GameRules ( ) ) ;
if ( pRules )
{
pRules - > SetSwitchTeams ( true ) ;
mp_restartgame . SetValue ( 5 ) ;
pRules - > ShouldResetScores ( false , false ) ;
pRules - > ShouldResetRoundsPlayed ( false ) ;
}
}
}
static ConCommand mp_switchteams ( " mp_switchteams " , cc_SwitchTeams , " Switch teams and restart the game " ) ;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void cc_ScrambleTeams ( const CCommand & args )
{
if ( UTIL_IsCommandIssuedByServerAdmin ( ) )
{
CTeamplayRoundBasedRules * pRules = dynamic_cast < CTeamplayRoundBasedRules * > ( GameRules ( ) ) ;
if ( pRules )
{
pRules - > SetScrambleTeams ( true ) ;
mp_restartgame . SetValue ( 5 ) ;
pRules - > ShouldResetScores ( true , false ) ;
}
}
}
static ConCommand mp_scrambleteams ( " mp_scrambleteams " , cc_ScrambleTeams , " Scramble the teams and restart the game " ) ;
// Classnames of entities that are preserved across round restarts
static const char * s_PreserveEnts [ ] =
{
" player " ,
" viewmodel " ,
" worldspawn " ,
" soundent " ,
" ai_network " ,
" ai_hint " ,
" env_soundscape " ,
" env_soundscape_proxy " ,
" env_soundscape_triggerable " ,
" env_sprite " ,
" env_sun " ,
" env_wind " ,
" env_fog_controller " ,
" func_wall " ,
" func_illusionary " ,
" info_node " ,
" info_target " ,
" info_node_hint " ,
" point_commentary_node " ,
" point_viewcontrol " ,
" func_precipitation " ,
" func_team_wall " ,
" shadow_control " ,
" sky_camera " ,
" scene_manager " ,
" trigger_soundscape " ,
" commentary_auto " ,
" point_commentary_node " ,
" point_commentary_viewpoint " ,
" " , // END Marker
} ;
CON_COMMAND_F ( mp_forcewin , " Forces team to win " , FCVAR_CHEAT )
{
if ( ! UTIL_IsCommandIssuedByServerAdmin ( ) )
return ;
CTeamplayRoundBasedRules * pRules = dynamic_cast < CTeamplayRoundBasedRules * > ( GameRules ( ) ) ;
if ( pRules )
{
int iTeam = TEAM_UNASSIGNED ;
if ( args . ArgC ( ) = = 1 )
{
// if no team specified, use player 1's team
iTeam = UTIL_PlayerByIndex ( 1 ) - > GetTeamNumber ( ) ;
}
else if ( args . ArgC ( ) = = 2 )
{
// if team # specified, use that
iTeam = atoi ( args [ 1 ] ) ;
}
else
{
Msg ( " Usage: mp_forcewin <opt: team#> " ) ;
return ;
}
int iWinReason = ( TEAM_UNASSIGNED = = iTeam ? WINREASON_STALEMATE : WINREASON_ALL_POINTS_CAPTURED ) ;
pRules - > SetWinningTeam ( iTeam , iWinReason ) ;
}
}
# endif // GAME_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTeamplayRoundBasedRules : : CTeamplayRoundBasedRules ( void )
{
for ( int i = 0 ; i < MAX_TEAMS ; i + + )
{
m_flNextRespawnWave . Set ( i , 0 ) ;
m_TeamRespawnWaveTimes . Set ( i , - 1.0f ) ;
m_bTeamReady . Set ( i , false ) ;
# ifdef GAME_DLL
m_flOriginalTeamRespawnWaveTime [ i ] = - 1.0f ;
# endif
}
m_bInOvertime = false ;
m_bInSetup = false ;
m_bSwitchedTeamsThisRound = false ;
m_flStopWatchTotalTime = - 1.0f ;
# ifdef GAME_DLL
m_pCurStateInfo = NULL ;
State_Transition ( GR_STATE_PREGAME ) ;
InitTeams ( ) ;
ResetMapTime ( ) ;
ResetScores ( ) ;
SetForceMapReset ( true ) ;
SetRoundToPlayNext ( NULL_STRING ) ;
m_bInWaitingForPlayers = false ;
m_bAwaitingReadyRestart = false ;
m_flRestartRoundTime = - 1 ;
m_flMapResetTime = 0 ;
m_bPrevRoundWasWaitingForPlayers = false ;
m_bResetTeamScores = true ;
m_bResetPlayerScores = true ;
m_bResetRoundsPlayed = true ;
m_iWinningTeam = TEAM_UNASSIGNED ;
m_iszPreviousRounds . RemoveAll ( ) ;
SetFirstRoundPlayed ( NULL_STRING ) ;
m_bAllowStalemateAtTimelimit = false ;
m_bChangelevelAfterStalemate = false ;
m_flRoundStartTime = 0 ;
m_flStartBalancingTeamsAt = 0 ;
m_bPrintedUnbalanceWarning = false ;
m_flFoundUnbalancedTeamsTime = - 1 ;
m_nRoundsPlayed = 0 ;
m_bUseAddScoreAnim = false ;
m_bStopWatch = false ;
m_bAwaitingReadyRestart = false ;
if ( IsInTournamentMode ( ) = = true )
{
m_bAwaitingReadyRestart = true ;
}
# endif
}
# ifdef GAME_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : SetTeamRespawnWaveTime ( int iTeam , float flValue )
{
if ( flValue < 0 )
{
flValue = 0 ;
}
// initialized to -1 so we can try to determine if this is the first spawn time we have received for this team
if ( m_flOriginalTeamRespawnWaveTime [ iTeam ] < 0 )
{
m_flOriginalTeamRespawnWaveTime [ iTeam ] = flValue ;
}
m_TeamRespawnWaveTimes . Set ( iTeam , flValue ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : AddTeamRespawnWaveTime ( int iTeam , float flValue )
{
float flAddAmount = flValue ;
float flCurrentSetting = m_TeamRespawnWaveTimes [ iTeam ] ;
float flNewValue ;
if ( flCurrentSetting < 0 )
{
flCurrentSetting = mp_respawnwavetime . GetFloat ( ) ;
}
// initialized to -1 so we can try to determine if this is the first spawn time we have received for this team
if ( m_flOriginalTeamRespawnWaveTime [ iTeam ] < 0 )
{
m_flOriginalTeamRespawnWaveTime [ iTeam ] = flCurrentSetting ;
}
flNewValue = flCurrentSetting + flAddAmount ;
if ( flNewValue < 0 )
{
flNewValue = 0 ;
}
m_TeamRespawnWaveTimes . Set ( iTeam , flNewValue ) ;
}
# endif
//-----------------------------------------------------------------------------
// Purpose: don't let us spawn before our freezepanel time would have ended, even if we skip it
//-----------------------------------------------------------------------------
float CTeamplayRoundBasedRules : : GetNextRespawnWave ( int iTeam , CBasePlayer * pPlayer )
{
if ( State_Get ( ) = = GR_STATE_STALEMATE )
return 0 ;
// If we are purely checking when the next respawn wave is for this team
if ( pPlayer = = NULL )
{
return m_flNextRespawnWave [ iTeam ] ;
}
// The soonest this player may spawn
float flMinSpawnTime = GetMinTimeWhenPlayerMaySpawn ( pPlayer ) ;
// the next scheduled respawn wave time
float flNextRespawnTime = m_flNextRespawnWave [ iTeam ] ;
// the length of one respawn wave. We'll check in increments of this
float flRespawnWaveMaxLen = GetRespawnWaveMaxLength ( iTeam ) ;
if ( flRespawnWaveMaxLen < = 0 )
{
return flNextRespawnTime ;
}
// Keep adding the length of one respawn until we find a wave that
// this player will be eligible to spawn in.
while ( flNextRespawnTime < flMinSpawnTime )
{
flNextRespawnTime + = flRespawnWaveMaxLen ;
}
return flNextRespawnTime ;
}
//-----------------------------------------------------------------------------
// Purpose: Is the player past the required delays for spawning
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : HasPassedMinRespawnTime ( CBasePlayer * pPlayer )
{
float flMinSpawnTime = GetMinTimeWhenPlayerMaySpawn ( pPlayer ) ;
return ( gpGlobals - > curtime > flMinSpawnTime ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CTeamplayRoundBasedRules : : GetMinTimeWhenPlayerMaySpawn ( CBasePlayer * pPlayer )
{
// Min respawn time is the sum of
//
// a) the length of one full *unscaled* respawn wave for their team
// and
// b) death anim length + freeze panel length
float flDeathAnimLength = 2.0 + spec_freeze_traveltime . GetFloat ( ) + spec_freeze_time . GetFloat ( ) ;
float fMinDelay = flDeathAnimLength + GetRespawnWaveMaxLength ( pPlayer - > GetTeamNumber ( ) , false ) ;
return pPlayer - > GetDeathTime ( ) + fMinDelay ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CTeamplayRoundBasedRules : : GetRespawnTimeScalar ( int iTeam )
{
// For long respawn times, scale the time as the number of players drops
int iOptimalPlayers = 8 ; // 16 players total, 8 per team
int iNumPlayers = GetGlobalTeam ( iTeam ) - > GetNumPlayers ( ) ;
float flScale = RemapValClamped ( iNumPlayers , 1 , iOptimalPlayers , 0.25 , 1.0 ) ;
return flScale ;
}
# ifdef GAME_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : SetForceMapReset ( bool reset )
{
m_bForceMapReset = reset ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : Think ( void )
{
if ( g_fGameOver ) // someone else quit the game already
{
// check to see if we should change levels now
if ( m_flIntermissionEndTime & & ( m_flIntermissionEndTime < gpGlobals - > curtime ) )
{
if ( ! IsX360 ( ) )
{
ChangeLevel ( ) ; // intermission is over
}
else
{
IGameEvent * event = gameeventmanager - > CreateEvent ( " player_stats_updated " ) ;
if ( event )
{
event - > SetBool ( " forceupload " , true ) ;
gameeventmanager - > FireEvent ( event ) ;
}
engine - > MultiplayerEndGame ( ) ;
}
// Don't run this code again
m_flIntermissionEndTime = 0.f ;
}
return ;
}
State_Think ( ) ;
if ( m_hWaitingForPlayersTimer )
{
Assert ( m_bInWaitingForPlayers ) ;
}
if ( gpGlobals - > curtime > m_flNextPeriodicThink )
{
// Don't end the game during win or stalemate states
if ( State_Get ( ) ! = GR_STATE_TEAM_WIN & & State_Get ( ) ! = GR_STATE_STALEMATE & & State_Get ( ) ! = GR_STATE_GAME_OVER )
{
if ( CheckWinLimit ( ) )
return ;
if ( CheckMaxRounds ( ) )
return ;
}
CheckRestartRound ( ) ;
CheckWaitingForPlayers ( ) ;
m_flNextPeriodicThink = gpGlobals - > curtime + 1.0 ;
}
// Bypass teamplay think.
CGameRules : : Think ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : TimerMayExpire ( void )
{
// team_train_watchers can also prevent timer expiring ( overtime )
CTeamTrainWatcher * pWatcher = dynamic_cast < CTeamTrainWatcher * > ( gEntList . FindEntityByClassname ( NULL , " team_train_watcher " ) ) ;
while ( pWatcher )
{
if ( ! pWatcher - > TimerMayExpire ( ) )
{
return false ;
}
pWatcher = dynamic_cast < CTeamTrainWatcher * > ( gEntList . FindEntityByClassname ( pWatcher , " team_train_watcher " ) ) ;
}
return BaseClass : : TimerMayExpire ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : CheckChatText ( CBasePlayer * pPlayer , char * pText )
{
CheckChatForReadySignal ( pPlayer , pText ) ;
BaseClass : : CheckChatText ( pPlayer , pText ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : CheckChatForReadySignal ( CBasePlayer * pPlayer , const char * chatmsg )
{
if ( IsInTournamentMode ( ) = = false )
{
if ( m_bAwaitingReadyRestart & & FStrEq ( chatmsg , mp_clan_ready_signal . GetString ( ) ) )
{
int iTeam = pPlayer - > GetTeamNumber ( ) ;
if ( iTeam > LAST_SHARED_TEAM & & iTeam < GetNumberOfTeams ( ) )
{
m_bTeamReady . Set ( iTeam , true ) ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_team_ready " ) ;
if ( event )
{
event - > SetInt ( " team " , iTeam ) ;
gameeventmanager - > FireEvent ( event ) ;
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : GoToIntermission ( void )
{
if ( IsInTournamentMode ( ) = = true )
return ;
BaseClass : : GoToIntermission ( ) ;
// set all players to FL_FROZEN
for ( int i = 1 ; i < = MAX_PLAYERS ; i + + )
{
CBasePlayer * pPlayer = UTIL_PlayerByIndex ( i ) ;
if ( pPlayer )
{
pPlayer - > AddFlag ( FL_FROZEN ) ;
}
}
// Print out map stats to a text file
//WriteStatsFile( "stats.xml" );
State_Enter ( GR_STATE_GAME_OVER ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : SetInWaitingForPlayers ( bool bWaitingForPlayers )
{
// never waiting for players when loading a bug report
if ( IsLoadingBugBaitReport ( ) | | gpGlobals - > eLoadType = = MapLoad_Background )
{
m_bInWaitingForPlayers = false ;
return ;
}
if ( m_bInWaitingForPlayers = = bWaitingForPlayers )
return ;
m_bInWaitingForPlayers = bWaitingForPlayers ;
if ( m_bInWaitingForPlayers )
{
m_flWaitingForPlayersTimeEnds = gpGlobals - > curtime + mp_waitingforplayers_time . GetFloat ( ) ;
}
else
{
m_flWaitingForPlayersTimeEnds = - 1 ;
if ( m_hWaitingForPlayersTimer )
{
UTIL_Remove ( m_hWaitingForPlayersTimer ) ;
}
RestoreActiveTimer ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : SetOvertime ( bool bOvertime )
{
if ( m_bInOvertime = = bOvertime )
return ;
if ( bOvertime )
{
UTIL_LogPrintf ( " World triggered \" Round_Overtime \" \n " ) ;
}
m_bInOvertime = bOvertime ;
if ( m_bInOvertime )
{
// tell train watchers that we've transitioned to overtime
CTeamTrainWatcher * pWatcher = dynamic_cast < CTeamTrainWatcher * > ( gEntList . FindEntityByClassname ( NULL , " team_train_watcher " ) ) ;
while ( pWatcher )
{
variant_t emptyVariant ;
pWatcher - > AcceptInput ( " OnStartOvertime " , NULL , NULL , emptyVariant , 0 ) ;
pWatcher = dynamic_cast < CTeamTrainWatcher * > ( gEntList . FindEntityByClassname ( pWatcher , " team_train_watcher " ) ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : SetSetup ( bool bSetup )
{
if ( m_bInSetup = = bSetup )
return ;
m_bInSetup = bSetup ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : CheckWaitingForPlayers ( void )
{
// never waiting for players when loading a bug report
if ( IsLoadingBugBaitReport ( ) | | gpGlobals - > eLoadType = = MapLoad_Background )
return ;
if ( mp_waitingforplayers_restart . GetBool ( ) )
{
if ( m_bInWaitingForPlayers )
{
m_flWaitingForPlayersTimeEnds = gpGlobals - > curtime + mp_waitingforplayers_time . GetFloat ( ) ;
if ( m_hWaitingForPlayersTimer )
{
variant_t sVariant ;
2008-09-15 02:50:57 -05:00
sVariant . SetInt ( ( int ) ( m_flWaitingForPlayersTimeEnds - gpGlobals - > curtime ) ) ;
2008-09-15 01:07:45 -05:00
m_hWaitingForPlayersTimer - > AcceptInput ( " SetTime " , NULL , NULL , sVariant , 0 ) ;
}
}
else
{
SetInWaitingForPlayers ( true ) ;
}
mp_waitingforplayers_restart . SetValue ( 0 ) ;
}
if ( mp_waitingforplayers_cancel . GetBool ( ) & & IsInTournamentMode ( ) = = false )
{
// Cancel the wait period and manually Resume() the timer if
// it's not supposed to start paused at the beginning of a round.
// We must do this before SetInWaitingForPlayers() is called because it will
// restore the timer in the HUD and set the handle to NULL
if ( m_hPreviousActiveTimer . Get ( ) )
{
CTeamRoundTimer * pTimer = dynamic_cast < CTeamRoundTimer * > ( m_hPreviousActiveTimer . Get ( ) ) ;
if ( pTimer & & ! pTimer - > StartPaused ( ) )
{
pTimer - > ResumeTimer ( ) ;
}
}
SetInWaitingForPlayers ( false ) ;
mp_waitingforplayers_cancel . SetValue ( 0 ) ;
}
if ( m_bInWaitingForPlayers )
{
if ( IsInTournamentMode ( ) = = true )
return ;
// only exit the waitingforplayers if the time is up, and we are not in a round
// restart countdown already, and we are not waiting for a ready restart
if ( gpGlobals - > curtime > m_flWaitingForPlayersTimeEnds & & m_flRestartRoundTime < 0 & & ! m_bAwaitingReadyRestart )
{
m_flRestartRoundTime = gpGlobals - > curtime ; // reset asap
// if "waiting for players" is ending and we're restarting...
// keep the current round that we're already running around in as the first round after the restart
CTeamControlPointMaster * pMaster = g_hControlPointMasters . Count ( ) ? g_hControlPointMasters [ 0 ] : NULL ;
if ( pMaster & & pMaster - > PlayingMiniRounds ( ) & & pMaster - > GetCurrentRound ( ) )
{
SetRoundToPlayNext ( pMaster - > GetRoundToUseAfterRestart ( ) ) ;
}
}
else
{
if ( ! m_hWaitingForPlayersTimer )
{
// Stop any timers, and bring up a new one
HideActiveTimer ( ) ;
variant_t sVariant ;
m_hWaitingForPlayersTimer = ( CTeamRoundTimer * ) CBaseEntity : : Create ( " team_round_timer " , vec3_origin , vec3_angle ) ;
m_hWaitingForPlayersTimer - > SetName ( MAKE_STRING ( " zz_teamplay_waiting_timer " ) ) ;
m_hWaitingForPlayersTimer - > KeyValue ( " show_in_hud " , " 1 " ) ;
2008-09-15 02:50:57 -05:00
sVariant . SetInt ( ( int ) ( m_flWaitingForPlayersTimeEnds - gpGlobals - > curtime ) ) ;
2008-09-15 01:07:45 -05:00
m_hWaitingForPlayersTimer - > AcceptInput ( " SetTime " , NULL , NULL , sVariant , 0 ) ;
m_hWaitingForPlayersTimer - > AcceptInput ( " Resume " , NULL , NULL , sVariant , 0 ) ;
m_hWaitingForPlayersTimer - > AcceptInput ( " Enable " , NULL , NULL , sVariant , 0 ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : CheckRestartRound ( void )
{
if ( mp_clan_readyrestart . GetBool ( ) & & IsInTournamentMode ( ) = = false )
{
m_bAwaitingReadyRestart = true ;
for ( int i = LAST_SHARED_TEAM + 1 ; i < GetNumberOfTeams ( ) ; i + + )
{
m_bTeamReady . Set ( i , false ) ;
}
const char * pszReadyString = mp_clan_ready_signal . GetString ( ) ;
UTIL_ClientPrintAll ( HUD_PRINTCONSOLE , " #clan_ready_rules " , pszReadyString ) ;
UTIL_ClientPrintAll ( HUD_PRINTTALK , " #clan_ready_rules " , pszReadyString ) ;
// Don't let them put anything malicious in there
if ( pszReadyString = = NULL | | Q_strlen ( pszReadyString ) > 16 )
{
pszReadyString = " ready " ;
}
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_ready_restart " ) ;
if ( event )
{
gameeventmanager - > FireEvent ( event ) ;
}
mp_clan_readyrestart . SetValue ( 0 ) ;
// cancel any restart round in progress
m_flRestartRoundTime = - 1 ;
}
// Restart the game if specified by the server
int iRestartDelay = mp_restartround . GetInt ( ) ;
if ( iRestartDelay = = 0 )
{
iRestartDelay = mp_restartgame . GetInt ( ) ;
}
if ( iRestartDelay > 0 )
{
if ( iRestartDelay > 60 )
iRestartDelay = 60 ;
if ( mp_restartgame . GetInt ( ) > 0 )
{
SetForceMapReset ( true ) ;
}
else
{
SetForceMapReset ( false ) ;
}
SetInStopWatch ( false ) ;
m_flRestartRoundTime = gpGlobals - > curtime + iRestartDelay ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_round_restart_seconds " ) ;
if ( event )
{
event - > SetInt ( " seconds " , iRestartDelay ) ;
gameeventmanager - > FireEvent ( event ) ;
}
if ( IsInTournamentMode ( ) = = false )
{
// let the players know
const char * pFormat = NULL ;
if ( mp_restartgame . GetInt ( ) > 0 )
{
if ( ShouldSwitchTeams ( ) )
{
pFormat = ( iRestartDelay > 1 ) ? " #game_switch_in_secs " : " #game_switch_in_sec " ;
}
else if ( ShouldScrambleTeams ( ) )
{
pFormat = ( iRestartDelay > 1 ) ? " #game_scramble_in_secs " : " #game_scramble_in_sec " ;
}
else
{
pFormat = ( iRestartDelay > 1 ) ? " #game_restart_in_secs " : " #game_restart_in_sec " ;
}
}
else if ( mp_restartround . GetInt ( ) > 0 )
{
pFormat = ( iRestartDelay > 1 ) ? " #round_restart_in_secs " : " #round_restart_in_sec " ;
}
if ( pFormat )
{
char strRestartDelay [ 64 ] ;
Q_snprintf ( strRestartDelay , sizeof ( strRestartDelay ) , " %d " , iRestartDelay ) ;
UTIL_ClientPrintAll ( HUD_PRINTCENTER , pFormat , strRestartDelay ) ;
UTIL_ClientPrintAll ( HUD_PRINTCONSOLE , pFormat , strRestartDelay ) ;
}
}
mp_restartround . SetValue ( 0 ) ;
mp_restartgame . SetValue ( 0 ) ;
// cancel any ready restart in progress
m_bAwaitingReadyRestart = false ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : CheckTimeLimit ( void )
{
if ( IsInPreMatch ( ) = = true )
return false ;
if ( ( mp_timelimit . GetInt ( ) > 0 & & CanChangelevelBecauseOfTimeLimit ( ) ) | | m_bChangelevelAfterStalemate )
{
// If there's less than 5 minutes to go, just switch now. This avoids the problem
// of sudden death modes starting shortly after a new round starts.
const int iMinTime = 5 ;
bool bSwitchDueToTime = ( mp_timelimit . GetInt ( ) > iMinTime & & GetTimeLeft ( ) < ( iMinTime * 60 ) ) ;
if ( IsInTournamentMode ( ) = = true )
{
bSwitchDueToTime = false ;
}
if ( GetTimeLeft ( ) < = 0 | | m_bChangelevelAfterStalemate | | bSwitchDueToTime )
{
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_game_over " ) ;
if ( event )
{
event - > SetString ( " reason " , " Reached Time Limit " ) ;
gameeventmanager - > FireEvent ( event ) ;
}
SendTeamScoresEvent ( ) ;
GoToIntermission ( ) ;
return true ;
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : IsGameUnderTimeLimit ( void )
{
return ( mp_timelimit . GetInt ( ) > 0 ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTeamplayRoundBasedRules : : GetTimeLeft ( void )
{
float flTimeLimit = mp_timelimit . GetInt ( ) * 60 ;
float flMapChangeTime = m_flMapResetTime + flTimeLimit ;
// If the round timer is longer, let the round complete
// TFTODO: Do we need to worry about the timelimit running our during a round?
int iTime = ( int ) ( flMapChangeTime - gpGlobals - > curtime ) ;
if ( iTime < 0 )
{
iTime = 0 ;
}
return ( iTime ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : CheckNextLevelCvar ( void )
{
if ( m_bForceMapReset )
{
if ( nextlevel . GetString ( ) & & * nextlevel . GetString ( ) & & engine - > IsMapValid ( nextlevel . GetString ( ) ) )
{
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_game_over " ) ;
if ( event )
{
event - > SetString ( " reason " , " NextLevel CVAR " ) ;
gameeventmanager - > FireEvent ( event ) ;
}
GoToIntermission ( ) ;
return true ;
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : CheckWinLimit ( void )
{
// has one team won the specified number of rounds?
int iWinLimit = mp_winlimit . GetInt ( ) ;
if ( iWinLimit > 0 )
{
for ( int i = LAST_SHARED_TEAM + 1 ; i < GetNumberOfTeams ( ) ; i + + )
{
CTeam * pTeam = GetGlobalTeam ( i ) ;
Assert ( pTeam ) ;
if ( pTeam - > GetScore ( ) > = iWinLimit )
{
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_game_over " ) ;
if ( event )
{
event - > SetString ( " reason " , " Reached Win Limit " ) ;
gameeventmanager - > FireEvent ( event ) ;
}
GoToIntermission ( ) ;
return true ;
}
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : CheckMaxRounds ( )
{
if ( mp_maxrounds . GetInt ( ) > 0 & & IsInPreMatch ( ) = = false )
{
if ( m_nRoundsPlayed > = mp_maxrounds . GetInt ( ) )
{
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_game_over " ) ;
if ( event )
{
event - > SetString ( " reason " , " Reached Round Limit " ) ;
gameeventmanager - > FireEvent ( event ) ;
}
GoToIntermission ( ) ;
return true ;
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Transition ( gamerules_roundstate_t newState )
{
State_Leave ( ) ;
State_Enter ( newState ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Enter ( gamerules_roundstate_t newState )
{
m_iRoundState = newState ;
m_pCurStateInfo = State_LookupInfo ( newState ) ;
if ( mp_showroundtransitions . GetInt ( ) > 0 )
{
if ( m_pCurStateInfo )
Msg ( " Gamerules: entering state '%s' \n " , m_pCurStateInfo - > m_pStateName ) ;
else
Msg ( " Gamerules: entering state #%d \n " , newState ) ;
}
// Initialize the new state.
if ( m_pCurStateInfo & & m_pCurStateInfo - > pfnEnterState )
{
( this - > * m_pCurStateInfo - > pfnEnterState ) ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Leave ( )
{
if ( m_pCurStateInfo & & m_pCurStateInfo - > pfnLeaveState )
{
( this - > * m_pCurStateInfo - > pfnLeaveState ) ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Think ( )
{
if ( m_pCurStateInfo & & m_pCurStateInfo - > pfnThink )
{
( this - > * m_pCurStateInfo - > pfnThink ) ( ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CGameRulesRoundStateInfo * CTeamplayRoundBasedRules : : State_LookupInfo ( gamerules_roundstate_t state )
{
static CGameRulesRoundStateInfo playerStateInfos [ ] =
{
{ GR_STATE_INIT , " GR_STATE_INIT " , & CTeamplayRoundBasedRules : : State_Enter_INIT , NULL , & CTeamplayRoundBasedRules : : State_Think_INIT } ,
{ GR_STATE_PREGAME , " GR_STATE_PREGAME " , & CTeamplayRoundBasedRules : : State_Enter_PREGAME , NULL , & CTeamplayRoundBasedRules : : State_Think_PREGAME } ,
{ GR_STATE_STARTGAME , " GR_STATE_STARTGAME " , & CTeamplayRoundBasedRules : : State_Enter_STARTGAME , NULL , & CTeamplayRoundBasedRules : : State_Think_STARTGAME } ,
{ GR_STATE_PREROUND , " GR_STATE_PREROUND " , & CTeamplayRoundBasedRules : : State_Enter_PREROUND , NULL , & CTeamplayRoundBasedRules : : State_Think_PREROUND } ,
{ GR_STATE_RND_RUNNING , " GR_STATE_RND_RUNNING " , & CTeamplayRoundBasedRules : : State_Enter_RND_RUNNING , NULL , & CTeamplayRoundBasedRules : : State_Think_RND_RUNNING } ,
{ GR_STATE_TEAM_WIN , " GR_STATE_TEAM_WIN " , & CTeamplayRoundBasedRules : : State_Enter_TEAM_WIN , NULL , & CTeamplayRoundBasedRules : : State_Think_TEAM_WIN } ,
{ GR_STATE_RESTART , " GR_STATE_RESTART " , & CTeamplayRoundBasedRules : : State_Enter_RESTART , NULL , & CTeamplayRoundBasedRules : : State_Think_RESTART } ,
{ GR_STATE_STALEMATE , " GR_STATE_STALEMATE " , & CTeamplayRoundBasedRules : : State_Enter_STALEMATE , & CTeamplayRoundBasedRules : : State_Leave_STALEMATE , & CTeamplayRoundBasedRules : : State_Think_STALEMATE } ,
{ GR_STATE_GAME_OVER , " GR_STATE_GAME_OVER " , NULL , NULL , NULL } ,
} ;
2008-09-15 02:50:57 -05:00
for ( size_t i = 0 ; i < ARRAYSIZE ( playerStateInfos ) ; i + + )
2008-09-15 01:07:45 -05:00
{
if ( playerStateInfos [ i ] . m_iRoundState = = state )
return & playerStateInfos [ i ] ;
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Enter_INIT ( void )
{
InitTeams ( ) ;
ResetMapTime ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Think_INIT ( void )
{
State_Transition ( GR_STATE_PREGAME ) ;
}
//-----------------------------------------------------------------------------
// Purpose: The server is idle and waiting for enough players to start up again.
// When we find an active player go to GR_STATE_STARTGAME.
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Enter_PREGAME ( void )
{
m_flNextPeriodicThink = gpGlobals - > curtime + 0.1 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Think_PREGAME ( void )
{
CheckRespawnWaves ( ) ;
// we'll just stay in pregame for the bugbait reports
if ( IsLoadingBugBaitReport ( ) | | gpGlobals - > eLoadType = = MapLoad_Background )
return ;
// Commentary stays in this mode too
if ( IsInCommentaryMode ( ) )
return ;
if ( CountActivePlayers ( ) > 0 )
{
State_Transition ( GR_STATE_STARTGAME ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Wait a bit and then spawn everyone into the preround
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Enter_STARTGAME ( void )
{
m_flStateTransitionTime = gpGlobals - > curtime ;
m_bInitialSpawn = true ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Think_STARTGAME ( )
{
if ( gpGlobals - > curtime > m_flStateTransitionTime )
{
if ( mp_waitingforplayers_time . GetFloat ( ) > 0 )
{
// go into waitingforplayers, reset at end of it
SetInWaitingForPlayers ( true ) ;
}
State_Transition ( GR_STATE_PREROUND ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Enter_PREROUND ( void )
{
m_flStateTransitionTime = gpGlobals - > curtime + 5 * mp_enableroundwaittime . GetFloat ( ) ;
BalanceTeams ( false ) ;
m_flStartBalancingTeamsAt = gpGlobals - > curtime + 60.0 ;
// Reset everything in the level
RoundRespawn ( ) ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_round_start " ) ;
if ( event )
{
event - > SetBool ( " full_reset " , m_bForceMapReset ) ;
gameeventmanager - > FireEvent ( event ) ;
}
StopWatchModeThink ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Think_PREROUND ( void )
{
if ( gpGlobals - > curtime > m_flStateTransitionTime )
{
State_Transition ( GR_STATE_RND_RUNNING ) ;
}
CheckRespawnWaves ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Enter_RND_RUNNING ( void )
{
SetupOnRoundRunning ( ) ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_round_active " ) ;
if ( event )
{
gameeventmanager - > FireEvent ( event ) ;
}
if ( ! IsInWaitingForPlayers ( ) )
{
PlayStartRoundVoice ( ) ;
}
m_bChangeLevelOnRoundEnd = false ;
m_bPrevRoundWasWaitingForPlayers = false ;
m_flNextBalanceTeamsTime = gpGlobals - > curtime + 1.0f ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Think_RND_RUNNING ( void )
{
//if we don't find any active players, return to GR_STATE_PREGAME
if ( CountActivePlayers ( ) < = 0 )
{
State_Transition ( GR_STATE_PREGAME ) ;
return ;
}
if ( m_flNextBalanceTeamsTime < gpGlobals - > curtime )
{
BalanceTeams ( true ) ;
m_flNextBalanceTeamsTime = gpGlobals - > curtime + 1.0f ;
}
CheckRespawnWaves ( ) ;
// check round restart
if ( m_flRestartRoundTime > 0 & & m_flRestartRoundTime < gpGlobals - > curtime & & ! g_pServerBenchmark - > IsBenchmarkRunning ( ) )
{
// time to restart!
State_Transition ( GR_STATE_RESTART ) ;
m_flRestartRoundTime = - 1 ;
}
// check ready restart
if ( m_bAwaitingReadyRestart )
{
bool bTeamNotReady = false ;
for ( int i = LAST_SHARED_TEAM + 1 ; i < GetNumberOfTeams ( ) ; i + + )
{
if ( ! m_bTeamReady [ i ] )
{
bTeamNotReady = true ;
break ;
}
}
if ( ! bTeamNotReady )
{
//State_Transition( GR_STATE_RESTART );
mp_restartgame . SetValue ( 5 ) ;
m_bAwaitingReadyRestart = false ;
ShouldResetScores ( true , true ) ;
ShouldResetRoundsPlayed ( true ) ;
}
}
bool bAllowStalemateAtTimelimit = m_bAllowStalemateAtTimelimit | | ( ! IsValveMap ( ) & & mp_stalemate_at_timelimit . GetBool ( ) ) ;
// See if we're coming up to the server timelimit, in which case force a stalemate immediately.
if ( State_Get ( ) = = GR_STATE_RND_RUNNING & & bAllowStalemateAtTimelimit & & mp_timelimit . GetInt ( ) > 0 & & IsInPreMatch ( ) = = false )
{
if ( GetTimeLeft ( ) < = 0.5 )
{
int iDrawScoreCheck = - 1 ;
int iWinningTeam = 0 ;
bool bTeamsAreDrawn = true ;
for ( int i = FIRST_GAME_TEAM ; ( i < GetNumberOfTeams ( ) ) & & bTeamsAreDrawn ; i + + )
{
int iTeamScore = GetGlobalTeam ( i ) - > GetScore ( ) ;
if ( iTeamScore > iDrawScoreCheck )
{
iWinningTeam = i ;
}
if ( iTeamScore ! = iDrawScoreCheck )
{
if ( iDrawScoreCheck = = - 1 )
{
iDrawScoreCheck = iTeamScore ;
}
else
{
bTeamsAreDrawn = false ;
}
}
}
if ( bTeamsAreDrawn )
{
if ( CanGoToStalemate ( ) )
{
m_bChangelevelAfterStalemate = true ;
SetStalemate ( STALEMATE_SERVER_TIMELIMIT , m_bForceMapReset ) ;
}
else
{
SetOvertime ( true ) ;
}
}
else
{
SetWinningTeam ( iWinningTeam , WINREASON_TIMELIMIT , true , false , true ) ;
}
}
}
StopWatchModeThink ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Enter_TEAM_WIN ( void )
{
2011-04-28 01:30:37 -05:00
float flTime = MAX ( 5 , mp_bonusroundtime . GetFloat ( ) ) ;
2008-09-15 01:07:45 -05:00
m_flStateTransitionTime = gpGlobals - > curtime + flTime * mp_enableroundwaittime . GetFloat ( ) ;
// if we're forcing the map to reset it must be the end of a "full" round not a mini-round
if ( m_bForceMapReset )
{
m_nRoundsPlayed + + ;
}
InternalHandleTeamWin ( m_iWinningTeam ) ;
SendWinPanelInfo ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Think_TEAM_WIN ( void )
{
if ( gpGlobals - > curtime > m_flStateTransitionTime )
{
bool bDone = ! ( ! CheckTimeLimit ( ) & & ! CheckWinLimit ( ) & & ! CheckMaxRounds ( ) & & ! CheckNextLevelCvar ( ) ) ;
// check the win limit, max rounds, time limit and nextlevel cvar before starting the next round
if ( bDone = = false )
{
PreviousRoundEnd ( ) ;
State_Transition ( GR_STATE_PREROUND ) ;
}
else if ( IsInTournamentMode ( ) = = true )
{
for ( int i = 1 ; i < = MAX_PLAYERS ; i + + )
{
CBasePlayer * pPlayer = UTIL_PlayerByIndex ( i ) ;
if ( ! pPlayer )
continue ;
pPlayer - > ShowViewPortPanel ( PANEL_SCOREBOARD ) ;
}
RestartTournament ( ) ;
State_Transition ( GR_STATE_RND_RUNNING ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Enter_STALEMATE ( void )
{
SetupOnStalemateStart ( ) ;
m_flStalemateStartTime = gpGlobals - > curtime ;
// Stop any timers, and bring up a new one
HideActiveTimer ( ) ;
variant_t sVariant ;
if ( ! m_hStalemateTimer )
{
m_hStalemateTimer = ( CTeamRoundTimer * ) CBaseEntity : : Create ( " team_round_timer " , vec3_origin , vec3_angle ) ;
}
m_hStalemateTimer - > KeyValue ( " show_in_hud " , " 1 " ) ;
sVariant . SetInt ( mp_stalemate_timelimit . GetInt ( ) ) ;
m_hStalemateTimer - > AcceptInput ( " SetTime " , NULL , NULL , sVariant , 0 ) ;
m_hStalemateTimer - > AcceptInput ( " Resume " , NULL , NULL , sVariant , 0 ) ;
m_hStalemateTimer - > AcceptInput ( " Enable " , NULL , NULL , sVariant , 0 ) ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_update_timer " ) ;
if ( event )
{
gameeventmanager - > FireEvent ( event ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Leave_STALEMATE ( void )
{
SetupOnStalemateEnd ( ) ;
if ( m_hStalemateTimer )
{
UTIL_Remove ( m_hStalemateTimer ) ;
}
RestoreActiveTimer ( ) ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_update_timer " ) ;
if ( event )
{
gameeventmanager - > FireEvent ( event ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : HideActiveTimer ( void )
{
// We can't handle this, because we won't be able to restore multiple timers
Assert ( m_hPreviousActiveTimer . Get ( ) = = NULL ) ;
m_hPreviousActiveTimer = NULL ;
CBaseEntity * pEntity = NULL ;
variant_t sVariant ;
sVariant . SetInt ( false ) ;
while ( ( pEntity = gEntList . FindEntityByClassname ( pEntity , " team_round_timer " ) ) ! = NULL )
{
CTeamRoundTimer * pTimer = assert_cast < CTeamRoundTimer * > ( pEntity ) ;
if ( pTimer & & pTimer - > ShowInHud ( ) )
{
Assert ( ! m_hPreviousActiveTimer ) ;
m_hPreviousActiveTimer = pTimer ;
pEntity - > AcceptInput ( " ShowInHUD " , NULL , NULL , sVariant , 0 ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : RestoreActiveTimer ( void )
{
if ( m_hPreviousActiveTimer )
{
variant_t sVariant ;
sVariant . SetInt ( true ) ;
m_hPreviousActiveTimer - > AcceptInput ( " ShowInHUD " , NULL , NULL , sVariant , 0 ) ;
m_hPreviousActiveTimer = NULL ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Think_STALEMATE ( void )
{
//if we don't find any active players, return to GR_STATE_PREGAME
if ( CountActivePlayers ( ) < = 0 )
{
State_Transition ( GR_STATE_PREGAME ) ;
return ;
}
int iDeadTeam = TEAM_UNASSIGNED ;
int iAliveTeam = TEAM_UNASSIGNED ;
// If a team is fully killed, the other team has won
for ( int i = LAST_SHARED_TEAM + 1 ; i < GetNumberOfTeams ( ) ; i + + )
{
CTeam * pTeam = GetGlobalTeam ( i ) ;
Assert ( pTeam ) ;
int iPlayers = pTeam - > GetNumPlayers ( ) ;
if ( iPlayers )
{
bool bFoundLiveOne = false ;
for ( int player = 0 ; player < iPlayers ; player + + )
{
if ( pTeam - > GetPlayer ( player ) & & pTeam - > GetPlayer ( player ) - > IsAlive ( ) )
{
bFoundLiveOne = true ;
break ;
}
}
if ( bFoundLiveOne )
{
iAliveTeam = i ;
}
else
{
iDeadTeam = i ;
}
}
}
if ( iDeadTeam & & iAliveTeam )
{
// The live team has won.
bool bMasterHandled = false ;
if ( ! m_bForceMapReset )
{
// We're not resetting the map, so give the winners control
// of all the points that were in play this round.
// Find the control point master.
CTeamControlPointMaster * pMaster = g_hControlPointMasters . Count ( ) ? g_hControlPointMasters [ 0 ] : NULL ;
if ( pMaster )
{
variant_t sVariant ;
sVariant . SetInt ( iAliveTeam ) ;
pMaster - > AcceptInput ( " SetWinnerAndForceCaps " , NULL , NULL , sVariant , 0 ) ;
bMasterHandled = true ;
}
}
if ( ! bMasterHandled )
{
SetWinningTeam ( iAliveTeam , WINREASON_OPPONENTS_DEAD , m_bForceMapReset ) ;
}
}
else if ( iDeadTeam & & iAliveTeam = = TEAM_UNASSIGNED | |
( m_hStalemateTimer & & TimerMayExpire ( ) & & m_hStalemateTimer - > GetTimeRemaining ( ) < = 0 ) )
{
bool bFullReset = true ;
CTeamControlPointMaster * pMaster = g_hControlPointMasters . Count ( ) ? g_hControlPointMasters [ 0 ] : NULL ;
if ( pMaster & & pMaster - > PlayingMiniRounds ( ) )
{
// we don't need to do a full map reset for maps with mini-rounds
bFullReset = false ;
}
// Both teams are dead. Pure stalemate.
SetWinningTeam ( TEAM_UNASSIGNED , WINREASON_STALEMATE , bFullReset , false ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: manual restart
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Enter_RESTART ( void )
{
// send scores
SendTeamScoresEvent ( ) ;
// send restart event
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_restart_round " ) ;
if ( event )
{
gameeventmanager - > FireEvent ( event ) ;
}
m_bPrevRoundWasWaitingForPlayers = m_bInWaitingForPlayers ;
SetInWaitingForPlayers ( false ) ;
ResetScores ( ) ;
// reset the round time
ResetMapTime ( ) ;
State_Transition ( GR_STATE_PREROUND ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : State_Think_RESTART ( void )
{
// should never get here, State_Enter_RESTART sets us into a different state
Assert ( 0 ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Input for other entities to declare a round winner.
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : SetWinningTeam ( int team , int iWinReason , bool bForceMapReset /* = true */ , bool bSwitchTeams /* = false*/ , bool bDontAddScore /* = false*/ )
{
// Commentary doesn't let anyone win
if ( IsInCommentaryMode ( ) )
return ;
if ( ( team ! = TEAM_UNASSIGNED ) & & ( team < = LAST_SHARED_TEAM | | team > = GetNumberOfTeams ( ) ) )
{
Assert ( ! " SetWinningTeam() called with invalid team. " ) ;
return ;
}
// are we already in this state?
if ( State_Get ( ) = = GR_STATE_TEAM_WIN )
return ;
SetForceMapReset ( bForceMapReset ) ;
SetSwitchTeams ( bSwitchTeams ) ;
m_iWinningTeam = team ;
m_iWinReason = iWinReason ;
PlayWinSong ( team ) ;
// only reward the team if they have won the map and we're going to do a full reset or the time has run out and we're changing maps
bool bRewardTeam = bForceMapReset | | ( IsGameUnderTimeLimit ( ) & & ( GetTimeLeft ( ) < = 0 ) ) ;
if ( bDontAddScore = = true )
{
bRewardTeam = false ;
}
m_bUseAddScoreAnim = false ;
if ( bRewardTeam & & ( team ! = TEAM_UNASSIGNED ) & & ShouldScorePerRound ( ) )
{
GetGlobalTeam ( team ) - > AddScore ( TEAMPLAY_ROUND_WIN_SCORE ) ;
m_bUseAddScoreAnim = true ;
}
// this was a sudden death win if we were in stalemate then a team won it
bool bWasSuddenDeath = ( InStalemate ( ) & & m_iWinningTeam > = FIRST_GAME_TEAM ) ;
State_Transition ( GR_STATE_TEAM_WIN ) ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_round_win " ) ;
if ( event )
{
event - > SetInt ( " team " , team ) ;
event - > SetBool ( " full_round " , bForceMapReset ) ;
event - > SetFloat ( " round_time " , gpGlobals - > curtime - m_flRoundStartTime ) ;
event - > SetBool ( " was_sudden_death " , bWasSuddenDeath ) ;
// let derived classes add more fields to the event
FillOutTeamplayRoundWinEvent ( event ) ;
gameeventmanager - > FireEvent ( event ) ;
}
// send team scores
SendTeamScoresEvent ( ) ;
if ( team = = TEAM_UNASSIGNED )
{
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBaseMultiplayerPlayer * pPlayer = ToBaseMultiplayerPlayer ( UTIL_PlayerByIndex ( i ) ) ;
if ( ! pPlayer )
continue ;
pPlayer - > SpeakConceptIfAllowed ( MP_CONCEPT_STALEMATE ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Input for other entities to declare a stalemate
// Most often a team_control_point_master saying that the
// round timer expired
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : SetStalemate ( int iReason , bool bForceMapReset /* = true */ , bool bSwitchTeams /* = false */ )
{
if ( IsInTournamentMode ( ) = = true & & IsInPreMatch ( ) = = true )
return ;
if ( ! mp_stalemate_enable . GetBool ( ) )
{
SetWinningTeam ( TEAM_UNASSIGNED , WINREASON_STALEMATE , bForceMapReset , bSwitchTeams ) ;
return ;
}
if ( InStalemate ( ) )
return ;
SetForceMapReset ( bForceMapReset ) ;
m_iWinningTeam = TEAM_UNASSIGNED ;
PlaySuddenDeathSong ( ) ;
State_Transition ( GR_STATE_STALEMATE ) ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_round_stalemate " ) ;
if ( event )
{
event - > SetInt ( " reason " , iReason ) ;
gameeventmanager - > FireEvent ( event ) ;
}
}
# ifdef GAME_DLL
void CC_CH_ForceRespawn ( void )
{
CTeamplayRoundBasedRules * pRules = dynamic_cast < CTeamplayRoundBasedRules * > ( GameRules ( ) ) ;
if ( pRules )
{
pRules - > RespawnPlayers ( true ) ;
}
}
static ConCommand mp_forcerespawnplayers ( " mp_forcerespawnplayers " , CC_CH_ForceRespawn , " Force all players to respawn. " , FCVAR_CHEAT ) ;
void CC_CH_TournamentRestart ( void )
{
CTeamplayRoundBasedRules * pRules = dynamic_cast < CTeamplayRoundBasedRules * > ( GameRules ( ) ) ;
if ( pRules )
{
pRules - > RestartTournament ( ) ;
}
}
static ConCommand mp_tournament_restart ( " mp_tournament_restart " , CC_CH_TournamentRestart , " Restart Tournament Mode on the current level. " ) ;
void CTeamplayRoundBasedRules : : RestartTournament ( void )
{
if ( IsInTournamentMode ( ) = = false )
return ;
SetInWaitingForPlayers ( true ) ;
m_bAwaitingReadyRestart = true ;
m_flStopWatchTotalTime = - 1.0f ;
m_bStopWatch = false ;
for ( int i = 0 ; i < MAX_TEAMS ; i + + )
{
m_bTeamReady . Set ( i , false ) ;
}
}
# endif
//-----------------------------------------------------------------------------
// Purpose:
// Input : bForceRespawn - respawn player even if dead or dying
// bTeam - if true, only respawn the passed team
// iTeam - team to respawn
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : RespawnPlayers ( bool bForceRespawn , bool bTeam /* = false */ , int iTeam /* = TEAM_UNASSIGNED */ )
{
if ( bTeam )
{
Assert ( iTeam > LAST_SHARED_TEAM & & iTeam < GetNumberOfTeams ( ) ) ;
}
int iPlayersSpawned = 0 ;
CBasePlayer * pPlayer ;
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
pPlayer = ToBasePlayer ( UTIL_PlayerByIndex ( i ) ) ;
if ( ! pPlayer )
continue ;
// Check for team specific spawn
if ( bTeam & & pPlayer - > GetTeamNumber ( ) ! = iTeam )
continue ;
// players that haven't chosen a team/class can never spawn
if ( ! pPlayer - > IsReadyToPlay ( ) )
{
// Let the player spawn immediately when they do pick a class
if ( pPlayer - > ShouldGainInstantSpawn ( ) )
{
pPlayer - > AllowInstantSpawn ( ) ;
}
continue ;
}
// If we aren't force respawning, don't respawn players that:
// - are alive
// - are still in the death anim stage of dying
if ( ! bForceRespawn )
{
if ( pPlayer - > IsAlive ( ) )
continue ;
if ( m_iRoundState ! = GR_STATE_PREROUND )
{
// If the player hasn't been dead the minimum respawn time, he
// waits until the next wave.
if ( bTeam & & ! HasPassedMinRespawnTime ( pPlayer ) )
continue ;
if ( ! pPlayer - > IsReadyToSpawn ( ) )
{
// Let the player spawn immediately when they do pick a class
if ( pPlayer - > ShouldGainInstantSpawn ( ) )
{
pPlayer - > AllowInstantSpawn ( ) ;
}
continue ;
}
}
}
// Respawn this player
pPlayer - > ForceRespawn ( ) ;
iPlayersSpawned + + ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : InitTeams ( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTeamplayRoundBasedRules : : CountActivePlayers ( void )
{
int i ;
int count = 0 ;
CBasePlayer * pPlayer ;
for ( i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
pPlayer = ToBasePlayer ( UTIL_PlayerByIndex ( i ) ) ;
if ( pPlayer )
{
if ( pPlayer - > IsReadyToPlay ( ) )
{
count + + ;
}
}
}
return count ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : HandleTimeLimitChange ( void )
{
// check that we have an active timer in the HUD and use mp_timelimit if we don't
if ( ! MapHasActiveTimer ( ) & & ( mp_timelimit . GetInt ( ) > 0 & & GetTimeLeft ( ) > 0 ) )
{
CreateTimeLimitTimer ( ) ;
}
else
{
if ( m_hTimeLimitTimer )
{
UTIL_Remove ( m_hTimeLimitTimer ) ;
m_hTimeLimitTimer = NULL ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : MapHasActiveTimer ( void )
{
CBaseEntity * pEntity = NULL ;
while ( ( pEntity = gEntList . FindEntityByClassname ( pEntity , " team_round_timer " ) ) ! = NULL )
{
CTeamRoundTimer * pTimer = assert_cast < CTeamRoundTimer * > ( pEntity ) ;
if ( pTimer & & pTimer - > ShowInHud ( ) & & ( Q_stricmp ( STRING ( pTimer - > GetEntityName ( ) ) , " zz_teamplay_timelimit_timer " ) ! = 0 ) )
{
return true ;
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : CreateTimeLimitTimer ( void )
{
if ( ! m_hTimeLimitTimer )
{
m_hTimeLimitTimer = ( CTeamRoundTimer * ) CBaseEntity : : Create ( " team_round_timer " , vec3_origin , vec3_angle ) ;
m_hTimeLimitTimer - > SetName ( MAKE_STRING ( " zz_teamplay_timelimit_timer " ) ) ;
}
variant_t sVariant ;
m_hTimeLimitTimer - > KeyValue ( " show_in_hud " , " 1 " ) ;
sVariant . SetInt ( GetTimeLeft ( ) ) ;
m_hTimeLimitTimer - > AcceptInput ( " SetTime " , NULL , NULL , sVariant , 0 ) ;
m_hTimeLimitTimer - > AcceptInput ( " Resume " , NULL , NULL , sVariant , 0 ) ;
m_hTimeLimitTimer - > AcceptInput ( " Enable " , NULL , NULL , sVariant , 0 ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : RoundRespawn ( void )
{
m_flRoundStartTime = gpGlobals - > curtime ;
if ( m_bForceMapReset | | m_bPrevRoundWasWaitingForPlayers )
{
CleanUpMap ( ) ;
// clear out the previously played rounds
m_iszPreviousRounds . RemoveAll ( ) ;
if ( mp_timelimit . GetInt ( ) > 0 & & GetTimeLeft ( ) > 0 )
{
// check that we have an active timer in the HUD and use mp_timelimit if we don't
if ( ! MapHasActiveTimer ( ) )
{
CreateTimeLimitTimer ( ) ;
}
}
m_iLastCapPointChanged = 0 ;
}
// reset our spawn times to the original values
for ( int i = 0 ; i < MAX_TEAMS ; i + + )
{
if ( m_flOriginalTeamRespawnWaveTime [ i ] > = 0 )
{
m_TeamRespawnWaveTimes . Set ( i , m_flOriginalTeamRespawnWaveTime [ i ] ) ;
}
}
if ( ! IsInWaitingForPlayers ( ) )
{
if ( m_bForceMapReset )
{
UTIL_LogPrintf ( " World triggered \" Round_Start \" \n " ) ;
}
}
// Setup before respawning players, so we can mess with spawnpoints
SetupOnRoundStart ( ) ;
// Do we need to switch the teams?
m_bSwitchedTeamsThisRound = false ;
if ( ShouldSwitchTeams ( ) )
{
m_bSwitchedTeamsThisRound = true ;
HandleSwitchTeams ( ) ;
SetSwitchTeams ( false ) ;
}
// Do we need to switch the teams?
if ( ShouldScrambleTeams ( ) )
{
HandleScrambleTeams ( ) ;
SetScrambleTeams ( false ) ;
}
RespawnPlayers ( true ) ;
// reset per-round scores for each player
for ( int i = 1 ; i < = MAX_PLAYERS ; i + + )
{
CBasePlayer * pPlayer = ToBasePlayer ( UTIL_PlayerByIndex ( i ) ) ;
if ( pPlayer )
{
pPlayer - > ResetPerRoundStats ( ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Recreate all the map entities from the map data (preserving their indices),
// then remove everything else except the players.
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : CleanUpMap ( )
{
if ( mp_showcleanedupents . GetInt ( ) )
{
Msg ( " CleanUpMap \n =============== \n " ) ;
Msg ( " Entities: %d (%d edicts) \n " , gEntList . NumberOfEntities ( ) , gEntList . NumberOfEdicts ( ) ) ;
}
// Get rid of all entities except players.
CBaseEntity * pCur = gEntList . FirstEnt ( ) ;
while ( pCur )
{
if ( ! RoundCleanupShouldIgnore ( pCur ) )
{
if ( mp_showcleanedupents . GetInt ( ) & 1 )
{
Msg ( " Removed Entity: %s \n " , pCur - > GetClassname ( ) ) ;
}
UTIL_Remove ( pCur ) ;
}
pCur = gEntList . NextEnt ( pCur ) ;
}
// Clear out the event queue
g_EventQueue . Clear ( ) ;
// Really remove the entities so we can have access to their slots below.
gEntList . CleanupDeleteList ( ) ;
engine - > AllowImmediateEdictReuse ( ) ;
if ( mp_showcleanedupents . GetInt ( ) & 2 )
{
Msg ( " Entities Left: \n " ) ;
pCur = gEntList . FirstEnt ( ) ;
while ( pCur )
{
Msg ( " %s (%d) \n " , pCur - > GetClassname ( ) , pCur - > entindex ( ) ) ;
pCur = gEntList . NextEnt ( pCur ) ;
}
}
// Now reload the map entities.
class CTeamplayMapEntityFilter : public IMapEntityFilter
{
public :
CTeamplayMapEntityFilter ( )
{
m_pRules = assert_cast < CTeamplayRoundBasedRules * > ( GameRules ( ) ) ;
}
virtual bool ShouldCreateEntity ( const char * pClassname )
{
// Don't recreate the preserved entities.
if ( m_pRules - > ShouldCreateEntity ( pClassname ) )
return true ;
// Increment our iterator since it's not going to call CreateNextEntity for this ent.
if ( m_iIterator ! = g_MapEntityRefs . InvalidIndex ( ) )
{
m_iIterator = g_MapEntityRefs . Next ( m_iIterator ) ;
}
return false ;
}
virtual CBaseEntity * CreateNextEntity ( const char * pClassname )
{
if ( m_iIterator = = g_MapEntityRefs . InvalidIndex ( ) )
{
// This shouldn't be possible. When we loaded the map, it should have used
// CTeamplayMapEntityFilter, which should have built the g_MapEntityRefs list
// with the same list of entities we're referring to here.
Assert ( false ) ;
return NULL ;
}
else
{
CMapEntityRef & ref = g_MapEntityRefs [ m_iIterator ] ;
m_iIterator = g_MapEntityRefs . Next ( m_iIterator ) ; // Seek to the next entity.
if ( ref . m_iEdict = = - 1 | | engine - > PEntityOfEntIndex ( ref . m_iEdict ) )
{
// Doh! The entity was delete and its slot was reused.
// Just use any old edict slot. This case sucks because we lose the baseline.
return CreateEntityByName ( pClassname ) ;
}
else
{
// Cool, the slot where this entity was is free again (most likely, the entity was
// freed above). Now create an entity with this specific index.
return CreateEntityByName ( pClassname , ref . m_iEdict ) ;
}
}
}
public :
int m_iIterator ; // Iterator into g_MapEntityRefs.
CTeamplayRoundBasedRules * m_pRules ;
} ;
CTeamplayMapEntityFilter filter ;
filter . m_iIterator = g_MapEntityRefs . Head ( ) ;
// DO NOT CALL SPAWN ON info_node ENTITIES!
MapEntity_ParseAllEntities ( engine - > GetMapEntitiesString ( ) , & filter , true ) ;
}
// Utility function
bool FindInList ( const char * * pStrings , const char * pToFind )
{
int i = 0 ;
while ( pStrings [ i ] [ 0 ] ! = 0 )
{
if ( Q_stricmp ( pStrings [ i ] , pToFind ) = = 0 )
return true ;
i + + ;
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : ShouldCreateEntity ( const char * pszClassName )
{
return ! FindInList ( s_PreserveEnts , pszClassName ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : RoundCleanupShouldIgnore ( CBaseEntity * pEnt )
{
return FindInList ( s_PreserveEnts , pEnt - > GetClassname ( ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Sort function for sorting players by time spent connected ( user ID )
//-----------------------------------------------------------------------------
static int SwitchPlayersSort ( CBaseMultiplayerPlayer * const * p1 , CBaseMultiplayerPlayer * const * p2 )
{
// sort by score
return ( ( * p2 ) - > GetTeamBalanceScore ( ) - ( * p1 ) - > GetTeamBalanceScore ( ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : CheckRespawnWaves ( void )
{
for ( int team = LAST_SHARED_TEAM + 1 ; team < GetNumberOfTeams ( ) ; team + + )
{
if ( m_flNextRespawnWave [ team ] > gpGlobals - > curtime )
continue ;
RespawnTeam ( team ) ;
m_flNextRespawnWave . Set ( team , gpGlobals - > curtime + GetRespawnWaveMaxLength ( team ) ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Return true if the teams are balanced after this function
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : BalanceTeams ( bool bRequireSwitcheesToBeDead )
{
if ( mp_autoteambalance . GetBool ( ) = = false )
{
return ;
}
// we don't balance for a period of time at the start of the game
if ( gpGlobals - > curtime < m_flStartBalancingTeamsAt )
{
return ;
}
// wrap with this bool, indicates it's a round running switch and not a between rounds insta-switch
if ( bRequireSwitcheesToBeDead )
{
// we don't balance if there is less than 60 seconds on the active timer
CTeamRoundTimer * pActiveTimer = GetActiveRoundTimer ( ) ;
if ( pActiveTimer & & pActiveTimer - > GetTimeRemaining ( ) < 60 )
{
return ;
}
}
int iHeaviestTeam = TEAM_UNASSIGNED , iLightestTeam = TEAM_UNASSIGNED ;
// Figure out if we're unbalanced
if ( ! AreTeamsUnbalanced ( iHeaviestTeam , iLightestTeam ) )
{
m_flFoundUnbalancedTeamsTime = - 1 ;
m_bPrintedUnbalanceWarning = false ;
return ;
}
if ( m_flFoundUnbalancedTeamsTime < 0 )
{
m_flFoundUnbalancedTeamsTime = gpGlobals - > curtime ;
}
// if teams have been unbalanced for X seconds, play a warning
if ( ! m_bPrintedUnbalanceWarning & & ( ( gpGlobals - > curtime - m_flFoundUnbalancedTeamsTime ) > 1.0 ) )
{
// print unbalance warning
UTIL_ClientPrintAll ( HUD_PRINTTALK , " #game_auto_team_balance_in " , " 5 " ) ;
m_bPrintedUnbalanceWarning = true ;
}
// teams are unblanced, figure out some players that need to be switched
CTeam * pHeavyTeam = GetGlobalTeam ( iHeaviestTeam ) ;
CTeam * pLightTeam = GetGlobalTeam ( iLightestTeam ) ;
Assert ( pHeavyTeam & & pLightTeam ) ;
int iNumSwitchesRequired = ( pHeavyTeam - > GetNumPlayers ( ) - pLightTeam - > GetNumPlayers ( ) ) / 2 ;
// sort the eligible players and switch the n best candidates
CUtlVector < CBaseMultiplayerPlayer * > vecPlayers ;
CBaseMultiplayerPlayer * pPlayer ;
int iScore ;
int i ;
for ( i = 0 ; i < pHeavyTeam - > GetNumPlayers ( ) ; i + + )
{
pPlayer = ToBaseMultiplayerPlayer ( pHeavyTeam - > GetPlayer ( i ) ) ;
if ( ! pPlayer )
continue ;
// calculate a score for this player. higher is more likely to be switched
iScore = pPlayer - > CalculateTeamBalanceScore ( ) ;
pPlayer - > SetTeamBalanceScore ( iScore ) ;
vecPlayers . AddToTail ( pPlayer ) ;
}
// sort the vector
vecPlayers . Sort ( SwitchPlayersSort ) ;
int iNumEligibleSwitchees = iNumSwitchesRequired + 2 ;
for ( int i = 0 ; i < vecPlayers . Count ( ) & & iNumSwitchesRequired > 0 & & i < iNumEligibleSwitchees ; i + + )
{
pPlayer = vecPlayers . Element ( i ) ;
Assert ( pPlayer ) ;
if ( ! pPlayer )
continue ;
if ( bRequireSwitcheesToBeDead = = false | | ! pPlayer - > IsAlive ( ) )
{
pPlayer - > ChangeTeam ( iLightestTeam ) ;
pPlayer - > SetLastForcedChangeTeamTimeToNow ( ) ;
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_teambalanced_player " ) ;
if ( event )
{
event - > SetInt ( " player " , pPlayer - > entindex ( ) ) ;
event - > SetInt ( " team " , iLightestTeam ) ;
gameeventmanager - > FireEvent ( event ) ;
}
// tell people that we've switched this player
UTIL_ClientPrintAll ( HUD_PRINTTALK , " #game_player_was_team_balanced " , pPlayer - > GetPlayerName ( ) ) ;
iNumSwitchesRequired - - ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : ResetScores ( void )
{
if ( m_bResetTeamScores )
{
for ( int i = 0 ; i < GetNumberOfTeams ( ) ; i + + )
{
GetGlobalTeam ( i ) - > ResetScores ( ) ;
}
}
if ( m_bResetPlayerScores )
{
CBasePlayer * pPlayer ;
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
pPlayer = ToBasePlayer ( UTIL_PlayerByIndex ( i ) ) ;
if ( pPlayer = = NULL )
continue ;
if ( FNullEnt ( pPlayer - > edict ( ) ) )
continue ;
pPlayer - > ResetScores ( ) ;
}
}
if ( m_bResetRoundsPlayed )
{
m_nRoundsPlayed = 0 ;
}
// assume we always want to reset the scores
// unless someone tells us not to for the next reset
m_bResetTeamScores = true ;
m_bResetPlayerScores = true ;
m_bResetRoundsPlayed = true ;
//m_flStopWatchTime = -1.0f;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : ResetMapTime ( void )
{
m_flMapResetTime = gpGlobals - > curtime ;
// send an event with the time remaining until map change
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_map_time_remaining " ) ;
if ( event )
{
event - > SetInt ( " seconds " , GetTimeLeft ( ) ) ;
gameeventmanager - > FireEvent ( event ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : PlayStartRoundVoice ( void )
{
for ( int i = LAST_SHARED_TEAM + 1 ; i < GetNumberOfTeams ( ) ; i + + )
{
BroadcastSound ( i , UTIL_VarArgs ( " Game.TeamRoundStart%d " , i ) ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : PlayWinSong ( int team )
{
if ( team = = TEAM_UNASSIGNED )
{
PlayStalemateSong ( ) ;
}
else
{
BroadcastSound ( TEAM_UNASSIGNED , UTIL_VarArgs ( " Game.TeamWin%d " , team ) ) ;
for ( int i = FIRST_GAME_TEAM ; i < GetNumberOfTeams ( ) ; i + + )
{
if ( i = = team )
{
BroadcastSound ( i , " Game.YourTeamWon " ) ;
}
else
{
BroadcastSound ( i , " Game.YourTeamLost " ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : PlaySuddenDeathSong ( void )
{
BroadcastSound ( TEAM_UNASSIGNED , " Game.SuddenDeath " ) ;
for ( int i = FIRST_GAME_TEAM ; i < GetNumberOfTeams ( ) ; i + + )
{
BroadcastSound ( i , " Game.SuddenDeath " ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : PlayStalemateSong ( void )
{
BroadcastSound ( TEAM_UNASSIGNED , " Game.Stalemate " ) ;
for ( int i = FIRST_GAME_TEAM ; i < GetNumberOfTeams ( ) ; i + + )
{
BroadcastSound ( i , " Game.Stalemate " ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : BroadcastSound ( int iTeam , const char * sound )
{
//send it to everyone
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_broadcast_audio " ) ;
if ( event )
{
event - > SetInt ( " team " , iTeam ) ;
event - > SetString ( " sound " , sound ) ;
gameeventmanager - > FireEvent ( event ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : AddPlayedRound ( string_t strName )
{
if ( strName ! = NULL_STRING )
{
m_iszPreviousRounds . AddToHead ( strName ) ;
// we only need to store the last two rounds that we've played
if ( m_iszPreviousRounds . Count ( ) > 2 )
{
// remove all but two of the entries (should only ever have to remove 1 when we're at 3)
for ( int i = m_iszPreviousRounds . Count ( ) - 1 ; i > 1 ; i - - )
{
m_iszPreviousRounds . Remove ( i ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : IsPreviouslyPlayedRound ( string_t strName )
{
return ( m_iszPreviousRounds . Find ( strName ) ! = m_iszPreviousRounds . InvalidIndex ( ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
string_t CTeamplayRoundBasedRules : : GetLastPlayedRound ( void )
{
return ( m_iszPreviousRounds . Count ( ) ? m_iszPreviousRounds [ 0 ] : NULL_STRING ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CTeamRoundTimer * CTeamplayRoundBasedRules : : GetActiveRoundTimer ( void )
{
# ifdef TF_DLL
int iTimerEntIndex = ObjectiveResource ( ) - > GetTimerInHUD ( ) ;
return ( dynamic_cast < CTeamRoundTimer * > ( UTIL_EntityByIndex ( iTimerEntIndex ) ) ) ;
# else
return NULL ;
# endif
}
# endif // GAME_DLL
//-----------------------------------------------------------------------------
// Purpose: How long are the respawn waves for this team currently?
//-----------------------------------------------------------------------------
float CTeamplayRoundBasedRules : : GetRespawnWaveMaxLength ( int iTeam , bool bScaleWithNumPlayers /* = true */ )
{
if ( State_Get ( ) ! = GR_STATE_RND_RUNNING )
return 0 ;
if ( mp_disable_respawn_times . GetBool ( ) = = true )
return 0.0f ;
//Let's just turn off respawn times while players are messing around waiting for the tournament to start
if ( IsInTournamentMode ( ) = = true & & IsInPreMatch ( ) = = true )
return 0.0f ;
float flTime = ( ( m_TeamRespawnWaveTimes [ iTeam ] > = 0 ) ? m_TeamRespawnWaveTimes [ iTeam ] : mp_respawnwavetime . GetFloat ( ) ) ;
// For long respawn times, scale the time as the number of players drops
if ( bScaleWithNumPlayers & & flTime > 5 )
{
2011-04-28 01:30:37 -05:00
flTime = MAX ( 5 , flTime * GetRespawnTimeScalar ( iTeam ) ) ;
2008-09-15 01:07:45 -05:00
}
return flTime ;
}
//-----------------------------------------------------------------------------
// Purpose: returns true if we are running tournament mode
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : IsInTournamentMode ( void )
{
return mp_tournament . GetBool ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: returns true if we should even bother to do balancing stuff
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : ShouldBalanceTeams ( void )
{
if ( IsInTournamentMode ( ) = = true )
return false ;
if ( mp_teams_unbalance_limit . GetInt ( ) < = 0 )
return false ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: returns true if the passed team change would cause unbalanced teams
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : WouldChangeUnbalanceTeams ( int iNewTeam , int iCurrentTeam )
{
// players are allowed to change to their own team
if ( iNewTeam = = iCurrentTeam )
return false ;
// if mp_teams_unbalance_limit is 0, don't check
if ( ShouldBalanceTeams ( ) = = false )
return false ;
// if they are joining a non-playing team, allow
if ( iNewTeam < FIRST_GAME_TEAM )
return false ;
CTeam * pNewTeam = GetGlobalTeam ( iNewTeam ) ;
if ( ! pNewTeam )
{
Assert ( 0 ) ;
return true ;
}
// add one because we're joining this team
int iNewTeamPlayers = pNewTeam - > GetNumPlayers ( ) + 1 ;
// for each game team
int i = FIRST_GAME_TEAM ;
CTeam * pTeam ;
for ( pTeam = GetGlobalTeam ( i ) ; pTeam ! = NULL ; pTeam = GetGlobalTeam ( + + i ) )
{
if ( pTeam = = pNewTeam )
continue ;
int iNumPlayers = pTeam - > GetNumPlayers ( ) ;
if ( i = = iCurrentTeam )
{
2011-04-28 01:30:37 -05:00
iNumPlayers = MAX ( 0 , iNumPlayers - 1 ) ;
2008-09-15 01:07:45 -05:00
}
if ( ( iNewTeamPlayers - iNumPlayers ) > mp_teams_unbalance_limit . GetInt ( ) )
{
return true ;
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTeamplayRoundBasedRules : : AreTeamsUnbalanced ( int & iHeaviestTeam , int & iLightestTeam )
{
if ( mp_teams_unbalance_limit . GetInt ( ) < = 0 | | IsInTournamentMode ( ) = = true )
{
return false ;
}
# ifndef CLIENT_DLL
if ( IsInCommentaryMode ( ) )
return false ;
# endif
int iMostPlayers = 0 ;
int iLeastPlayers = MAX_PLAYERS + 1 ;
int i = FIRST_GAME_TEAM ;
for ( CTeam * pTeam = GetGlobalTeam ( i ) ; pTeam ! = NULL ; pTeam = GetGlobalTeam ( + + i ) )
{
int iNumPlayers = pTeam - > GetNumPlayers ( ) ;
if ( iNumPlayers < iLeastPlayers )
{
iLeastPlayers = iNumPlayers ;
iLightestTeam = i ;
}
if ( iNumPlayers > iMostPlayers )
{
iMostPlayers = iNumPlayers ;
iHeaviestTeam = i ;
}
}
if ( ( iMostPlayers - iLeastPlayers ) > mp_teams_unbalance_limit . GetInt ( ) )
{
return true ;
}
return false ;
}
# ifdef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : SetRoundState ( int iRoundState )
{
m_iRoundState = iRoundState ;
m_flLastRoundStateChangeTime = gpGlobals - > curtime ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : OnPreDataChanged ( DataUpdateType_t updateType )
{
m_bOldInWaitingForPlayers = m_bInWaitingForPlayers ;
m_bOldInOvertime = m_bInOvertime ;
m_bOldInSetup = m_bInSetup ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTeamplayRoundBasedRules : : OnDataChanged ( DataUpdateType_t updateType )
{
if ( updateType = = DATA_UPDATE_CREATED | |
m_bOldInWaitingForPlayers ! = m_bInWaitingForPlayers | |
m_bOldInOvertime ! = m_bInOvertime | |
m_bOldInSetup ! = m_bInSetup )
{
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_update_timer " ) ;
if ( event )
{
gameeventmanager - > FireEventClientSide ( event ) ;
}
}
if ( updateType = = DATA_UPDATE_CREATED )
{
if ( State_Get ( ) = = GR_STATE_STALEMATE )
{
IGameEvent * event = gameeventmanager - > CreateEvent ( " teamplay_round_stalemate " ) ;
if ( event )
{
event - > SetInt ( " reason " , STALEMATE_JOIN_MID ) ;
gameeventmanager - > FireEventClientSide ( event ) ;
}
}
}
if ( m_bInOvertime & & ( m_bOldInOvertime ! = m_bInOvertime ) )
{
HandleOvertimeBegin ( ) ;
}
}
# endif // CLIENT_DLL