Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2934 lines
84 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "mp_shareddefs.h"
  8. #include "teamplayroundbased_gamerules.h"
  9. #ifdef CLIENT_DLL
  10. #include "iclientmode.h"
  11. #include <vgui_controls/AnimationController.h>
  12. #include <igameevents.h>
  13. #include "c_team.h"
  14. #define CTeam C_Team
  15. #else
  16. #include "viewport_panel_names.h"
  17. #include "team.h"
  18. #include "mapentities.h"
  19. #include "gameinterface.h"
  20. #include "eventqueue.h"
  21. #include "team_control_point_master.h"
  22. #include "team_train_watcher.h"
  23. #include "serverbenchmark_base.h"
  24. #endif
  25. #include "matchmaking/imatchframework.h"
  26. // memdbgon must be the last include file in a .cpp file!!!
  27. #include "tier0/memdbgon.h"
  28. #ifndef CLIENT_DLL
  29. CUtlVector< CHandle<CTeamControlPointMaster> > g_hControlPointMasters;
  30. extern bool IsInCommentaryMode( void );
  31. #endif
  32. extern ConVar spec_freeze_time;
  33. extern ConVar spec_freeze_traveltime;
  34. #ifdef CLIENT_DLL
  35. void RecvProxy_TeamplayRoundState( const CRecvProxyData *pData, void *pStruct, void *pOut )
  36. {
  37. CTeamplayRoundBasedRules *pGamerules = ( CTeamplayRoundBasedRules *)pStruct;
  38. int iRoundState = pData->m_Value.m_Int;
  39. pGamerules->SetRoundState( iRoundState );
  40. }
  41. #endif
  42. BEGIN_NETWORK_TABLE_NOBASE( CTeamplayRoundBasedRules, DT_TeamplayRoundBasedRules )
  43. #ifdef CLIENT_DLL
  44. RecvPropInt( RECVINFO( m_iRoundState ), 0, RecvProxy_TeamplayRoundState ),
  45. RecvPropBool( RECVINFO( m_bInWaitingForPlayers ) ),
  46. RecvPropInt( RECVINFO( m_iWinningTeam ) ),
  47. RecvPropInt( RECVINFO( m_bInOvertime ) ),
  48. RecvPropInt( RECVINFO( m_bInSetup ) ),
  49. RecvPropInt( RECVINFO( m_bSwitchedTeamsThisRound ) ),
  50. RecvPropBool( RECVINFO( m_bAwaitingReadyRestart ) ),
  51. RecvPropTime( RECVINFO( m_flRestartRoundTime ) ),
  52. RecvPropTime( RECVINFO( m_flMapResetTime ) ),
  53. RecvPropArray3( RECVINFO_ARRAY(m_flNextRespawnWave), RecvPropTime( RECVINFO(m_flNextRespawnWave[0]) ) ),
  54. RecvPropArray3( RECVINFO_ARRAY(m_TeamRespawnWaveTimes), RecvPropFloat( RECVINFO(m_TeamRespawnWaveTimes[0]) ) ),
  55. RecvPropArray3( RECVINFO_ARRAY(m_bTeamReady), RecvPropBool( RECVINFO(m_bTeamReady[0]) ) ),
  56. RecvPropBool( RECVINFO( m_bStopWatch ) ),
  57. #else
  58. SendPropInt( SENDINFO( m_iRoundState ), 5 ),
  59. SendPropBool( SENDINFO( m_bInWaitingForPlayers ) ),
  60. SendPropInt( SENDINFO( m_iWinningTeam ), 3, SPROP_UNSIGNED ),
  61. SendPropBool( SENDINFO( m_bInOvertime ) ),
  62. SendPropBool( SENDINFO( m_bInSetup ) ),
  63. SendPropBool( SENDINFO( m_bSwitchedTeamsThisRound ) ),
  64. SendPropBool( SENDINFO( m_bAwaitingReadyRestart ) ),
  65. SendPropTime( SENDINFO( m_flRestartRoundTime ) ),
  66. SendPropTime( SENDINFO( m_flMapResetTime ) ),
  67. SendPropArray3( SENDINFO_ARRAY3(m_flNextRespawnWave), SendPropTime( SENDINFO_ARRAY(m_flNextRespawnWave) ) ),
  68. SendPropArray3( SENDINFO_ARRAY3(m_TeamRespawnWaveTimes), SendPropFloat( SENDINFO_ARRAY(m_TeamRespawnWaveTimes) ) ),
  69. SendPropArray3( SENDINFO_ARRAY3(m_bTeamReady), SendPropBool( SENDINFO_ARRAY(m_bTeamReady) ) ),
  70. SendPropBool( SENDINFO( m_bStopWatch ) ),
  71. #endif
  72. END_NETWORK_TABLE()
  73. IMPLEMENT_NETWORKCLASS_ALIASED( TeamplayRoundBasedRulesProxy, DT_TeamplayRoundBasedRulesProxy )
  74. #ifdef CLIENT_DLL
  75. void RecvProxy_TeamplayRoundBasedRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
  76. {
  77. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  78. Assert( pRules );
  79. *pOut = pRules;
  80. }
  81. BEGIN_RECV_TABLE( CTeamplayRoundBasedRulesProxy, DT_TeamplayRoundBasedRulesProxy )
  82. RecvPropDataTable( "teamplayroundbased_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_TeamplayRoundBasedRules ), RecvProxy_TeamplayRoundBasedRules )
  83. END_RECV_TABLE()
  84. void CTeamplayRoundBasedRulesProxy::OnPreDataChanged( DataUpdateType_t updateType )
  85. {
  86. BaseClass::OnPreDataChanged( updateType );
  87. // Reroute data changed calls to the non-entity gamerules
  88. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  89. Assert( pRules );
  90. pRules->OnPreDataChanged(updateType);
  91. }
  92. void CTeamplayRoundBasedRulesProxy::OnDataChanged( DataUpdateType_t updateType )
  93. {
  94. BaseClass::OnDataChanged( updateType );
  95. // Reroute data changed calls to the non-entity gamerules
  96. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  97. Assert( pRules );
  98. pRules->OnDataChanged(updateType);
  99. }
  100. #else
  101. void* SendProxy_TeamplayRoundBasedRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
  102. {
  103. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  104. Assert( pRules );
  105. pRecipients->SetAllRecipients();
  106. return pRules;
  107. }
  108. BEGIN_SEND_TABLE( CTeamplayRoundBasedRulesProxy, DT_TeamplayRoundBasedRulesProxy )
  109. SendPropDataTable( "teamplayroundbased_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_TeamplayRoundBasedRules ), SendProxy_TeamplayRoundBasedRules )
  110. END_SEND_TABLE()
  111. BEGIN_DATADESC( CTeamplayRoundBasedRulesProxy )
  112. // Inputs.
  113. DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetStalemateOnTimelimit", InputSetStalemateOnTimelimit ),
  114. END_DATADESC()
  115. //-----------------------------------------------------------------------------
  116. // Purpose:
  117. //-----------------------------------------------------------------------------
  118. void CTeamplayRoundBasedRulesProxy::InputSetStalemateOnTimelimit( inputdata_t &inputdata )
  119. {
  120. TeamplayRoundBasedRules()->SetStalemateOnTimelimit( inputdata.value.Bool() );
  121. }
  122. #endif
  123. 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." );
  124. 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." );
  125. ConVar mp_respawnwavetime( "mp_respawnwavetime", "10.0", FCVAR_NOTIFY | FCVAR_REPLICATED, "Time between respawn waves." );
  126. ConVar mp_capdeteriorate_time( "mp_capdeteriorate_time", "90.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Time it takes for a full capture point to deteriorate." );
  127. ConVar mp_tournament ( "mp_tournament", "0", FCVAR_REPLICATED | FCVAR_NOTIFY );
  128. #ifndef CSTRIKE_DLL
  129. //Arena Mode
  130. ConVar tf_arena_preround_time( "tf_arena_preround_time", "10", FCVAR_NOTIFY | FCVAR_REPLICATED, "Length of the Pre-Round time", true, 5.0, true, 15.0 );
  131. ConVar tf_arena_round_time( "tf_arena_round_time", "0", FCVAR_NOTIFY | FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY );
  132. ConVar tf_arena_max_streak( "tf_arena_max_streak", "5", FCVAR_NOTIFY | FCVAR_REPLICATED, "Teams will be scrambled if one team reaches this streak" );
  133. #endif // !CSTRIKE_DLL
  134. ConVar mp_teams_unbalance_limit( "mp_teams_unbalance_limit", "1", FCVAR_REPLICATED | FCVAR_NOTIFY,
  135. "Teams are unbalanced when one team has this many more players than the other team. (0 disables check)",
  136. true, 0, // min value
  137. true, 30 // max value
  138. );
  139. ConVar mp_maxrounds( "mp_maxrounds", "0", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_RELEASE, "max number of rounds to play before server changes maps", true, 0, false, 0 );
  140. ConVar mp_winlimit( "mp_winlimit", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "Max score one team can reach before server changes maps", true, 0, false, 0 );
  141. ConVar mp_disable_respawn_times( "mp_disable_respawn_times", "0", FCVAR_NOTIFY | FCVAR_REPLICATED );
  142. ConVar mp_bonusroundtime( "mp_bonusroundtime", "15", FCVAR_REPLICATED, "Time after round win until round restarts", true, 5, true, 15 );
  143. #ifdef GAME_DLL
  144. ConVar mp_showroundtransitions( "mp_showroundtransitions", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Show gamestate round transitions." );
  145. ConVar mp_enableroundwaittime( "mp_enableroundwaittime", "1", FCVAR_REPLICATED, "Enable timers to wait between rounds." );
  146. ConVar mp_showcleanedupents( "mp_showcleanedupents", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Show entities that are removed on round respawn." );
  147. ConVar mp_restartround( "mp_restartround", "0", FCVAR_GAMEDLL, "If non-zero, the current round will restart in the specified number of seconds" );
  148. ConVar mp_stalemate_timelimit( "mp_stalemate_timelimit", "240", FCVAR_REPLICATED, "Timelimit (in seconds) of the stalemate round." );
  149. ConVar mp_autoteambalance( "mp_autoteambalance", "1", FCVAR_NOTIFY | FCVAR_RELEASE );
  150. ConVar mp_stalemate_enable( "mp_stalemate_enable", "0", FCVAR_NOTIFY, "Enable/Disable stalemate mode." );
  151. 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." );
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. void cc_SwitchTeams( const CCommand& args )
  156. {
  157. if ( UTIL_IsCommandIssuedByServerAdmin() )
  158. {
  159. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  160. if ( pRules )
  161. {
  162. pRules->SetSwitchTeams( true );
  163. mp_restartgame.SetValue( 5 );
  164. pRules->ShouldResetScores( false, false );
  165. pRules->ShouldResetRoundsPlayed( false );
  166. }
  167. }
  168. }
  169. static ConCommand mp_switchteams( "mp_switchteams", cc_SwitchTeams, "Switch teams and restart the game" );
  170. // Classnames of entities that are preserved across round restarts
  171. static const char *s_PreserveEnts[] =
  172. {
  173. "player",
  174. "viewmodel",
  175. "worldspawn",
  176. "soundent",
  177. "ai_network",
  178. "ai_hint",
  179. "env_soundscape",
  180. "env_soundscape_proxy",
  181. "env_soundscape_triggerable",
  182. "env_sprite",
  183. "env_sun",
  184. "env_wind",
  185. "env_fog_controller",
  186. "func_wall",
  187. "func_illusionary",
  188. "info_node",
  189. "info_target",
  190. "info_node_hint",
  191. "point_commentary_node",
  192. "point_viewcontrol",
  193. "func_precipitation",
  194. "func_team_wall",
  195. "shadow_control",
  196. "sky_camera",
  197. "scene_manager",
  198. "trigger_soundscape",
  199. "commentary_auto",
  200. "point_commentary_viewpoint",
  201. "wearable_item",
  202. "", // END Marker
  203. };
  204. CON_COMMAND_F( mp_forcewin, "Forces team to win", FCVAR_CHEAT )
  205. {
  206. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  207. return;
  208. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  209. if ( pRules )
  210. {
  211. int iTeam = TEAM_UNASSIGNED;
  212. if ( args.ArgC() == 1 )
  213. {
  214. // if no team specified, use player 1's team
  215. iTeam = UTIL_PlayerByIndex( 1 )->GetTeamNumber();
  216. }
  217. else if ( args.ArgC() == 2 )
  218. {
  219. // if team # specified, use that
  220. iTeam = atoi( args[1] );
  221. }
  222. else
  223. {
  224. Msg( "Usage: mp_forcewin <opt: team#>" );
  225. return;
  226. }
  227. int iWinReason = ( TEAM_UNASSIGNED == iTeam ? WINREASON_STALEMATE : WINREASON_ALL_POINTS_CAPTURED );
  228. pRules->SetWinningTeam( iTeam, iWinReason );
  229. }
  230. }
  231. #endif // GAME_DLL
  232. //-----------------------------------------------------------------------------
  233. // Purpose:
  234. //-----------------------------------------------------------------------------
  235. CTeamplayRoundBasedRules::CTeamplayRoundBasedRules( void )
  236. {
  237. for ( int i = 0; i < MAX_TEAMS; i++ )
  238. {
  239. m_flNextRespawnWave.Set( i, 0 );
  240. m_TeamRespawnWaveTimes.Set( i, -1.0f );
  241. m_bTeamReady.Set( i, false );
  242. #ifdef GAME_DLL
  243. m_flOriginalTeamRespawnWaveTime[i] = -1.0f;
  244. #endif
  245. }
  246. m_bInOvertime = false;
  247. m_bInSetup = false;
  248. m_bSwitchedTeamsThisRound = false;
  249. m_flStopWatchTotalTime = -1.0f;
  250. #ifdef GAME_DLL
  251. m_pCurStateInfo = NULL;
  252. State_Transition( GR_STATE_PREGAME );
  253. InitTeams();
  254. ResetMapTime();
  255. ResetScores();
  256. SetForceMapReset( true );
  257. SetRoundToPlayNext( NULL_STRING );
  258. m_bInWaitingForPlayers = false;
  259. m_bAwaitingReadyRestart = false;
  260. m_flRestartRoundTime = -1;
  261. m_flMapResetTime = 0;
  262. m_bPrevRoundWasWaitingForPlayers = false;
  263. m_bResetTeamScores = true;
  264. m_bResetPlayerScores = true;
  265. m_bResetRoundsPlayed = true;
  266. m_iWinningTeam = TEAM_UNASSIGNED;
  267. m_iszPreviousRounds.RemoveAll();
  268. SetFirstRoundPlayed( NULL_STRING );
  269. m_bAllowStalemateAtTimelimit = false;
  270. m_bChangelevelAfterStalemate = false;
  271. m_flRoundStartTime = 0;
  272. m_flStartBalancingTeamsAt = 0;
  273. m_bPrintedUnbalanceWarning = false;
  274. m_flFoundUnbalancedTeamsTime = -1;
  275. m_flWaitingForPlayersTimeEnds = 0.0f;
  276. m_nRoundsPlayed = 0;
  277. m_bUseAddScoreAnim = false;
  278. m_bStopWatch = false;
  279. m_bAwaitingReadyRestart = false;
  280. if ( IsInTournamentMode() == true )
  281. {
  282. m_bAwaitingReadyRestart = true;
  283. }
  284. #endif
  285. }
  286. #ifdef GAME_DLL
  287. //-----------------------------------------------------------------------------
  288. // Purpose:
  289. //-----------------------------------------------------------------------------
  290. void CTeamplayRoundBasedRules::SetTeamRespawnWaveTime( int iTeam, float flValue )
  291. {
  292. if ( flValue < 0 )
  293. {
  294. flValue = 0;
  295. }
  296. // initialized to -1 so we can try to determine if this is the first spawn time we have received for this team
  297. if ( m_flOriginalTeamRespawnWaveTime[iTeam] < 0 )
  298. {
  299. m_flOriginalTeamRespawnWaveTime[iTeam] = flValue;
  300. }
  301. m_TeamRespawnWaveTimes.Set( iTeam, flValue );
  302. }
  303. //-----------------------------------------------------------------------------
  304. // Purpose:
  305. //-----------------------------------------------------------------------------
  306. void CTeamplayRoundBasedRules::AddTeamRespawnWaveTime( int iTeam, float flValue )
  307. {
  308. float flAddAmount = flValue;
  309. float flCurrentSetting = m_TeamRespawnWaveTimes[iTeam];
  310. float flNewValue;
  311. if ( flCurrentSetting < 0 )
  312. {
  313. flCurrentSetting = mp_respawnwavetime.GetFloat();
  314. }
  315. // initialized to -1 so we can try to determine if this is the first spawn time we have received for this team
  316. if ( m_flOriginalTeamRespawnWaveTime[iTeam] < 0 )
  317. {
  318. m_flOriginalTeamRespawnWaveTime[iTeam] = flCurrentSetting;
  319. }
  320. flNewValue = flCurrentSetting + flAddAmount;
  321. if ( flNewValue < 0 )
  322. {
  323. flNewValue = 0;
  324. }
  325. m_TeamRespawnWaveTimes.Set( iTeam, flNewValue );
  326. }
  327. #endif
  328. //-----------------------------------------------------------------------------
  329. // Purpose: don't let us spawn before our freezepanel time would have ended, even if we skip it
  330. //-----------------------------------------------------------------------------
  331. float CTeamplayRoundBasedRules::GetNextRespawnWave( int iTeam, CBasePlayer *pPlayer )
  332. {
  333. if ( State_Get() == GR_STATE_STALEMATE )
  334. return 0;
  335. // If we are purely checking when the next respawn wave is for this team
  336. if ( pPlayer == NULL )
  337. {
  338. return m_flNextRespawnWave[iTeam];
  339. }
  340. // The soonest this player may spawn
  341. float flMinSpawnTime = GetMinTimeWhenPlayerMaySpawn( pPlayer );
  342. // the next scheduled respawn wave time
  343. float flNextRespawnTime = m_flNextRespawnWave[iTeam];
  344. // the length of one respawn wave. We'll check in increments of this
  345. float flRespawnWaveMaxLen = GetRespawnWaveMaxLength( iTeam );
  346. if ( flRespawnWaveMaxLen <= 0 )
  347. {
  348. return flNextRespawnTime;
  349. }
  350. // Keep adding the length of one respawn until we find a wave that
  351. // this player will be eligible to spawn in.
  352. while ( flNextRespawnTime < flMinSpawnTime )
  353. {
  354. flNextRespawnTime += flRespawnWaveMaxLen;
  355. }
  356. return flNextRespawnTime;
  357. }
  358. //-----------------------------------------------------------------------------
  359. // Purpose: Is the player past the required delays for spawning
  360. //-----------------------------------------------------------------------------
  361. bool CTeamplayRoundBasedRules::HasPassedMinRespawnTime( CBasePlayer *pPlayer )
  362. {
  363. float flMinSpawnTime = GetMinTimeWhenPlayerMaySpawn( pPlayer );
  364. return ( gpGlobals->curtime > flMinSpawnTime );
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose:
  368. //-----------------------------------------------------------------------------
  369. float CTeamplayRoundBasedRules::GetMinTimeWhenPlayerMaySpawn( CBasePlayer *pPlayer )
  370. {
  371. // Min respawn time is the sum of
  372. //
  373. // a) the length of one full *unscaled* respawn wave for their team
  374. // and
  375. // b) death anim length + freeze panel length
  376. float flDeathAnimLength = 2.0 + spec_freeze_traveltime.GetFloat() + spec_freeze_time.GetFloat();
  377. float fMinDelay = flDeathAnimLength + GetRespawnWaveMaxLength( pPlayer->GetTeamNumber(), false );
  378. return pPlayer->GetDeathTime() + fMinDelay;
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Purpose:
  382. //-----------------------------------------------------------------------------
  383. float CTeamplayRoundBasedRules::GetRespawnTimeScalar( int iTeam )
  384. {
  385. // For long respawn times, scale the time as the number of players drops
  386. int iOptimalPlayers = 8; // 16 players total, 8 per team
  387. int iNumPlayers = GetGlobalTeam(iTeam)->GetNumPlayers();
  388. float flScale = RemapValClamped( iNumPlayers, 1, iOptimalPlayers, 0.25, 1.0 );
  389. return flScale;
  390. }
  391. #ifdef GAME_DLL
  392. //-----------------------------------------------------------------------------
  393. // Purpose:
  394. //-----------------------------------------------------------------------------
  395. void CTeamplayRoundBasedRules::SetForceMapReset( bool reset )
  396. {
  397. m_bForceMapReset = reset;
  398. }
  399. //-----------------------------------------------------------------------------
  400. // Purpose:
  401. //-----------------------------------------------------------------------------
  402. void CTeamplayRoundBasedRules::Think( void )
  403. {
  404. if ( g_fGameOver ) // someone else quit the game already
  405. {
  406. // check to see if we should change levels now
  407. if ( m_flIntermissionStartTime && ( m_flIntermissionStartTime + GetIntermissionDuration() < gpGlobals->curtime ) )
  408. {
  409. if ( !IsGameConsole() )
  410. {
  411. ChangeLevel(); // intermission is over
  412. }
  413. else
  414. {
  415. IGameEvent * event = gameeventmanager->CreateEvent( "player_stats_updated" );
  416. if ( event )
  417. {
  418. event->SetBool( "forceupload", true );
  419. gameeventmanager->FireEvent( event );
  420. }
  421. g_pMatchFramework->GetEventsSubscription()->BroadcastEvent( new KeyValues(
  422. "OnEngineEndGame", "reason", "gameover" ) );
  423. }
  424. // Don't run this code again
  425. m_flIntermissionStartTime = 0.f;
  426. }
  427. return;
  428. }
  429. State_Think();
  430. if ( m_hWaitingForPlayersTimer )
  431. {
  432. Assert( m_bInWaitingForPlayers );
  433. }
  434. if ( gpGlobals->curtime > m_flNextPeriodicThink )
  435. {
  436. // Don't end the game during win or stalemate states
  437. if ( State_Get() != GR_STATE_TEAM_WIN && State_Get() != GR_STATE_STALEMATE && State_Get() != GR_STATE_GAME_OVER )
  438. {
  439. if ( CheckWinLimit() )
  440. return;
  441. if ( CheckMaxRounds() )
  442. return;
  443. }
  444. CheckRestartRound();
  445. CheckWaitingForPlayers();
  446. m_flNextPeriodicThink = gpGlobals->curtime + 1.0;
  447. }
  448. // Bypass teamplay think.
  449. CGameRules::Think();
  450. }
  451. //-----------------------------------------------------------------------------
  452. // Purpose:
  453. //-----------------------------------------------------------------------------
  454. bool CTeamplayRoundBasedRules::TimerMayExpire( void )
  455. {
  456. #ifndef CSTRIKE_DLL
  457. // team_train_watchers can also prevent timer expiring ( overtime )
  458. CTeamTrainWatcher *pWatcher = dynamic_cast<CTeamTrainWatcher*>( gEntList.FindEntityByClassname( NULL, "team_train_watcher" ) );
  459. while ( pWatcher )
  460. {
  461. if ( !pWatcher->TimerMayExpire() )
  462. {
  463. return false;
  464. }
  465. pWatcher = dynamic_cast<CTeamTrainWatcher*>( gEntList.FindEntityByClassname( pWatcher, "team_train_watcher" ) );
  466. }
  467. #endif
  468. return BaseClass::TimerMayExpire();
  469. }
  470. //-----------------------------------------------------------------------------
  471. // Purpose:
  472. //-----------------------------------------------------------------------------
  473. void CTeamplayRoundBasedRules::CheckChatText( CBasePlayer *pPlayer, char *pText )
  474. {
  475. CheckChatForReadySignal( pPlayer, pText );
  476. BaseClass::CheckChatText( pPlayer, pText );
  477. }
  478. //-----------------------------------------------------------------------------
  479. // Purpose:
  480. //-----------------------------------------------------------------------------
  481. void CTeamplayRoundBasedRules::CheckChatForReadySignal( CBasePlayer *pPlayer, const char *chatmsg )
  482. {
  483. if ( IsInTournamentMode() == false )
  484. {
  485. if( m_bAwaitingReadyRestart && FStrEq( chatmsg, mp_clan_ready_signal.GetString() ) )
  486. {
  487. int iTeam = pPlayer->GetTeamNumber();
  488. if ( iTeam > LAST_SHARED_TEAM && iTeam < GetNumberOfTeams() )
  489. {
  490. m_bTeamReady.Set( iTeam, true );
  491. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_team_ready" );
  492. if ( event )
  493. {
  494. event->SetInt( "team", iTeam );
  495. gameeventmanager->FireEvent( event );
  496. }
  497. }
  498. }
  499. }
  500. }
  501. //-----------------------------------------------------------------------------
  502. // Purpose:
  503. //-----------------------------------------------------------------------------
  504. void CTeamplayRoundBasedRules::GoToIntermission( void )
  505. {
  506. if ( IsInTournamentMode() == true )
  507. return;
  508. BaseClass::GoToIntermission();
  509. // set all players to FL_FROZEN
  510. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  511. {
  512. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  513. if ( pPlayer )
  514. {
  515. pPlayer->AddFlag( FL_FROZEN );
  516. }
  517. }
  518. // Print out map stats to a text file
  519. //WriteStatsFile( "stats.xml" );
  520. State_Enter( GR_STATE_GAME_OVER );
  521. }
  522. //-----------------------------------------------------------------------------
  523. // Purpose:
  524. //-----------------------------------------------------------------------------
  525. void CTeamplayRoundBasedRules::SetInWaitingForPlayers( bool bWaitingForPlayers )
  526. {
  527. // never waiting for players when loading a bug report
  528. if ( IsLoadingBugBaitReport() || gpGlobals->eLoadType == MapLoad_Background )
  529. {
  530. m_bInWaitingForPlayers = false;
  531. return;
  532. }
  533. if( m_bInWaitingForPlayers == bWaitingForPlayers )
  534. return;
  535. if ( IsInArenaMode() == true && m_flWaitingForPlayersTimeEnds == -1 )
  536. {
  537. m_bInWaitingForPlayers = false;
  538. return;
  539. }
  540. m_bInWaitingForPlayers = bWaitingForPlayers;
  541. if( m_bInWaitingForPlayers )
  542. {
  543. m_flWaitingForPlayersTimeEnds = gpGlobals->curtime + mp_waitingforplayers_time.GetFloat();
  544. }
  545. else
  546. {
  547. m_flWaitingForPlayersTimeEnds = -1;
  548. if ( m_hWaitingForPlayersTimer )
  549. {
  550. UTIL_Remove( m_hWaitingForPlayersTimer );
  551. }
  552. RestoreActiveTimer();
  553. }
  554. }
  555. //-----------------------------------------------------------------------------
  556. // Purpose:
  557. //-----------------------------------------------------------------------------
  558. void CTeamplayRoundBasedRules::SetOvertime( bool bOvertime )
  559. {
  560. if ( m_bInOvertime == bOvertime )
  561. return;
  562. if ( bOvertime )
  563. {
  564. UTIL_LogPrintf( "World triggered \"Round_Overtime\"\n" );
  565. }
  566. m_bInOvertime = bOvertime;
  567. if ( m_bInOvertime )
  568. {
  569. // tell train watchers that we've transitioned to overtime
  570. #ifndef CSTRIKE_DLL
  571. CTeamTrainWatcher *pWatcher = dynamic_cast<CTeamTrainWatcher*>( gEntList.FindEntityByClassname( NULL, "team_train_watcher" ) );
  572. while ( pWatcher )
  573. {
  574. variant_t emptyVariant;
  575. pWatcher->AcceptInput( "OnStartOvertime", NULL, NULL, emptyVariant, 0 );
  576. pWatcher = dynamic_cast<CTeamTrainWatcher*>( gEntList.FindEntityByClassname( pWatcher, "team_train_watcher" ) );
  577. }
  578. #endif // CSTRIKE_DLL
  579. }
  580. }
  581. //-----------------------------------------------------------------------------
  582. // Purpose:
  583. //-----------------------------------------------------------------------------
  584. void CTeamplayRoundBasedRules::SetSetup( bool bSetup )
  585. {
  586. if ( m_bInSetup == bSetup )
  587. return;
  588. m_bInSetup = bSetup;
  589. }
  590. //-----------------------------------------------------------------------------
  591. // Purpose:
  592. //-----------------------------------------------------------------------------
  593. void CTeamplayRoundBasedRules::CheckWaitingForPlayers( void )
  594. {
  595. // never waiting for players when loading a bug report
  596. if ( IsLoadingBugBaitReport() || gpGlobals->eLoadType == MapLoad_Background )
  597. return;
  598. if( mp_waitingforplayers_restart.GetBool() )
  599. {
  600. if( m_bInWaitingForPlayers )
  601. {
  602. m_flWaitingForPlayersTimeEnds = gpGlobals->curtime + mp_waitingforplayers_time.GetFloat();
  603. if ( m_hWaitingForPlayersTimer )
  604. {
  605. variant_t sVariant;
  606. sVariant.SetInt( m_flWaitingForPlayersTimeEnds - gpGlobals->curtime );
  607. m_hWaitingForPlayersTimer->AcceptInput( "SetTime", NULL, NULL, sVariant, 0 );
  608. }
  609. }
  610. else
  611. {
  612. SetInWaitingForPlayers( true );
  613. }
  614. mp_waitingforplayers_restart.SetValue( 0 );
  615. }
  616. if( mp_waitingforplayers_cancel.GetBool() && IsInTournamentMode() == false )
  617. {
  618. // Cancel the wait period and manually Resume() the timer if
  619. // it's not supposed to start paused at the beginning of a round.
  620. // We must do this before SetInWaitingForPlayers() is called because it will
  621. // restore the timer in the HUD and set the handle to NULL
  622. #ifndef CSTRIKE_DLL
  623. if ( m_hPreviousActiveTimer.Get() )
  624. {
  625. CTeamRoundTimer *pTimer = dynamic_cast<CTeamRoundTimer*>( m_hPreviousActiveTimer.Get() );
  626. if ( pTimer && !pTimer->StartPaused() )
  627. {
  628. pTimer->ResumeTimer();
  629. }
  630. }
  631. #endif
  632. SetInWaitingForPlayers( false );
  633. mp_waitingforplayers_cancel.SetValue( 0 );
  634. }
  635. if( m_bInWaitingForPlayers )
  636. {
  637. if ( IsInTournamentMode() == true )
  638. return;
  639. // only exit the waitingforplayers if the time is up, and we are not in a round
  640. // restart countdown already, and we are not waiting for a ready restart
  641. if( gpGlobals->curtime > m_flWaitingForPlayersTimeEnds && m_flRestartRoundTime < 0 && !m_bAwaitingReadyRestart )
  642. {
  643. m_flRestartRoundTime = gpGlobals->curtime; // reset asap
  644. if ( IsInArenaMode() == true )
  645. {
  646. if ( gpGlobals->curtime > m_flWaitingForPlayersTimeEnds )
  647. {
  648. SetInWaitingForPlayers( false );
  649. State_Transition( GR_STATE_PREROUND );
  650. }
  651. return;
  652. }
  653. // if "waiting for players" is ending and we're restarting...
  654. // keep the current round that we're already running around in as the first round after the restart
  655. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0].Get() : nullptr;
  656. if ( pMaster && pMaster->PlayingMiniRounds() && pMaster->GetCurrentRound() )
  657. {
  658. SetRoundToPlayNext( pMaster->GetRoundToUseAfterRestart() );
  659. }
  660. }
  661. else
  662. {
  663. if ( !m_hWaitingForPlayersTimer )
  664. {
  665. // Stop any timers, and bring up a new one
  666. HideActiveTimer();
  667. #ifndef CSTRIKE_DLL
  668. variant_t sVariant;
  669. m_hWaitingForPlayersTimer = (CTeamRoundTimer*)CBaseEntity::Create( "team_round_timer", vec3_origin, vec3_angle );
  670. m_hWaitingForPlayersTimer->SetName( MAKE_STRING("zz_teamplay_waiting_timer") );
  671. m_hWaitingForPlayersTimer->KeyValue( "show_in_hud", "1" );
  672. sVariant.SetInt( m_flWaitingForPlayersTimeEnds - gpGlobals->curtime );
  673. m_hWaitingForPlayersTimer->AcceptInput( "SetTime", NULL, NULL, sVariant, 0 );
  674. m_hWaitingForPlayersTimer->AcceptInput( "Resume", NULL, NULL, sVariant, 0 );
  675. m_hWaitingForPlayersTimer->AcceptInput( "Enable", NULL, NULL, sVariant, 0 );
  676. #endif // CSTRIKE_DLL
  677. }
  678. }
  679. }
  680. }
  681. //-----------------------------------------------------------------------------
  682. // Purpose:
  683. //-----------------------------------------------------------------------------
  684. void CTeamplayRoundBasedRules::CheckRestartRound( void )
  685. {
  686. if( mp_clan_readyrestart.GetBool() && IsInTournamentMode() == false )
  687. {
  688. m_bAwaitingReadyRestart = true;
  689. for ( int i = LAST_SHARED_TEAM+1; i < GetNumberOfTeams(); i++ )
  690. {
  691. m_bTeamReady.Set( i, false );
  692. }
  693. const char *pszReadyString = mp_clan_ready_signal.GetString();
  694. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#clan_ready_rules", pszReadyString );
  695. UTIL_ClientPrintAll( HUD_PRINTTALK, "#clan_ready_rules", pszReadyString );
  696. // Don't let them put anything malicious in there
  697. if( pszReadyString == NULL || Q_strlen(pszReadyString) > 16 )
  698. {
  699. pszReadyString = "ready";
  700. }
  701. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_ready_restart" );
  702. if ( event )
  703. {
  704. gameeventmanager->FireEvent( event );
  705. }
  706. mp_clan_readyrestart.SetValue( 0 );
  707. // cancel any restart round in progress
  708. m_flRestartRoundTime = -1;
  709. }
  710. // Restart the game if specified by the server
  711. int iRestartDelay = mp_restartround.GetInt();
  712. if ( iRestartDelay == 0 )
  713. {
  714. iRestartDelay = mp_restartgame.GetInt();
  715. }
  716. if ( iRestartDelay > 0 )
  717. {
  718. if ( iRestartDelay > 60 )
  719. iRestartDelay = 60;
  720. if ( mp_restartgame.GetInt() > 0 )
  721. {
  722. SetForceMapReset( true );
  723. }
  724. else
  725. {
  726. SetForceMapReset( false );
  727. }
  728. SetInStopWatch( false );
  729. m_flRestartRoundTime = gpGlobals->curtime + iRestartDelay;
  730. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_restart_seconds" );
  731. if ( event )
  732. {
  733. event->SetInt( "seconds", iRestartDelay );
  734. gameeventmanager->FireEvent( event );
  735. }
  736. if ( IsInTournamentMode() == false )
  737. {
  738. // let the players know
  739. const char *pFormat = NULL;
  740. if ( mp_restartgame.GetInt() > 0 )
  741. {
  742. if ( ShouldSwitchTeams() )
  743. {
  744. pFormat = ( iRestartDelay > 1 ) ? "#game_switch_in_secs" : "#game_switch_in_sec";
  745. }
  746. else if ( ShouldScrambleTeams() )
  747. {
  748. pFormat = ( iRestartDelay > 1 ) ? "#game_scramble_in_secs" : "#game_scramble_in_sec";
  749. }
  750. else
  751. {
  752. pFormat = ( iRestartDelay > 1 ) ? "#game_restart_in_secs" : "#game_restart_in_sec";
  753. }
  754. }
  755. else if ( mp_restartround.GetInt() > 0 )
  756. {
  757. pFormat = ( iRestartDelay > 1 ) ? "#round_restart_in_secs" : "#round_restart_in_sec";
  758. }
  759. if ( pFormat )
  760. {
  761. char strRestartDelay[64];
  762. Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay );
  763. UTIL_ClientPrintAll( HUD_PRINTCENTER, pFormat, strRestartDelay );
  764. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, pFormat, strRestartDelay );
  765. }
  766. }
  767. mp_restartround.SetValue( 0 );
  768. mp_restartgame.SetValue( 0 );
  769. // cancel any ready restart in progress
  770. m_bAwaitingReadyRestart = false;
  771. }
  772. }
  773. //-----------------------------------------------------------------------------
  774. // Purpose:
  775. //-----------------------------------------------------------------------------
  776. bool CTeamplayRoundBasedRules::CheckTimeLimit( void )
  777. {
  778. if ( IsInPreMatch() == true )
  779. return false;
  780. if ( ( mp_timelimit.GetInt() > 0 && CanChangelevelBecauseOfTimeLimit() ) || m_bChangelevelAfterStalemate )
  781. {
  782. // If there's less than 5 minutes to go, just switch now. This avoids the problem
  783. // of sudden death modes starting shortly after a new round starts.
  784. const int iMinTime = 5;
  785. bool bSwitchDueToTime = ( mp_timelimit.GetInt() > iMinTime && GetTimeLeft() < (iMinTime * 60) );
  786. if ( IsInTournamentMode() == true || IsInArenaMode() == true )
  787. {
  788. bSwitchDueToTime = false;
  789. }
  790. if( GetTimeLeft() <= 0 || m_bChangelevelAfterStalemate || bSwitchDueToTime )
  791. {
  792. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_game_over" );
  793. if ( event )
  794. {
  795. event->SetString( "reason", "Reached Time Limit" );
  796. gameeventmanager->FireEvent( event );
  797. }
  798. SendTeamScoresEvent();
  799. GoToIntermission();
  800. return true;
  801. }
  802. }
  803. return false;
  804. }
  805. //-----------------------------------------------------------------------------
  806. // Purpose:
  807. //-----------------------------------------------------------------------------
  808. bool CTeamplayRoundBasedRules::IsGameUnderTimeLimit( void )
  809. {
  810. return ( mp_timelimit.GetInt() > 0 );
  811. }
  812. //-----------------------------------------------------------------------------
  813. // Purpose:
  814. //-----------------------------------------------------------------------------
  815. int CTeamplayRoundBasedRules::GetTimeLeft( void )
  816. {
  817. float flTimeLimit = mp_timelimit.GetInt() * 60;
  818. float flMapChangeTime = m_flMapResetTime + flTimeLimit;
  819. // If the round timer is longer, let the round complete
  820. // TFTODO: Do we need to worry about the timelimit running our during a round?
  821. int iTime = (int)(flMapChangeTime - gpGlobals->curtime);
  822. if ( iTime < 0 )
  823. {
  824. iTime = 0;
  825. }
  826. return ( iTime );
  827. }
  828. //-----------------------------------------------------------------------------
  829. // Purpose:
  830. //-----------------------------------------------------------------------------
  831. bool CTeamplayRoundBasedRules::CheckNextLevelCvar( void )
  832. {
  833. if ( m_bForceMapReset )
  834. {
  835. if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) )
  836. {
  837. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_game_over" );
  838. if ( event )
  839. {
  840. event->SetString( "reason", "NextLevel CVAR" );
  841. gameeventmanager->FireEvent( event );
  842. }
  843. GoToIntermission();
  844. return true;
  845. }
  846. }
  847. return false;
  848. }
  849. //-----------------------------------------------------------------------------
  850. // Purpose:
  851. //-----------------------------------------------------------------------------
  852. bool CTeamplayRoundBasedRules::CheckWinLimit( void )
  853. {
  854. // has one team won the specified number of rounds?
  855. int iWinLimit = mp_winlimit.GetInt();
  856. if ( iWinLimit > 0 )
  857. {
  858. for ( int i = LAST_SHARED_TEAM+1; i < GetNumberOfTeams(); i++ )
  859. {
  860. CTeam *pTeam = GetGlobalTeam(i);
  861. Assert( pTeam );
  862. if ( pTeam->GetScore() >= iWinLimit )
  863. {
  864. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_game_over" );
  865. if ( event )
  866. {
  867. event->SetString( "reason", "Reached Win Limit" );
  868. gameeventmanager->FireEvent( event );
  869. }
  870. GoToIntermission();
  871. return true;
  872. }
  873. }
  874. }
  875. return false;
  876. }
  877. //-----------------------------------------------------------------------------
  878. // Purpose:
  879. //-----------------------------------------------------------------------------
  880. bool CTeamplayRoundBasedRules::CheckMaxRounds()
  881. {
  882. if ( mp_maxrounds.GetInt() > 0 && IsInPreMatch() == false )
  883. {
  884. if ( m_nRoundsPlayed >= mp_maxrounds.GetInt() )
  885. {
  886. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_game_over" );
  887. if ( event )
  888. {
  889. event->SetString( "reason", "Reached Round Limit" );
  890. gameeventmanager->FireEvent( event );
  891. }
  892. GoToIntermission();
  893. return true;
  894. }
  895. }
  896. return false;
  897. }
  898. //-----------------------------------------------------------------------------
  899. // Purpose:
  900. //-----------------------------------------------------------------------------
  901. void CTeamplayRoundBasedRules::State_Transition( gamerules_roundstate_t newState )
  902. {
  903. State_Leave();
  904. State_Enter( newState );
  905. }
  906. //-----------------------------------------------------------------------------
  907. // Purpose:
  908. //-----------------------------------------------------------------------------
  909. void CTeamplayRoundBasedRules::State_Enter( gamerules_roundstate_t newState )
  910. {
  911. m_iRoundState = newState;
  912. m_pCurStateInfo = State_LookupInfo( newState );
  913. if ( mp_showroundtransitions.GetInt() > 0 )
  914. {
  915. if ( m_pCurStateInfo )
  916. Msg( "Gamerules: entering state '%s'\n", m_pCurStateInfo->m_pStateName );
  917. else
  918. Msg( "Gamerules: entering state #%d\n", newState );
  919. }
  920. // Initialize the new state.
  921. if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState )
  922. {
  923. (this->*m_pCurStateInfo->pfnEnterState)();
  924. }
  925. }
  926. //-----------------------------------------------------------------------------
  927. // Purpose:
  928. //-----------------------------------------------------------------------------
  929. void CTeamplayRoundBasedRules::State_Leave()
  930. {
  931. if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState )
  932. {
  933. (this->*m_pCurStateInfo->pfnLeaveState)();
  934. }
  935. }
  936. //-----------------------------------------------------------------------------
  937. // Purpose:
  938. //-----------------------------------------------------------------------------
  939. void CTeamplayRoundBasedRules::State_Think()
  940. {
  941. if ( m_pCurStateInfo && m_pCurStateInfo->pfnThink )
  942. {
  943. (this->*m_pCurStateInfo->pfnThink)();
  944. }
  945. }
  946. //-----------------------------------------------------------------------------
  947. // Purpose:
  948. //-----------------------------------------------------------------------------
  949. CGameRulesRoundStateInfo* CTeamplayRoundBasedRules::State_LookupInfo( gamerules_roundstate_t state )
  950. {
  951. static CGameRulesRoundStateInfo playerStateInfos[] =
  952. {
  953. { GR_STATE_INIT, "GR_STATE_INIT", &CTeamplayRoundBasedRules::State_Enter_INIT, NULL, &CTeamplayRoundBasedRules::State_Think_INIT },
  954. { GR_STATE_PREGAME, "GR_STATE_PREGAME", &CTeamplayRoundBasedRules::State_Enter_PREGAME, NULL, &CTeamplayRoundBasedRules::State_Think_PREGAME },
  955. { GR_STATE_STARTGAME, "GR_STATE_STARTGAME", &CTeamplayRoundBasedRules::State_Enter_STARTGAME, NULL, &CTeamplayRoundBasedRules::State_Think_STARTGAME },
  956. { GR_STATE_PREROUND, "GR_STATE_PREROUND", &CTeamplayRoundBasedRules::State_Enter_PREROUND, NULL, &CTeamplayRoundBasedRules::State_Think_PREROUND },
  957. { GR_STATE_RND_RUNNING, "GR_STATE_RND_RUNNING", &CTeamplayRoundBasedRules::State_Enter_RND_RUNNING, NULL, &CTeamplayRoundBasedRules::State_Think_RND_RUNNING },
  958. { GR_STATE_TEAM_WIN, "GR_STATE_TEAM_WIN", &CTeamplayRoundBasedRules::State_Enter_TEAM_WIN, NULL, &CTeamplayRoundBasedRules::State_Think_TEAM_WIN },
  959. { GR_STATE_RESTART, "GR_STATE_RESTART", &CTeamplayRoundBasedRules::State_Enter_RESTART, NULL, &CTeamplayRoundBasedRules::State_Think_RESTART },
  960. { GR_STATE_STALEMATE, "GR_STATE_STALEMATE", &CTeamplayRoundBasedRules::State_Enter_STALEMATE, &CTeamplayRoundBasedRules::State_Leave_STALEMATE, &CTeamplayRoundBasedRules::State_Think_STALEMATE },
  961. { GR_STATE_GAME_OVER, "GR_STATE_GAME_OVER", NULL, NULL, NULL },
  962. };
  963. for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ )
  964. {
  965. if ( playerStateInfos[i].m_iRoundState == state )
  966. return &playerStateInfos[i];
  967. }
  968. return NULL;
  969. }
  970. //-----------------------------------------------------------------------------
  971. // Purpose:
  972. //-----------------------------------------------------------------------------
  973. void CTeamplayRoundBasedRules::State_Enter_INIT( void )
  974. {
  975. InitTeams();
  976. ResetMapTime();
  977. }
  978. //-----------------------------------------------------------------------------
  979. // Purpose:
  980. //-----------------------------------------------------------------------------
  981. void CTeamplayRoundBasedRules::State_Think_INIT( void )
  982. {
  983. State_Transition( GR_STATE_PREGAME );
  984. }
  985. //-----------------------------------------------------------------------------
  986. // Purpose: The server is idle and waiting for enough players to start up again.
  987. // When we find an active player go to GR_STATE_STARTGAME.
  988. //-----------------------------------------------------------------------------
  989. void CTeamplayRoundBasedRules::State_Enter_PREGAME( void )
  990. {
  991. m_flNextPeriodicThink = gpGlobals->curtime + 0.1;
  992. }
  993. //-----------------------------------------------------------------------------
  994. // Purpose:
  995. //-----------------------------------------------------------------------------
  996. void CTeamplayRoundBasedRules::State_Think_PREGAME( void )
  997. {
  998. CheckRespawnWaves();
  999. // we'll just stay in pregame for the bugbait reports
  1000. if ( IsLoadingBugBaitReport() || gpGlobals->eLoadType == MapLoad_Background )
  1001. return;
  1002. // Commentary stays in this mode too
  1003. if ( IsInCommentaryMode() )
  1004. return;
  1005. if( CountActivePlayers() > 0 || (IsInArenaMode() == true && m_flWaitingForPlayersTimeEnds == 0.0f) )
  1006. {
  1007. State_Transition( GR_STATE_STARTGAME );
  1008. }
  1009. }
  1010. //-----------------------------------------------------------------------------
  1011. // Purpose: Wait a bit and then spawn everyone into the preround
  1012. //-----------------------------------------------------------------------------
  1013. void CTeamplayRoundBasedRules::State_Enter_STARTGAME( void )
  1014. {
  1015. m_flStateTransitionTime = gpGlobals->curtime;
  1016. m_bInitialSpawn = true;
  1017. }
  1018. //-----------------------------------------------------------------------------
  1019. // Purpose:
  1020. //-----------------------------------------------------------------------------
  1021. void CTeamplayRoundBasedRules::State_Think_STARTGAME()
  1022. {
  1023. if( gpGlobals->curtime > m_flStateTransitionTime )
  1024. {
  1025. if ( mp_waitingforplayers_time.GetFloat() > 0 )
  1026. {
  1027. // go into waitingforplayers, reset at end of it
  1028. SetInWaitingForPlayers( true );
  1029. }
  1030. State_Transition( GR_STATE_PREROUND );
  1031. }
  1032. }
  1033. //-----------------------------------------------------------------------------
  1034. // Purpose:
  1035. //-----------------------------------------------------------------------------
  1036. void CTeamplayRoundBasedRules::State_Enter_PREROUND( void )
  1037. {
  1038. BalanceTeams( false );
  1039. m_flStartBalancingTeamsAt = gpGlobals->curtime + 60.0;
  1040. // Reset everything in the level
  1041. RoundRespawn();
  1042. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_start" );
  1043. if ( event )
  1044. {
  1045. event->SetBool( "full_reset", m_bForceMapReset );
  1046. gameeventmanager->FireEvent( event );
  1047. }
  1048. if ( IsInArenaMode() == true )
  1049. {
  1050. if ( CountActivePlayers() > 0 )
  1051. {
  1052. #ifndef CSTRIKE_DLL
  1053. variant_t sVariant;
  1054. if ( !m_hStalemateTimer )
  1055. {
  1056. m_hStalemateTimer = (CTeamRoundTimer*)CBaseEntity::Create( "team_round_timer", vec3_origin, vec3_angle );
  1057. }
  1058. m_hStalemateTimer->KeyValue( "show_in_hud", "1" );
  1059. sVariant.SetInt( tf_arena_preround_time.GetInt() );
  1060. m_hStalemateTimer->AcceptInput( "SetTime", NULL, NULL, sVariant, 0 );
  1061. m_hStalemateTimer->AcceptInput( "Resume", NULL, NULL, sVariant, 0 );
  1062. m_hStalemateTimer->AcceptInput( "Enable", NULL, NULL, sVariant, 0 );
  1063. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_update_timer" );
  1064. if ( event )
  1065. {
  1066. gameeventmanager->FireEvent( event );
  1067. }
  1068. #endif // CSTRIKE_DLL
  1069. }
  1070. #ifndef CSTRIKE_DLL
  1071. m_flStateTransitionTime = gpGlobals->curtime + tf_arena_preround_time.GetInt();
  1072. #endif // CSTRIKE_DLL
  1073. }
  1074. else
  1075. {
  1076. m_flStateTransitionTime = gpGlobals->curtime + 5 * mp_enableroundwaittime.GetFloat();
  1077. }
  1078. StopWatchModeThink();
  1079. }
  1080. //-----------------------------------------------------------------------------
  1081. // Purpose:
  1082. //-----------------------------------------------------------------------------
  1083. void CTeamplayRoundBasedRules::State_Think_PREROUND( void )
  1084. {
  1085. if( gpGlobals->curtime > m_flStateTransitionTime )
  1086. {
  1087. if ( IsInArenaMode() == true )
  1088. {
  1089. if ( IsInWaitingForPlayers() == true )
  1090. {
  1091. if ( IsInTournamentMode() == true )
  1092. {
  1093. // check round restart
  1094. CheckReadyRestart();
  1095. }
  1096. return;
  1097. }
  1098. State_Transition( GR_STATE_STALEMATE );
  1099. // hide the class composition panel
  1100. }
  1101. else
  1102. {
  1103. State_Transition( GR_STATE_RND_RUNNING );
  1104. }
  1105. }
  1106. CheckRespawnWaves();
  1107. }
  1108. //-----------------------------------------------------------------------------
  1109. // Purpose:
  1110. //-----------------------------------------------------------------------------
  1111. void CTeamplayRoundBasedRules::State_Enter_RND_RUNNING( void )
  1112. {
  1113. SetupOnRoundRunning();
  1114. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_active" );
  1115. if ( event )
  1116. {
  1117. gameeventmanager->FireEvent( event );
  1118. }
  1119. if( !IsInWaitingForPlayers() )
  1120. {
  1121. PlayStartRoundVoice();
  1122. }
  1123. m_bChangeLevelOnRoundEnd = false;
  1124. m_bPrevRoundWasWaitingForPlayers = false;
  1125. m_flNextBalanceTeamsTime = gpGlobals->curtime + 1.0f;
  1126. }
  1127. //-----------------------------------------------------------------------------
  1128. // Purpose:
  1129. //-----------------------------------------------------------------------------
  1130. void CTeamplayRoundBasedRules::CheckReadyRestart( void )
  1131. {
  1132. // check round restart
  1133. if( m_flRestartRoundTime > 0 && m_flRestartRoundTime < gpGlobals->curtime && !g_pServerBenchmark->IsBenchmarkRunning() )
  1134. {
  1135. // time to restart!
  1136. State_Transition( GR_STATE_RESTART );
  1137. m_flRestartRoundTime = -1;
  1138. }
  1139. // check ready restart
  1140. if( m_bAwaitingReadyRestart )
  1141. {
  1142. bool bTeamNotReady = false;
  1143. for ( int i = LAST_SHARED_TEAM+1; i < GetNumberOfTeams(); i++ )
  1144. {
  1145. if ( !m_bTeamReady[i] )
  1146. {
  1147. bTeamNotReady = true;
  1148. break;
  1149. }
  1150. }
  1151. if ( !bTeamNotReady )
  1152. {
  1153. //State_Transition( GR_STATE_RESTART );
  1154. mp_restartgame.SetValue( 5 );
  1155. m_bAwaitingReadyRestart = false;
  1156. ShouldResetScores( true, true );
  1157. ShouldResetRoundsPlayed( true );
  1158. }
  1159. }
  1160. }
  1161. //-----------------------------------------------------------------------------
  1162. // Purpose:
  1163. //-----------------------------------------------------------------------------
  1164. void CTeamplayRoundBasedRules::State_Think_RND_RUNNING( void )
  1165. {
  1166. //if we don't find any active players, return to GR_STATE_PREGAME
  1167. if( CountActivePlayers() <= 0 )
  1168. {
  1169. State_Transition( GR_STATE_PREGAME );
  1170. return;
  1171. }
  1172. if ( m_flNextBalanceTeamsTime < gpGlobals->curtime )
  1173. {
  1174. BalanceTeams( true );
  1175. m_flNextBalanceTeamsTime = gpGlobals->curtime + 1.0f;
  1176. }
  1177. CheckRespawnWaves();
  1178. // check round restart
  1179. CheckReadyRestart();
  1180. bool bAllowStalemateAtTimelimit = m_bAllowStalemateAtTimelimit || ( !IsValveMap() && mp_stalemate_at_timelimit.GetBool() );
  1181. // See if we're coming up to the server timelimit, in which case force a stalemate immediately.
  1182. if ( State_Get() == GR_STATE_RND_RUNNING && bAllowStalemateAtTimelimit && mp_timelimit.GetInt() > 0 && IsInPreMatch() == false )
  1183. {
  1184. if ( GetTimeLeft() <= 0.5 )
  1185. {
  1186. int iDrawScoreCheck = -1;
  1187. int iWinningTeam = 0;
  1188. bool bTeamsAreDrawn = true;
  1189. for ( int i = FIRST_GAME_TEAM; (i < GetNumberOfTeams()) && bTeamsAreDrawn; i++ )
  1190. {
  1191. int iTeamScore = GetGlobalTeam(i)->GetScore();
  1192. if ( iTeamScore > iDrawScoreCheck )
  1193. {
  1194. iWinningTeam = i;
  1195. }
  1196. if ( iTeamScore != iDrawScoreCheck )
  1197. {
  1198. if ( iDrawScoreCheck == -1 )
  1199. {
  1200. iDrawScoreCheck = iTeamScore;
  1201. }
  1202. else
  1203. {
  1204. bTeamsAreDrawn = false;
  1205. }
  1206. }
  1207. }
  1208. if ( bTeamsAreDrawn )
  1209. {
  1210. if ( CanGoToStalemate() )
  1211. {
  1212. m_bChangelevelAfterStalemate = true;
  1213. SetStalemate( STALEMATE_SERVER_TIMELIMIT, m_bForceMapReset );
  1214. }
  1215. else
  1216. {
  1217. SetOvertime( true );
  1218. }
  1219. }
  1220. else
  1221. {
  1222. SetWinningTeam( iWinningTeam, WINREASON_TIMELIMIT, true, false, true );
  1223. }
  1224. }
  1225. }
  1226. StopWatchModeThink();
  1227. }
  1228. //-----------------------------------------------------------------------------
  1229. // Purpose:
  1230. //-----------------------------------------------------------------------------
  1231. void CTeamplayRoundBasedRules::State_Enter_TEAM_WIN( void )
  1232. {
  1233. float flTime = MAX( 5, mp_bonusroundtime.GetFloat() );
  1234. m_flStateTransitionTime = gpGlobals->curtime + flTime * mp_enableroundwaittime.GetFloat();
  1235. // if we're forcing the map to reset it must be the end of a "full" round not a mini-round
  1236. if ( m_bForceMapReset )
  1237. {
  1238. m_nRoundsPlayed++;
  1239. }
  1240. InternalHandleTeamWin( m_iWinningTeam );
  1241. SendWinPanelInfo();
  1242. }
  1243. //-----------------------------------------------------------------------------
  1244. // Purpose:
  1245. //-----------------------------------------------------------------------------
  1246. void CTeamplayRoundBasedRules::State_Think_TEAM_WIN( void )
  1247. {
  1248. if( gpGlobals->curtime > m_flStateTransitionTime )
  1249. {
  1250. bool bDone = !(!CheckTimeLimit() && !CheckWinLimit() && !CheckMaxRounds() && !CheckNextLevelCvar());
  1251. // check the win limit, max rounds, time limit and nextlevel cvar before starting the next round
  1252. if ( bDone == false )
  1253. {
  1254. PreviousRoundEnd();
  1255. State_Transition( GR_STATE_PREROUND );
  1256. }
  1257. else if ( IsInTournamentMode() == true )
  1258. {
  1259. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  1260. {
  1261. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  1262. if ( !pPlayer )
  1263. continue;
  1264. pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD );
  1265. }
  1266. RestartTournament();
  1267. State_Transition( GR_STATE_RND_RUNNING );
  1268. }
  1269. }
  1270. }
  1271. //-----------------------------------------------------------------------------
  1272. // Purpose:
  1273. //-----------------------------------------------------------------------------
  1274. void CTeamplayRoundBasedRules::State_Enter_STALEMATE( void )
  1275. {
  1276. m_flStalemateStartTime = gpGlobals->curtime;
  1277. SetupOnStalemateStart();
  1278. // Stop any timers, and bring up a new one
  1279. HideActiveTimer();
  1280. if ( m_hStalemateTimer )
  1281. {
  1282. UTIL_Remove( m_hStalemateTimer );
  1283. m_hStalemateTimer = NULL;
  1284. }
  1285. int iTimeLimit = mp_stalemate_timelimit.GetInt();
  1286. #ifndef CSTRIKE_DLL
  1287. if ( IsInArenaMode() == true )
  1288. {
  1289. iTimeLimit = tf_arena_round_time.GetInt();
  1290. }
  1291. #endif
  1292. if ( iTimeLimit > 0 )
  1293. {
  1294. #ifndef CSTRIKE_DLL
  1295. variant_t sVariant;
  1296. if ( !m_hStalemateTimer )
  1297. {
  1298. m_hStalemateTimer = (CTeamRoundTimer*)CBaseEntity::Create( "team_round_timer", vec3_origin, vec3_angle );
  1299. }
  1300. m_hStalemateTimer->KeyValue( "show_in_hud", "1" );
  1301. sVariant.SetInt( iTimeLimit );
  1302. m_hStalemateTimer->AcceptInput( "SetTime", NULL, NULL, sVariant, 0 );
  1303. m_hStalemateTimer->AcceptInput( "Resume", NULL, NULL, sVariant, 0 );
  1304. m_hStalemateTimer->AcceptInput( "Enable", NULL, NULL, sVariant, 0 );
  1305. #endif // CSTRIKE_DLL
  1306. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_update_timer" );
  1307. if ( event )
  1308. {
  1309. gameeventmanager->FireEvent( event );
  1310. }
  1311. }
  1312. }
  1313. //-----------------------------------------------------------------------------
  1314. // Purpose:
  1315. //-----------------------------------------------------------------------------
  1316. void CTeamplayRoundBasedRules::State_Leave_STALEMATE( void )
  1317. {
  1318. SetupOnStalemateEnd();
  1319. if ( m_hStalemateTimer )
  1320. {
  1321. UTIL_Remove( m_hStalemateTimer );
  1322. }
  1323. if ( IsInArenaMode() == false )
  1324. {
  1325. RestoreActiveTimer();
  1326. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_update_timer" );
  1327. if ( event )
  1328. {
  1329. gameeventmanager->FireEvent( event );
  1330. }
  1331. }
  1332. }
  1333. //-----------------------------------------------------------------------------
  1334. // Purpose:
  1335. //-----------------------------------------------------------------------------
  1336. void CTeamplayRoundBasedRules::HideActiveTimer( void )
  1337. {
  1338. #ifndef CSTRIKE_DLL
  1339. // We can't handle this, because we won't be able to restore multiple timers
  1340. Assert( m_hPreviousActiveTimer.Get() == NULL );
  1341. m_hPreviousActiveTimer = NULL;
  1342. CBaseEntity *pEntity = NULL;
  1343. variant_t sVariant;
  1344. sVariant.SetInt( false );
  1345. while ((pEntity = gEntList.FindEntityByClassname( pEntity, "team_round_timer" )) != NULL)
  1346. {
  1347. CTeamRoundTimer *pTimer = assert_cast<CTeamRoundTimer*>(pEntity);
  1348. if ( pTimer && pTimer->ShowInHud() )
  1349. {
  1350. Assert( !m_hPreviousActiveTimer );
  1351. m_hPreviousActiveTimer = pTimer;
  1352. pEntity->AcceptInput( "ShowInHUD", NULL, NULL, sVariant, 0 );
  1353. }
  1354. }
  1355. #endif // CSTRIKE_DLL
  1356. }
  1357. //-----------------------------------------------------------------------------
  1358. // Purpose:
  1359. //-----------------------------------------------------------------------------
  1360. void CTeamplayRoundBasedRules::RestoreActiveTimer( void )
  1361. {
  1362. if ( m_hPreviousActiveTimer )
  1363. {
  1364. variant_t sVariant;
  1365. sVariant.SetInt( true );
  1366. m_hPreviousActiveTimer->AcceptInput( "ShowInHUD", NULL, NULL, sVariant, 0 );
  1367. m_hPreviousActiveTimer = NULL;
  1368. }
  1369. }
  1370. //-----------------------------------------------------------------------------
  1371. // Purpose:
  1372. //-----------------------------------------------------------------------------
  1373. void CTeamplayRoundBasedRules::State_Think_STALEMATE( void )
  1374. {
  1375. //if we don't find any active players, return to GR_STATE_PREGAME
  1376. if( CountActivePlayers() <= 0 && IsInArenaMode() == false )
  1377. {
  1378. State_Transition( GR_STATE_PREGAME );
  1379. return;
  1380. }
  1381. int iDeadTeam = TEAM_UNASSIGNED;
  1382. int iAliveTeam = TEAM_UNASSIGNED;
  1383. // If a team is fully killed, the other team has won
  1384. for ( int i = LAST_SHARED_TEAM+1; i < GetNumberOfTeams(); i++ )
  1385. {
  1386. CTeam *pTeam = GetGlobalTeam(i);
  1387. Assert( pTeam );
  1388. int iPlayers = pTeam->GetNumPlayers();
  1389. if ( iPlayers )
  1390. {
  1391. bool bFoundLiveOne = false;
  1392. for ( int player = 0; player < iPlayers; player++ )
  1393. {
  1394. if ( pTeam->GetPlayer(player) && pTeam->GetPlayer(player)->IsAlive() )
  1395. {
  1396. bFoundLiveOne = true;
  1397. break;
  1398. }
  1399. }
  1400. if ( bFoundLiveOne )
  1401. {
  1402. iAliveTeam = i;
  1403. }
  1404. else
  1405. {
  1406. iDeadTeam = i;
  1407. }
  1408. }
  1409. else
  1410. {
  1411. iDeadTeam = i;
  1412. }
  1413. }
  1414. if ( iDeadTeam && iAliveTeam )
  1415. {
  1416. // The live team has won.
  1417. bool bMasterHandled = false;
  1418. if ( !m_bForceMapReset )
  1419. {
  1420. // We're not resetting the map, so give the winners control
  1421. // of all the points that were in play this round.
  1422. // Find the control point master.
  1423. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0].Get() : nullptr;
  1424. if ( pMaster )
  1425. {
  1426. variant_t sVariant;
  1427. sVariant.SetInt( iAliveTeam );
  1428. pMaster->AcceptInput( "SetWinnerAndForceCaps", NULL, NULL, sVariant, 0 );
  1429. bMasterHandled = true;
  1430. }
  1431. }
  1432. if ( !bMasterHandled )
  1433. {
  1434. SetWinningTeam( iAliveTeam, WINREASON_OPPONENTS_DEAD, m_bForceMapReset );
  1435. }
  1436. }
  1437. else if ( iDeadTeam && iAliveTeam == TEAM_UNASSIGNED ||
  1438. ( m_hStalemateTimer && TimerMayExpire() && m_hStalemateTimer->GetTimeRemaining() <= 0 ) )
  1439. {
  1440. bool bFullReset = true;
  1441. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0].Get() : nullptr;
  1442. if ( pMaster && pMaster->PlayingMiniRounds() )
  1443. {
  1444. // we don't need to do a full map reset for maps with mini-rounds
  1445. bFullReset = false;
  1446. }
  1447. // Both teams are dead. Pure stalemate.
  1448. SetWinningTeam( TEAM_UNASSIGNED, WINREASON_STALEMATE, bFullReset, false );
  1449. }
  1450. }
  1451. //-----------------------------------------------------------------------------
  1452. // Purpose: manual restart
  1453. //-----------------------------------------------------------------------------
  1454. void CTeamplayRoundBasedRules::State_Enter_RESTART( void )
  1455. {
  1456. // send scores
  1457. SendTeamScoresEvent();
  1458. // send restart event
  1459. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_restart_round" );
  1460. if ( event )
  1461. {
  1462. gameeventmanager->FireEvent( event );
  1463. }
  1464. m_bPrevRoundWasWaitingForPlayers = m_bInWaitingForPlayers;
  1465. SetInWaitingForPlayers( false );
  1466. ResetScores();
  1467. // reset the round time
  1468. ResetMapTime();
  1469. State_Transition( GR_STATE_PREROUND );
  1470. }
  1471. //-----------------------------------------------------------------------------
  1472. // Purpose:
  1473. //-----------------------------------------------------------------------------
  1474. void CTeamplayRoundBasedRules::State_Think_RESTART( void )
  1475. {
  1476. // should never get here, State_Enter_RESTART sets us into a different state
  1477. Assert( 0 );
  1478. }
  1479. //-----------------------------------------------------------------------------
  1480. // Purpose: Input for other entities to declare a round winner.
  1481. //-----------------------------------------------------------------------------
  1482. void CTeamplayRoundBasedRules::SetWinningTeam( int team, int iWinReason, bool bForceMapReset /* = true */, bool bSwitchTeams /* = false*/, bool bDontAddScore /* = false*/ )
  1483. {
  1484. // Commentary doesn't let anyone win
  1485. if ( IsInCommentaryMode() )
  1486. return;
  1487. if ( ( team != TEAM_UNASSIGNED ) && ( team <= LAST_SHARED_TEAM || team >= GetNumberOfTeams() ) )
  1488. {
  1489. Assert( !"SetWinningTeam() called with invalid team." );
  1490. return;
  1491. }
  1492. // are we already in this state?
  1493. if ( State_Get() == GR_STATE_TEAM_WIN )
  1494. return;
  1495. SetForceMapReset( bForceMapReset );
  1496. SetSwitchTeams( bSwitchTeams );
  1497. m_iWinningTeam = team;
  1498. m_iWinReason = iWinReason;
  1499. PlayWinSong( team );
  1500. // 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
  1501. bool bRewardTeam = bForceMapReset || ( IsGameUnderTimeLimit() && ( GetTimeLeft() <= 0 ) );
  1502. if ( bDontAddScore == true )
  1503. {
  1504. bRewardTeam = false;
  1505. }
  1506. m_bUseAddScoreAnim = false;
  1507. if ( bRewardTeam && ( team != TEAM_UNASSIGNED ) && ShouldScorePerRound() )
  1508. {
  1509. GetGlobalTeam( team )->AddScore( TEAMPLAY_ROUND_WIN_SCORE );
  1510. m_bUseAddScoreAnim = true;
  1511. }
  1512. // this was a sudden death win if we were in stalemate then a team won it
  1513. bool bWasSuddenDeath = ( InStalemate() && m_iWinningTeam >= FIRST_GAME_TEAM );
  1514. State_Transition( GR_STATE_TEAM_WIN );
  1515. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_win" );
  1516. if ( event )
  1517. {
  1518. event->SetInt( "team", team );
  1519. event->SetBool( "full_round", bForceMapReset );
  1520. event->SetFloat( "round_time", gpGlobals->curtime - m_flRoundStartTime );
  1521. event->SetBool( "was_sudden_death", bWasSuddenDeath );
  1522. // let derived classes add more fields to the event
  1523. FillOutTeamplayRoundWinEvent( event );
  1524. gameeventmanager->FireEvent( event );
  1525. }
  1526. // send team scores
  1527. SendTeamScoresEvent();
  1528. if ( team == TEAM_UNASSIGNED )
  1529. {
  1530. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  1531. {
  1532. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( i ) );
  1533. if ( !pPlayer )
  1534. continue;
  1535. pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_STALEMATE );
  1536. }
  1537. }
  1538. }
  1539. //-----------------------------------------------------------------------------
  1540. // Purpose: Input for other entities to declare a stalemate
  1541. // Most often a team_control_point_master saying that the
  1542. // round timer expired
  1543. //-----------------------------------------------------------------------------
  1544. void CTeamplayRoundBasedRules::SetStalemate( int iReason, bool bForceMapReset /* = true */, bool bSwitchTeams /* = false */ )
  1545. {
  1546. if ( IsInTournamentMode() == true && IsInPreMatch() == true )
  1547. return;
  1548. if ( !mp_stalemate_enable.GetBool() )
  1549. {
  1550. SetWinningTeam( TEAM_UNASSIGNED, WINREASON_STALEMATE, bForceMapReset, bSwitchTeams );
  1551. return;
  1552. }
  1553. if ( InStalemate() )
  1554. return;
  1555. SetForceMapReset( bForceMapReset );
  1556. m_iWinningTeam = TEAM_UNASSIGNED;
  1557. PlaySuddenDeathSong();
  1558. State_Transition( GR_STATE_STALEMATE );
  1559. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_stalemate" );
  1560. if ( event )
  1561. {
  1562. event->SetInt( "reason", iReason );
  1563. gameeventmanager->FireEvent( event );
  1564. }
  1565. }
  1566. #ifdef GAME_DLL
  1567. void CC_CH_ForceRespawn( void )
  1568. {
  1569. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  1570. if ( pRules )
  1571. {
  1572. pRules->RespawnPlayers( true );
  1573. }
  1574. }
  1575. static ConCommand mp_forcerespawnplayers("mp_forcerespawnplayers", CC_CH_ForceRespawn, "Force all players to respawn.", FCVAR_CHEAT );
  1576. void CC_CH_TournamentRestart( void )
  1577. {
  1578. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  1579. if ( pRules )
  1580. {
  1581. pRules->RestartTournament();
  1582. }
  1583. }
  1584. static ConCommand mp_tournament_restart("mp_tournament_restart", CC_CH_TournamentRestart, "Restart Tournament Mode on the current level." );
  1585. void CTeamplayRoundBasedRules::RestartTournament( void )
  1586. {
  1587. if ( IsInTournamentMode() == false )
  1588. return;
  1589. SetInWaitingForPlayers( true );
  1590. m_bAwaitingReadyRestart = true;
  1591. m_flStopWatchTotalTime = -1.0f;
  1592. m_bStopWatch = false;
  1593. for ( int i = 0; i < MAX_TEAMS; i++ )
  1594. {
  1595. m_bTeamReady.Set( i, false );
  1596. }
  1597. }
  1598. #endif
  1599. //-----------------------------------------------------------------------------
  1600. // Purpose:
  1601. // Input : bForceRespawn - respawn player even if dead or dying
  1602. // bTeam - if true, only respawn the passed team
  1603. // iTeam - team to respawn
  1604. //-----------------------------------------------------------------------------
  1605. void CTeamplayRoundBasedRules::RespawnPlayers( bool bForceRespawn, bool bTeam /* = false */, int iTeam/* = TEAM_UNASSIGNED */ )
  1606. {
  1607. if ( bTeam )
  1608. {
  1609. Assert( iTeam > LAST_SHARED_TEAM && iTeam < GetNumberOfTeams() );
  1610. }
  1611. int iPlayersSpawned = 0;
  1612. CBasePlayer *pPlayer;
  1613. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  1614. {
  1615. pPlayer = ToBasePlayer( UTIL_PlayerByIndex( i ) );
  1616. if ( !pPlayer )
  1617. continue;
  1618. // Check for team specific spawn
  1619. if ( bTeam && pPlayer->GetTeamNumber() != iTeam )
  1620. continue;
  1621. // players that haven't chosen a team/class can never spawn
  1622. if ( !pPlayer->IsReadyToPlay() )
  1623. {
  1624. // Let the player spawn immediately when they do pick a class
  1625. if ( pPlayer->ShouldGainInstantSpawn() )
  1626. {
  1627. pPlayer->AllowInstantSpawn();
  1628. }
  1629. continue;
  1630. }
  1631. // If we aren't force respawning, don't respawn players that:
  1632. // - are alive
  1633. // - are still in the death anim stage of dying
  1634. if ( !bForceRespawn )
  1635. {
  1636. if ( pPlayer->IsAlive() )
  1637. continue;
  1638. if ( m_iRoundState != GR_STATE_PREROUND )
  1639. {
  1640. // If the player hasn't been dead the minimum respawn time, he
  1641. // waits until the next wave.
  1642. if ( bTeam && !HasPassedMinRespawnTime( pPlayer ) )
  1643. continue;
  1644. if ( !pPlayer->IsReadyToSpawn() )
  1645. {
  1646. // Let the player spawn immediately when they do pick a class
  1647. if ( pPlayer->ShouldGainInstantSpawn() )
  1648. {
  1649. pPlayer->AllowInstantSpawn();
  1650. }
  1651. continue;
  1652. }
  1653. }
  1654. }
  1655. // Respawn this player
  1656. pPlayer->ForceRespawn();
  1657. iPlayersSpawned++;
  1658. }
  1659. }
  1660. //-----------------------------------------------------------------------------
  1661. // Purpose:
  1662. //-----------------------------------------------------------------------------
  1663. void CTeamplayRoundBasedRules::InitTeams( void )
  1664. {
  1665. }
  1666. //-----------------------------------------------------------------------------
  1667. // Purpose:
  1668. //-----------------------------------------------------------------------------
  1669. int CTeamplayRoundBasedRules::CountActivePlayers( void )
  1670. {
  1671. int i;
  1672. int count = 0;
  1673. CBasePlayer *pPlayer;
  1674. for (i = 1; i <= gpGlobals->maxClients; i++ )
  1675. {
  1676. pPlayer = ToBasePlayer( UTIL_PlayerByIndex( i ) );
  1677. if ( pPlayer )
  1678. {
  1679. if( pPlayer->IsReadyToPlay() )
  1680. {
  1681. count++;
  1682. }
  1683. }
  1684. }
  1685. return count;
  1686. }
  1687. //-----------------------------------------------------------------------------
  1688. // Purpose:
  1689. //-----------------------------------------------------------------------------
  1690. void CTeamplayRoundBasedRules::HandleTimeLimitChange( void )
  1691. {
  1692. // check that we have an active timer in the HUD and use mp_timelimit if we don't
  1693. if ( !MapHasActiveTimer() && ( mp_timelimit.GetInt() > 0 && GetTimeLeft() > 0 ) )
  1694. {
  1695. CreateTimeLimitTimer();
  1696. }
  1697. else
  1698. {
  1699. if ( m_hTimeLimitTimer )
  1700. {
  1701. UTIL_Remove( m_hTimeLimitTimer );
  1702. m_hTimeLimitTimer = NULL;
  1703. }
  1704. }
  1705. }
  1706. //-----------------------------------------------------------------------------
  1707. // Purpose:
  1708. //-----------------------------------------------------------------------------
  1709. bool CTeamplayRoundBasedRules::MapHasActiveTimer( void )
  1710. {
  1711. #ifndef CSTRIKE_DLL
  1712. CBaseEntity *pEntity = NULL;
  1713. while ( ( pEntity = gEntList.FindEntityByClassname( pEntity, "team_round_timer" ) ) != NULL )
  1714. {
  1715. CTeamRoundTimer *pTimer = assert_cast<CTeamRoundTimer*>( pEntity );
  1716. if ( pTimer && pTimer->ShowInHud() && ( Q_stricmp( STRING( pTimer->GetEntityName() ), "zz_teamplay_timelimit_timer" ) != 0 ) )
  1717. {
  1718. return true;
  1719. }
  1720. }
  1721. #endif // CSTRIKE_DLL
  1722. return false;
  1723. }
  1724. //-----------------------------------------------------------------------------
  1725. // Purpose:
  1726. //-----------------------------------------------------------------------------
  1727. void CTeamplayRoundBasedRules::CreateTimeLimitTimer( void )
  1728. {
  1729. #ifndef CSTRIKE_DLL
  1730. if ( !m_hTimeLimitTimer )
  1731. {
  1732. m_hTimeLimitTimer = (CTeamRoundTimer*)CBaseEntity::Create( "team_round_timer", vec3_origin, vec3_angle );
  1733. m_hTimeLimitTimer->SetName( MAKE_STRING( "zz_teamplay_timelimit_timer" ) );
  1734. }
  1735. variant_t sVariant;
  1736. m_hTimeLimitTimer->KeyValue( "show_in_hud", "1" );
  1737. sVariant.SetInt( GetTimeLeft() );
  1738. m_hTimeLimitTimer->AcceptInput( "SetTime", NULL, NULL, sVariant, 0 );
  1739. m_hTimeLimitTimer->AcceptInput( "Resume", NULL, NULL, sVariant, 0 );
  1740. m_hTimeLimitTimer->AcceptInput( "Enable", NULL, NULL, sVariant, 0 );
  1741. #endif // CSTRIKE_DLL
  1742. }
  1743. //-----------------------------------------------------------------------------
  1744. // Purpose:
  1745. //-----------------------------------------------------------------------------
  1746. void CTeamplayRoundBasedRules::RoundRespawn( void )
  1747. {
  1748. m_flRoundStartTime = gpGlobals->curtime;
  1749. if ( m_bForceMapReset || m_bPrevRoundWasWaitingForPlayers )
  1750. {
  1751. CleanUpMap();
  1752. // clear out the previously played rounds
  1753. m_iszPreviousRounds.RemoveAll();
  1754. if ( mp_timelimit.GetInt() > 0 && GetTimeLeft() > 0 )
  1755. {
  1756. // check that we have an active timer in the HUD and use mp_timelimit if we don't
  1757. if ( !MapHasActiveTimer() )
  1758. {
  1759. CreateTimeLimitTimer();
  1760. }
  1761. }
  1762. m_iLastCapPointChanged = 0;
  1763. }
  1764. // reset our spawn times to the original values
  1765. for ( int i = 0; i < MAX_TEAMS; i++ )
  1766. {
  1767. if ( m_flOriginalTeamRespawnWaveTime[i] >= 0 )
  1768. {
  1769. m_TeamRespawnWaveTimes.Set( i, m_flOriginalTeamRespawnWaveTime[i] );
  1770. }
  1771. }
  1772. if ( !IsInWaitingForPlayers() )
  1773. {
  1774. if ( m_bForceMapReset )
  1775. {
  1776. UTIL_LogPrintf( "World triggered \"Round_Start\"\n" );
  1777. }
  1778. }
  1779. // Setup before respawning players, so we can mess with spawnpoints
  1780. SetupOnRoundStart();
  1781. // Do we need to switch the teams?
  1782. m_bSwitchedTeamsThisRound = false;
  1783. if ( ShouldSwitchTeams() )
  1784. {
  1785. m_bSwitchedTeamsThisRound = true;
  1786. HandleSwitchTeams();
  1787. SetSwitchTeams( false );
  1788. }
  1789. // Do we need to switch the teams?
  1790. if ( ShouldScrambleTeams() )
  1791. {
  1792. HandleScrambleTeams();
  1793. SetScrambleTeams( false );
  1794. }
  1795. RespawnPlayers( true );
  1796. // reset per-round scores for each player
  1797. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  1798. {
  1799. CBasePlayer *pPlayer = ToBasePlayer( UTIL_PlayerByIndex( i ) );
  1800. if ( pPlayer )
  1801. {
  1802. pPlayer->ResetPerRoundStats();
  1803. }
  1804. }
  1805. }
  1806. //-----------------------------------------------------------------------------
  1807. // Purpose: Recreate all the map entities from the map data (preserving their indices),
  1808. // then remove everything else except the players.
  1809. //-----------------------------------------------------------------------------
  1810. void CTeamplayRoundBasedRules::CleanUpMap()
  1811. {
  1812. if( mp_showcleanedupents.GetInt() )
  1813. {
  1814. Msg( "CleanUpMap\n===============\n" );
  1815. Msg( " Entities: %d (%d edicts)\n", gEntList.NumberOfEntities(), gEntList.NumberOfEdicts() );
  1816. }
  1817. // Get rid of all entities except players.
  1818. CBaseEntity *pCur = gEntList.FirstEnt();
  1819. while ( pCur )
  1820. {
  1821. if ( !RoundCleanupShouldIgnore( pCur ) )
  1822. {
  1823. if( mp_showcleanedupents.GetInt() & 1 )
  1824. {
  1825. Msg( "Removed Entity: %s\n", pCur->GetClassname() );
  1826. }
  1827. UTIL_Remove( pCur );
  1828. }
  1829. pCur = gEntList.NextEnt( pCur );
  1830. }
  1831. // Clear out the event queue
  1832. g_EventQueue.Clear();
  1833. // Really remove the entities so we can have access to their slots below.
  1834. gEntList.CleanupDeleteList();
  1835. engine->AllowImmediateEdictReuse();
  1836. if ( mp_showcleanedupents.GetInt() & 2 )
  1837. {
  1838. Msg( " Entities Left:\n" );
  1839. pCur = gEntList.FirstEnt();
  1840. while ( pCur )
  1841. {
  1842. Msg( " %s (%d)\n", pCur->GetClassname(), pCur->entindex() );
  1843. pCur = gEntList.NextEnt( pCur );
  1844. }
  1845. }
  1846. // Now reload the map entities.
  1847. class CTeamplayMapEntityFilter : public IMapEntityFilter
  1848. {
  1849. public:
  1850. CTeamplayMapEntityFilter()
  1851. {
  1852. m_pRules = assert_cast<CTeamplayRoundBasedRules*>( GameRules() );
  1853. }
  1854. virtual bool ShouldCreateEntity( const char *pClassname )
  1855. {
  1856. // Don't recreate the preserved entities.
  1857. if ( m_pRules->ShouldCreateEntity( pClassname ) )
  1858. return true;
  1859. // Increment our iterator since it's not going to call CreateNextEntity for this ent.
  1860. if ( m_iIterator != g_MapEntityRefs.InvalidIndex() )
  1861. {
  1862. m_iIterator = g_MapEntityRefs.Next( m_iIterator );
  1863. }
  1864. return false;
  1865. }
  1866. virtual CBaseEntity* CreateNextEntity( const char *pClassname )
  1867. {
  1868. if ( m_iIterator == g_MapEntityRefs.InvalidIndex() )
  1869. {
  1870. // This shouldn't be possible. When we loaded the map, it should have used
  1871. // CTeamplayMapEntityFilter, which should have built the g_MapEntityRefs list
  1872. // with the same list of entities we're referring to here.
  1873. Assert( false );
  1874. return NULL;
  1875. }
  1876. else
  1877. {
  1878. CMapEntityRef &ref = g_MapEntityRefs[m_iIterator];
  1879. m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity.
  1880. if ( ref.m_iEdict == -1 || INDEXENT( ref.m_iEdict ) )
  1881. {
  1882. // Doh! The entity was delete and its slot was reused.
  1883. // Just use any old edict slot. This case sucks because we lose the baseline.
  1884. return CreateEntityByName( pClassname );
  1885. }
  1886. else
  1887. {
  1888. // Cool, the slot where this entity was is free again (most likely, the entity was
  1889. // freed above). Now create an entity with this specific index.
  1890. return CreateEntityByName( pClassname, ref.m_iEdict );
  1891. }
  1892. }
  1893. }
  1894. public:
  1895. int m_iIterator; // Iterator into g_MapEntityRefs.
  1896. CTeamplayRoundBasedRules *m_pRules;
  1897. };
  1898. CTeamplayMapEntityFilter filter;
  1899. filter.m_iIterator = g_MapEntityRefs.Head();
  1900. // DO NOT CALL SPAWN ON info_node ENTITIES!
  1901. MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
  1902. }
  1903. // Utility function
  1904. bool FindInList( const char **pStrings, const char *pToFind )
  1905. {
  1906. int i = 0;
  1907. while ( pStrings[i][0] != 0 )
  1908. {
  1909. if ( Q_stricmp( pStrings[i], pToFind ) == 0 )
  1910. return true;
  1911. i++;
  1912. }
  1913. return false;
  1914. }
  1915. //-----------------------------------------------------------------------------
  1916. // Purpose:
  1917. //-----------------------------------------------------------------------------
  1918. bool CTeamplayRoundBasedRules::ShouldCreateEntity( const char *pszClassName )
  1919. {
  1920. return !FindInList( s_PreserveEnts, pszClassName );
  1921. }
  1922. //-----------------------------------------------------------------------------
  1923. // Purpose:
  1924. //-----------------------------------------------------------------------------
  1925. bool CTeamplayRoundBasedRules::RoundCleanupShouldIgnore( CBaseEntity *pEnt )
  1926. {
  1927. return FindInList( s_PreserveEnts, pEnt->GetClassname() );
  1928. }
  1929. //-----------------------------------------------------------------------------
  1930. // Purpose: Sort function for sorting players by time spent connected ( user ID )
  1931. //-----------------------------------------------------------------------------
  1932. static int SwitchPlayersSort( CBaseMultiplayerPlayer * const *p1, CBaseMultiplayerPlayer * const *p2 )
  1933. {
  1934. // sort by score
  1935. return ( (*p2)->GetTeamBalanceScore() - (*p1)->GetTeamBalanceScore() );
  1936. }
  1937. //-----------------------------------------------------------------------------
  1938. // Purpose:
  1939. //-----------------------------------------------------------------------------
  1940. void CTeamplayRoundBasedRules::CheckRespawnWaves( void )
  1941. {
  1942. for ( int team = LAST_SHARED_TEAM+1; team < GetNumberOfTeams(); team++ )
  1943. {
  1944. if ( m_flNextRespawnWave[team] > gpGlobals->curtime )
  1945. continue;
  1946. RespawnTeam( team );
  1947. m_flNextRespawnWave.Set( team, gpGlobals->curtime + GetRespawnWaveMaxLength( team ) );
  1948. }
  1949. }
  1950. class CBalanceTeamGroup : public CSameTeamGroup
  1951. {
  1952. typedef CSameTeamGroup BaseClass;
  1953. public:
  1954. CBalanceTeamGroup();
  1955. CBalanceTeamGroup( const CBalanceTeamGroup &src );
  1956. virtual void Build( CGameRules *pGameRules, CBasePlayer *pl );
  1957. virtual void MaybeAddPlayer( CBasePlayer *pl );
  1958. bool AllAlive() const;
  1959. bool AllDead() const;
  1960. static bool Less( const CBalanceTeamGroup &p1, const CBalanceTeamGroup &p2 )
  1961. {
  1962. return CSameTeamGroup::Less( p1, p2 );
  1963. }
  1964. private:
  1965. int m_nAlive;
  1966. int m_nDead;
  1967. };
  1968. CBalanceTeamGroup::CBalanceTeamGroup() :
  1969. m_nAlive( 0 ),
  1970. m_nDead( 0 )
  1971. {
  1972. }
  1973. CBalanceTeamGroup::CBalanceTeamGroup( const CBalanceTeamGroup &src )
  1974. {
  1975. m_nAlive = src.m_nAlive;
  1976. m_nDead = src.m_nDead;
  1977. }
  1978. bool CBalanceTeamGroup::AllAlive() const
  1979. {
  1980. return m_nAlive > 0 && m_nDead == 0;
  1981. }
  1982. bool CBalanceTeamGroup::AllDead() const
  1983. {
  1984. return m_nDead > 0 && m_nAlive == 0;
  1985. }
  1986. void CBalanceTeamGroup::Build( CGameRules *pGameRules, CBasePlayer *pl )
  1987. {
  1988. m_Players.RemoveAll();
  1989. m_nScore = INT_MIN;
  1990. m_nAlive = m_nDead = 0;
  1991. // These are built by the "parent" player
  1992. if ( pGameRules->ForceSplitScreenPlayersOnToSameTeam() &&
  1993. pl->IsSplitScreenPlayer() )
  1994. return;
  1995. MaybeAddPlayer( pl );
  1996. if ( !pGameRules->ForceSplitScreenPlayersOnToSameTeam() )
  1997. return;
  1998. CUtlVector< CHandle< CBasePlayer > > &list = pl->GetSplitScreenPlayers();
  1999. for ( int i = 0; i < list.Count(); ++i )
  2000. {
  2001. MaybeAddPlayer( list[ i ] );
  2002. }
  2003. }
  2004. void CBalanceTeamGroup::MaybeAddPlayer( CBasePlayer *pl )
  2005. {
  2006. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( pl );
  2007. if ( pPlayer )
  2008. {
  2009. int nScore = pPlayer->CalculateTeamBalanceScore();
  2010. pPlayer->SetTeamBalanceScore( nScore );
  2011. if ( nScore > m_nScore )
  2012. {
  2013. m_nScore = nScore;
  2014. }
  2015. if ( pPlayer->IsAlive() )
  2016. {
  2017. ++m_nAlive;
  2018. }
  2019. else
  2020. {
  2021. ++m_nDead;
  2022. }
  2023. m_Players.AddToTail( pl );
  2024. }
  2025. }
  2026. //-----------------------------------------------------------------------------
  2027. // Purpose: Return true if the teams are balanced after this function
  2028. //-----------------------------------------------------------------------------
  2029. void CTeamplayRoundBasedRules::BalanceTeams( bool bRequireSwitcheesToBeDead )
  2030. {
  2031. if ( mp_autoteambalance.GetBool() == false || IsInArenaMode() == true )
  2032. {
  2033. return;
  2034. }
  2035. // we don't balance for a period of time at the start of the game
  2036. if ( gpGlobals->curtime < m_flStartBalancingTeamsAt )
  2037. {
  2038. return;
  2039. }
  2040. // wrap with this bool, indicates it's a round running switch and not a between rounds insta-switch
  2041. if ( bRequireSwitcheesToBeDead )
  2042. {
  2043. #ifndef CSTRIKE_DLL
  2044. // we don't balance if there is less than 60 seconds on the active timer
  2045. CTeamRoundTimer *pActiveTimer = GetActiveRoundTimer();
  2046. if ( pActiveTimer && pActiveTimer->GetTimeRemaining() < 60 )
  2047. {
  2048. return;
  2049. }
  2050. #endif // CSTRIKE_DLL
  2051. }
  2052. int iHeaviestTeam = TEAM_UNASSIGNED, iLightestTeam = TEAM_UNASSIGNED;
  2053. // Figure out if we're unbalanced
  2054. if ( !AreTeamsUnbalanced( iHeaviestTeam, iLightestTeam ) )
  2055. {
  2056. m_flFoundUnbalancedTeamsTime = -1;
  2057. m_bPrintedUnbalanceWarning = false;
  2058. return;
  2059. }
  2060. if ( m_flFoundUnbalancedTeamsTime < 0 )
  2061. {
  2062. m_flFoundUnbalancedTeamsTime = gpGlobals->curtime;
  2063. }
  2064. // if teams have been unbalanced for X seconds, play a warning
  2065. if ( !m_bPrintedUnbalanceWarning && ( ( gpGlobals->curtime - m_flFoundUnbalancedTeamsTime ) > 1.0 ) )
  2066. {
  2067. // print unbalance warning
  2068. UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_auto_team_balance_in", "5" );
  2069. m_bPrintedUnbalanceWarning = true;
  2070. }
  2071. // teams are unbalanced, figure out players that need to be switched
  2072. CTeam *pHeavyTeam = GetGlobalTeam( iHeaviestTeam );
  2073. CTeam *pLightTeam = GetGlobalTeam( iLightestTeam );
  2074. Assert( pHeavyTeam && pLightTeam );
  2075. int iNumSwitchesRequired = ( pHeavyTeam->GetNumPlayers() - pLightTeam->GetNumPlayers() ) / 2;
  2076. // sort the eligible players and switch the n best candidates
  2077. CUtlRBTree<CBalanceTeamGroup> vecGroups( 0, 0, CBalanceTeamGroup::Less );
  2078. CBaseMultiplayerPlayer *pPlayer;
  2079. int i;
  2080. for ( i = 0; i < pHeavyTeam->GetNumPlayers(); i++ )
  2081. {
  2082. CBasePlayer *pl = pHeavyTeam->GetPlayer(i);
  2083. if ( !pl )
  2084. continue;
  2085. CBalanceTeamGroup group;
  2086. group.Build( this, pl );
  2087. if ( group.Count() <= 0 )
  2088. continue;
  2089. if ( bRequireSwitcheesToBeDead && !group.AllDead() )
  2090. continue;
  2091. // Too many players
  2092. if ( group.Count() > iNumSwitchesRequired )
  2093. continue;
  2094. vecGroups.Insert( group );
  2095. }
  2096. for ( int i=vecGroups.FirstInorder(); i != vecGroups.InvalidIndex(); i = vecGroups.NextInorder( i ) )
  2097. {
  2098. if ( iNumSwitchesRequired <= 0 )
  2099. break;
  2100. CSameTeamGroup &group = vecGroups[ i ];
  2101. if ( !group.Count() || group.Count() > iNumSwitchesRequired )
  2102. continue;
  2103. for ( int j = 0; j < group.Count(); ++j )
  2104. {
  2105. pPlayer = ToBaseMultiplayerPlayer( group.GetPlayer( j ) );
  2106. if ( !pPlayer )
  2107. continue;
  2108. pPlayer->ChangeTeam( iLightestTeam );
  2109. pPlayer->SetLastForcedChangeTeamTimeToNow();
  2110. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_teambalanced_player" );
  2111. if ( event )
  2112. {
  2113. event->SetInt( "player", pPlayer->entindex() );
  2114. event->SetInt( "team", iLightestTeam );
  2115. gameeventmanager->FireEvent( event );
  2116. }
  2117. // tell people that we've switched this player
  2118. UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_player_was_team_balanced", CFmtStr( "#ENTNAME[%d]%s", pPlayer->entindex(), pPlayer->GetPlayerName() ) );
  2119. iNumSwitchesRequired--;
  2120. }
  2121. }
  2122. }
  2123. //-----------------------------------------------------------------------------
  2124. // Purpose:
  2125. //-----------------------------------------------------------------------------
  2126. void CTeamplayRoundBasedRules::ResetScores( void )
  2127. {
  2128. if ( m_bResetTeamScores )
  2129. {
  2130. for ( int i = 0; i < GetNumberOfTeams(); i++ )
  2131. {
  2132. GetGlobalTeam( i )->ResetScores();
  2133. }
  2134. }
  2135. if ( m_bResetPlayerScores )
  2136. {
  2137. CBasePlayer *pPlayer;
  2138. for( int i = 1; i <= gpGlobals->maxClients; i++ )
  2139. {
  2140. pPlayer = ToBasePlayer( UTIL_PlayerByIndex( i ) );
  2141. if (pPlayer == NULL)
  2142. continue;
  2143. if (FNullEnt( pPlayer->edict() ))
  2144. continue;
  2145. pPlayer->ResetScores();
  2146. }
  2147. }
  2148. if ( m_bResetRoundsPlayed )
  2149. {
  2150. m_nRoundsPlayed = 0;
  2151. }
  2152. // assume we always want to reset the scores
  2153. // unless someone tells us not to for the next reset
  2154. m_bResetTeamScores = true;
  2155. m_bResetPlayerScores = true;
  2156. m_bResetRoundsPlayed = true;
  2157. //m_flStopWatchTime = -1.0f;
  2158. }
  2159. //-----------------------------------------------------------------------------
  2160. // Purpose:
  2161. //-----------------------------------------------------------------------------
  2162. void CTeamplayRoundBasedRules::ResetMapTime( void )
  2163. {
  2164. m_flMapResetTime = gpGlobals->curtime;
  2165. // send an event with the time remaining until map change
  2166. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_map_time_remaining" );
  2167. if ( event )
  2168. {
  2169. event->SetInt( "seconds", GetTimeLeft() );
  2170. gameeventmanager->FireEvent( event );
  2171. }
  2172. }
  2173. //-----------------------------------------------------------------------------
  2174. // Purpose:
  2175. //-----------------------------------------------------------------------------
  2176. void CTeamplayRoundBasedRules::PlayStartRoundVoice( void )
  2177. {
  2178. for ( int i = LAST_SHARED_TEAM+1; i < GetNumberOfTeams(); i++ )
  2179. {
  2180. BroadcastSound( i, UTIL_VarArgs("Game.TeamRoundStart%d", i ) );
  2181. }
  2182. }
  2183. //-----------------------------------------------------------------------------
  2184. // Purpose:
  2185. //-----------------------------------------------------------------------------
  2186. void CTeamplayRoundBasedRules::PlayWinSong( int team )
  2187. {
  2188. if ( team == TEAM_UNASSIGNED )
  2189. {
  2190. PlayStalemateSong();
  2191. }
  2192. else
  2193. {
  2194. BroadcastSound( TEAM_UNASSIGNED, UTIL_VarArgs("Game.TeamWin%d", team ) );
  2195. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  2196. {
  2197. if ( i == team )
  2198. {
  2199. BroadcastSound( i, "Game.YourTeamWon" );
  2200. }
  2201. else
  2202. {
  2203. BroadcastSound( i, "Game.YourTeamLost" );
  2204. }
  2205. }
  2206. }
  2207. }
  2208. //-----------------------------------------------------------------------------
  2209. // Purpose:
  2210. //-----------------------------------------------------------------------------
  2211. void CTeamplayRoundBasedRules::PlaySuddenDeathSong( void )
  2212. {
  2213. BroadcastSound( TEAM_UNASSIGNED, "Game.SuddenDeath" );
  2214. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  2215. {
  2216. BroadcastSound( i, "Game.SuddenDeath" );
  2217. }
  2218. }
  2219. //-----------------------------------------------------------------------------
  2220. // Purpose:
  2221. //-----------------------------------------------------------------------------
  2222. void CTeamplayRoundBasedRules::PlayStalemateSong( void )
  2223. {
  2224. BroadcastSound( TEAM_UNASSIGNED, "Game.Stalemate" );
  2225. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  2226. {
  2227. BroadcastSound( i, "Game.Stalemate" );
  2228. }
  2229. }
  2230. //-----------------------------------------------------------------------------
  2231. // Purpose:
  2232. //-----------------------------------------------------------------------------
  2233. void CTeamplayRoundBasedRules::BroadcastSound( int iTeam, const char *sound )
  2234. {
  2235. //send it to everyone
  2236. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_broadcast_audio" );
  2237. if ( event )
  2238. {
  2239. event->SetInt( "team", iTeam );
  2240. event->SetString( "sound", sound );
  2241. gameeventmanager->FireEvent( event );
  2242. }
  2243. }
  2244. //-----------------------------------------------------------------------------
  2245. // Purpose:
  2246. //-----------------------------------------------------------------------------
  2247. void CTeamplayRoundBasedRules::AddPlayedRound( string_t strName )
  2248. {
  2249. if ( strName != NULL_STRING )
  2250. {
  2251. m_iszPreviousRounds.AddToHead( strName );
  2252. // we only need to store the last two rounds that we've played
  2253. if ( m_iszPreviousRounds.Count() > 2 )
  2254. {
  2255. // remove all but two of the entries (should only ever have to remove 1 when we're at 3)
  2256. for ( int i = m_iszPreviousRounds.Count() - 1 ; i > 1 ; i-- )
  2257. {
  2258. m_iszPreviousRounds.Remove( i );
  2259. }
  2260. }
  2261. }
  2262. }
  2263. //-----------------------------------------------------------------------------
  2264. // Purpose:
  2265. //-----------------------------------------------------------------------------
  2266. bool CTeamplayRoundBasedRules::IsPreviouslyPlayedRound( string_t strName )
  2267. {
  2268. return ( m_iszPreviousRounds.Find( strName ) != m_iszPreviousRounds.InvalidIndex() );
  2269. }
  2270. //-----------------------------------------------------------------------------
  2271. // Purpose:
  2272. //-----------------------------------------------------------------------------
  2273. string_t CTeamplayRoundBasedRules::GetLastPlayedRound( void )
  2274. {
  2275. return ( m_iszPreviousRounds.Count() ? m_iszPreviousRounds[0] : NULL_STRING );
  2276. }
  2277. //-----------------------------------------------------------------------------
  2278. // Purpose:
  2279. //-----------------------------------------------------------------------------
  2280. CTeamRoundTimer *CTeamplayRoundBasedRules::GetActiveRoundTimer( void )
  2281. {
  2282. #ifdef TF_DLL
  2283. int iTimerEntIndex = ObjectiveResource()->GetTimerInHUD();
  2284. return ( dynamic_cast<CTeamRoundTimer *>( UTIL_EntityByIndex( iTimerEntIndex ) ) );
  2285. #else
  2286. return NULL;
  2287. #endif
  2288. }
  2289. #endif // GAME_DLL
  2290. //-----------------------------------------------------------------------------
  2291. // Purpose: How long are the respawn waves for this team currently?
  2292. //-----------------------------------------------------------------------------
  2293. float CTeamplayRoundBasedRules::GetRespawnWaveMaxLength( int iTeam, bool bScaleWithNumPlayers /* = true */ )
  2294. {
  2295. if (State_Get() != GR_STATE_RND_RUNNING)
  2296. return 0;
  2297. if ( mp_disable_respawn_times.GetBool() == true )
  2298. return 0.0f;
  2299. //Let's just turn off respawn times while players are messing around waiting for the tournament to start
  2300. if ( IsInTournamentMode() == true && IsInPreMatch() == true )
  2301. return 0.0f;
  2302. float flTime = ( ( m_TeamRespawnWaveTimes[iTeam] >= 0 ) ? m_TeamRespawnWaveTimes[iTeam] : mp_respawnwavetime.GetFloat() );
  2303. // For long respawn times, scale the time as the number of players drops
  2304. if ( bScaleWithNumPlayers && flTime > 5 )
  2305. {
  2306. flTime = MAX( 5, flTime * GetRespawnTimeScalar(iTeam) );
  2307. }
  2308. return flTime;
  2309. }
  2310. //-----------------------------------------------------------------------------
  2311. // Purpose: returns true if we are running tournament mode
  2312. //-----------------------------------------------------------------------------
  2313. bool CTeamplayRoundBasedRules::IsInTournamentMode( void )
  2314. {
  2315. return mp_tournament.GetBool();
  2316. }
  2317. //-----------------------------------------------------------------------------
  2318. // Purpose: returns true if we should even bother to do balancing stuff
  2319. //-----------------------------------------------------------------------------
  2320. bool CTeamplayRoundBasedRules::ShouldBalanceTeams( void )
  2321. {
  2322. if ( IsInTournamentMode() == true )
  2323. return false;
  2324. if ( mp_teams_unbalance_limit.GetInt() <= 0 )
  2325. return false;
  2326. return true;
  2327. }
  2328. //-----------------------------------------------------------------------------
  2329. // Purpose: returns true if the passed team change would cause unbalanced teams
  2330. //-----------------------------------------------------------------------------
  2331. bool CTeamplayRoundBasedRules::WouldChangeUnbalanceTeams( int iNumPlayersToMove, int iNewTeam, int iCurrentTeam )
  2332. {
  2333. // players are allowed to change to their own team
  2334. if( iNewTeam == iCurrentTeam )
  2335. return false;
  2336. // if mp_teams_unbalance_limit is 0, don't check
  2337. if ( ShouldBalanceTeams() == false )
  2338. return false;
  2339. // if they are joining a non-playing team, allow
  2340. if ( iNewTeam < FIRST_GAME_TEAM )
  2341. return false;
  2342. CTeam *pNewTeam = GetGlobalTeam( iNewTeam );
  2343. if ( !pNewTeam )
  2344. {
  2345. Assert( 0 );
  2346. return true;
  2347. }
  2348. // Figure out how big the new team would be
  2349. int iNewTeamPlayers = pNewTeam->GetNumPlayers() + iNumPlayersToMove;
  2350. // for each game team
  2351. int i = FIRST_GAME_TEAM;
  2352. CTeam *pTeam;
  2353. for ( pTeam = GetGlobalTeam(i); pTeam != NULL; pTeam = GetGlobalTeam(++i) )
  2354. {
  2355. if ( pTeam == pNewTeam )
  2356. continue;
  2357. int iNumPlayers = pTeam->GetNumPlayers();
  2358. if ( i == iCurrentTeam )
  2359. {
  2360. // Figure out how big the old team would end up being
  2361. iNumPlayers = MAX( 0, iNumPlayers-iNumPlayersToMove );
  2362. }
  2363. if ( ( iNewTeamPlayers - iNumPlayers ) > ( mp_teams_unbalance_limit.GetInt() + iNumPlayersToMove - 1 ) )
  2364. {
  2365. return true;
  2366. }
  2367. }
  2368. return false;
  2369. }
  2370. //-----------------------------------------------------------------------------
  2371. // Purpose:
  2372. //-----------------------------------------------------------------------------
  2373. bool CTeamplayRoundBasedRules::AreTeamsUnbalanced( int &iHeaviestTeam, int &iLightestTeam )
  2374. {
  2375. if ( IsInArenaMode() == false )
  2376. {
  2377. if ( mp_teams_unbalance_limit.GetInt() <= 0 || IsInTournamentMode() == true )
  2378. {
  2379. return false;
  2380. }
  2381. }
  2382. #ifndef CLIENT_DLL
  2383. if ( IsInCommentaryMode() )
  2384. return false;
  2385. #endif
  2386. int iMostPlayers = 0;
  2387. int iLeastPlayers = MAX_PLAYERS + 1;
  2388. int i = FIRST_GAME_TEAM;
  2389. for ( CTeam *pTeam = GetGlobalTeam(i); pTeam != NULL; pTeam = GetGlobalTeam(++i) )
  2390. {
  2391. int iNumPlayers = pTeam->GetNumPlayers();
  2392. if ( iNumPlayers < iLeastPlayers )
  2393. {
  2394. iLeastPlayers = iNumPlayers;
  2395. iLightestTeam = i;
  2396. }
  2397. if ( iNumPlayers > iMostPlayers )
  2398. {
  2399. iMostPlayers = iNumPlayers;
  2400. iHeaviestTeam = i;
  2401. }
  2402. }
  2403. if ( IsInArenaMode() == true )
  2404. {
  2405. if ( iMostPlayers == 0 && iMostPlayers == iLeastPlayers )
  2406. return true;
  2407. if ( iMostPlayers != iLeastPlayers )
  2408. return true;
  2409. return false;
  2410. }
  2411. if ( ( iMostPlayers - iLeastPlayers ) > mp_teams_unbalance_limit.GetInt() )
  2412. {
  2413. return true;
  2414. }
  2415. return false;
  2416. }
  2417. #ifdef CLIENT_DLL
  2418. //-----------------------------------------------------------------------------
  2419. // Purpose:
  2420. //-----------------------------------------------------------------------------
  2421. void CTeamplayRoundBasedRules::SetRoundState( int iRoundState )
  2422. {
  2423. m_iRoundState = iRoundState;
  2424. m_flLastRoundStateChangeTime = gpGlobals->curtime;
  2425. }
  2426. //-----------------------------------------------------------------------------
  2427. // Purpose:
  2428. //-----------------------------------------------------------------------------
  2429. void CTeamplayRoundBasedRules::OnPreDataChanged( DataUpdateType_t updateType )
  2430. {
  2431. m_bOldInWaitingForPlayers = m_bInWaitingForPlayers;
  2432. m_bOldInOvertime = m_bInOvertime;
  2433. m_bOldInSetup = m_bInSetup;
  2434. }
  2435. //-----------------------------------------------------------------------------
  2436. // Purpose:
  2437. //-----------------------------------------------------------------------------
  2438. void CTeamplayRoundBasedRules::OnDataChanged( DataUpdateType_t updateType )
  2439. {
  2440. if ( updateType == DATA_UPDATE_CREATED ||
  2441. m_bOldInWaitingForPlayers != m_bInWaitingForPlayers ||
  2442. m_bOldInOvertime != m_bInOvertime ||
  2443. m_bOldInSetup != m_bInSetup )
  2444. {
  2445. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_update_timer" );
  2446. if ( event )
  2447. {
  2448. gameeventmanager->FireEventClientSide( event );
  2449. }
  2450. }
  2451. if ( updateType == DATA_UPDATE_CREATED )
  2452. {
  2453. if ( State_Get() == GR_STATE_STALEMATE )
  2454. {
  2455. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_stalemate" );
  2456. if ( event )
  2457. {
  2458. event->SetInt( "reason", STALEMATE_JOIN_MID );
  2459. gameeventmanager->FireEventClientSide( event );
  2460. }
  2461. }
  2462. }
  2463. if ( m_bInOvertime && ( m_bOldInOvertime != m_bInOvertime ) )
  2464. {
  2465. HandleOvertimeBegin();
  2466. }
  2467. }
  2468. #endif // CLIENT_DLL