Team Fortress 2 Source Code as on 22/4/2020
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.

3745 lines
108 KiB

  1. //========= Copyright 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. #include "c_playerresource.h"
  15. #define CTeam C_Team
  16. #else
  17. #include "viewport_panel_names.h"
  18. #include "team.h"
  19. #include "mapentities.h"
  20. #include "gameinterface.h"
  21. #include "eventqueue.h"
  22. #include "team_control_point_master.h"
  23. #include "team_train_watcher.h"
  24. #include "serverbenchmark_base.h"
  25. #if defined( REPLAY_ENABLED )
  26. #include "replay/ireplaysystem.h"
  27. #include "replay/iserverreplaycontext.h"
  28. #include "replay/ireplaysessionrecorder.h"
  29. #endif // REPLAY_ENABLED
  30. #endif
  31. #if defined(TF_CLIENT_DLL) || defined(TF_DLL)
  32. #include "tf_gamerules.h"
  33. #ifdef GAME_DLL
  34. #include "player_vs_environment/tf_population_manager.h"
  35. #include "../server/tf/tf_gc_server.h"
  36. #include "../server/tf/tf_objective_resource.h"
  37. #else
  38. #include "../client/tf/tf_gc_client.h"
  39. #include "../client/tf/c_tf_objective_resource.h"
  40. #endif // GAME_DLL
  41. #endif
  42. // memdbgon must be the last include file in a .cpp file!!!
  43. #include "tier0/memdbgon.h"
  44. #ifndef CLIENT_DLL
  45. CUtlVector< CHandle<CTeamControlPointMaster> > g_hControlPointMasters;
  46. extern bool IsInCommentaryMode( void );
  47. #if defined( REPLAY_ENABLED )
  48. extern IReplaySystem *g_pReplay;
  49. #endif // REPLAY_ENABLED
  50. #endif
  51. extern ConVar spec_freeze_time;
  52. extern ConVar spec_freeze_traveltime;
  53. #ifdef CLIENT_DLL
  54. void RecvProxy_TeamplayRoundState( const CRecvProxyData *pData, void *pStruct, void *pOut )
  55. {
  56. CTeamplayRoundBasedRules *pGamerules = ( CTeamplayRoundBasedRules *)pStruct;
  57. int iRoundState = pData->m_Value.m_Int;
  58. pGamerules->SetRoundState( iRoundState );
  59. }
  60. void RecvProxy_StopWatch( const CRecvProxyData *pData, void *pStruct, void *pOut )
  61. {
  62. *(bool*)(pOut) = ( pData->m_Value.m_Int > 0 );
  63. IGameEvent *event = gameeventmanager->CreateEvent( "stop_watch_changed" );
  64. if ( event )
  65. {
  66. // Client-side once it's actually happened
  67. gameeventmanager->FireEventClientSide( event );
  68. }
  69. }
  70. #endif
  71. BEGIN_NETWORK_TABLE_NOBASE( CTeamplayRoundBasedRules, DT_TeamplayRoundBasedRules )
  72. #ifdef CLIENT_DLL
  73. RecvPropInt( RECVINFO( m_iRoundState ), 0, RecvProxy_TeamplayRoundState ),
  74. RecvPropBool( RECVINFO( m_bInWaitingForPlayers ) ),
  75. RecvPropInt( RECVINFO( m_iWinningTeam ) ),
  76. RecvPropInt( RECVINFO( m_bInOvertime ) ),
  77. RecvPropInt( RECVINFO( m_bInSetup ) ),
  78. RecvPropInt( RECVINFO( m_bSwitchedTeamsThisRound ) ),
  79. RecvPropBool( RECVINFO( m_bAwaitingReadyRestart ) ),
  80. RecvPropTime( RECVINFO( m_flRestartRoundTime ) ),
  81. RecvPropTime( RECVINFO( m_flMapResetTime ) ),
  82. RecvPropInt( RECVINFO( m_nRoundsPlayed ) ),
  83. RecvPropArray3( RECVINFO_ARRAY(m_flNextRespawnWave), RecvPropTime( RECVINFO(m_flNextRespawnWave[0]) ) ),
  84. RecvPropArray3( RECVINFO_ARRAY(m_TeamRespawnWaveTimes), RecvPropFloat( RECVINFO(m_TeamRespawnWaveTimes[0]) ) ),
  85. RecvPropArray3( RECVINFO_ARRAY(m_bTeamReady), RecvPropBool( RECVINFO(m_bTeamReady[0]) ) ),
  86. RecvPropBool( RECVINFO( m_bStopWatch ), 0, RecvProxy_StopWatch ),
  87. RecvPropBool( RECVINFO( m_bMultipleTrains ) ),
  88. RecvPropArray3( RECVINFO_ARRAY(m_bPlayerReady), RecvPropBool( RECVINFO(m_bPlayerReady[0]) ) ),
  89. RecvPropBool( RECVINFO( m_bCheatsEnabledDuringLevel ) ),
  90. RecvPropTime( RECVINFO( m_flCountdownTime ) ),
  91. RecvPropTime( RECVINFO( m_flStateTransitionTime ) ),
  92. #else
  93. SendPropInt( SENDINFO( m_iRoundState ), 5 ),
  94. SendPropBool( SENDINFO( m_bInWaitingForPlayers ) ),
  95. SendPropInt( SENDINFO( m_iWinningTeam ), 3, SPROP_UNSIGNED ),
  96. SendPropBool( SENDINFO( m_bInOvertime ) ),
  97. SendPropBool( SENDINFO( m_bInSetup ) ),
  98. SendPropBool( SENDINFO( m_bSwitchedTeamsThisRound ) ),
  99. SendPropBool( SENDINFO( m_bAwaitingReadyRestart ) ),
  100. SendPropTime( SENDINFO( m_flRestartRoundTime ) ),
  101. SendPropTime( SENDINFO( m_flMapResetTime ) ),
  102. SendPropInt( SENDINFO( m_nRoundsPlayed ), 4, SPROP_UNSIGNED ),
  103. SendPropArray3( SENDINFO_ARRAY3(m_flNextRespawnWave), SendPropTime( SENDINFO_ARRAY(m_flNextRespawnWave) ) ),
  104. SendPropArray3( SENDINFO_ARRAY3(m_TeamRespawnWaveTimes), SendPropFloat( SENDINFO_ARRAY(m_TeamRespawnWaveTimes) ) ),
  105. SendPropArray3( SENDINFO_ARRAY3(m_bTeamReady), SendPropBool( SENDINFO_ARRAY(m_bTeamReady) ) ),
  106. SendPropBool( SENDINFO( m_bStopWatch ) ),
  107. SendPropBool( SENDINFO( m_bMultipleTrains ) ),
  108. SendPropArray3( SENDINFO_ARRAY3(m_bPlayerReady), SendPropBool( SENDINFO_ARRAY(m_bPlayerReady) ) ),
  109. SendPropBool( SENDINFO( m_bCheatsEnabledDuringLevel ) ),
  110. SendPropTime( SENDINFO( m_flCountdownTime ) ),
  111. SendPropTime( SENDINFO( m_flStateTransitionTime ) ),
  112. #endif
  113. END_NETWORK_TABLE()
  114. IMPLEMENT_NETWORKCLASS_ALIASED( TeamplayRoundBasedRulesProxy, DT_TeamplayRoundBasedRulesProxy )
  115. #ifdef CLIENT_DLL
  116. void RecvProxy_TeamplayRoundBasedRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
  117. {
  118. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  119. Assert( pRules );
  120. *pOut = pRules;
  121. }
  122. BEGIN_RECV_TABLE( CTeamplayRoundBasedRulesProxy, DT_TeamplayRoundBasedRulesProxy )
  123. RecvPropDataTable( "teamplayroundbased_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_TeamplayRoundBasedRules ), RecvProxy_TeamplayRoundBasedRules )
  124. END_RECV_TABLE()
  125. void CTeamplayRoundBasedRulesProxy::OnPreDataChanged( DataUpdateType_t updateType )
  126. {
  127. BaseClass::OnPreDataChanged( updateType );
  128. // Reroute data changed calls to the non-entity gamerules
  129. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  130. Assert( pRules );
  131. pRules->OnPreDataChanged(updateType);
  132. }
  133. void CTeamplayRoundBasedRulesProxy::OnDataChanged( DataUpdateType_t updateType )
  134. {
  135. BaseClass::OnDataChanged( updateType );
  136. // Reroute data changed calls to the non-entity gamerules
  137. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  138. Assert( pRules );
  139. pRules->OnDataChanged(updateType);
  140. }
  141. #else
  142. void* SendProxy_TeamplayRoundBasedRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
  143. {
  144. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  145. Assert( pRules );
  146. pRecipients->SetAllRecipients();
  147. return pRules;
  148. }
  149. BEGIN_SEND_TABLE( CTeamplayRoundBasedRulesProxy, DT_TeamplayRoundBasedRulesProxy )
  150. SendPropDataTable( "teamplayroundbased_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_TeamplayRoundBasedRules ), SendProxy_TeamplayRoundBasedRules )
  151. END_SEND_TABLE()
  152. BEGIN_DATADESC( CTeamplayRoundBasedRulesProxy )
  153. // Inputs.
  154. DEFINE_INPUTFUNC( FIELD_BOOLEAN, "SetStalemateOnTimelimit", InputSetStalemateOnTimelimit ),
  155. END_DATADESC()
  156. //-----------------------------------------------------------------------------
  157. // Purpose:
  158. //-----------------------------------------------------------------------------
  159. void CTeamplayRoundBasedRulesProxy::InputSetStalemateOnTimelimit( inputdata_t &inputdata )
  160. {
  161. TeamplayRoundBasedRules()->SetStalemateOnTimelimit( inputdata.value.Bool() );
  162. }
  163. #endif
  164. #ifdef GAME_DLL
  165. void WinlimitChanged(IConVar *var, const char *pOldValue, float flOldValue)
  166. {
  167. IGameEvent * event = gameeventmanager->CreateEvent("winlimit_changed");
  168. if (event)
  169. {
  170. gameeventmanager->FireEvent(event);
  171. }
  172. }
  173. #endif // GAME_DLL
  174. 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." );
  175. 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." );
  176. ConVar mp_respawnwavetime( "mp_respawnwavetime", "10.0", FCVAR_NOTIFY | FCVAR_REPLICATED, "Time between respawn waves." );
  177. ConVar mp_capdeteriorate_time( "mp_capdeteriorate_time", "90.0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "Time it takes for a full capture point to deteriorate." );
  178. ConVar mp_tournament( "mp_tournament", "0", FCVAR_REPLICATED | FCVAR_NOTIFY );
  179. ConVar mp_tournament_post_match_period( "mp_tournament_post_match_period", "90", FCVAR_REPLICATED, "The amount of time (in seconds) before the server resets post-match.", true, 5, true, 300 );
  180. #if defined( TF_CLIENT_DLL ) || defined( TF_DLL )
  181. ConVar mp_highlander( "mp_highlander", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "Allow only 1 of each player class type." );
  182. #endif
  183. #ifdef TF_DLL
  184. extern ConVar tf_competitive_preround_duration;
  185. extern ConVar tf_competitive_preround_countdown_duration;
  186. #endif // TF_DLL
  187. //Arena Mode
  188. 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 );
  189. ConVar tf_arena_round_time( "tf_arena_round_time", "0", FCVAR_NOTIFY | FCVAR_REPLICATED );
  190. ConVar tf_arena_max_streak( "tf_arena_max_streak", "3", FCVAR_NOTIFY | FCVAR_REPLICATED, "Teams will be scrambled if one team reaches this streak" );
  191. ConVar tf_arena_use_queue( "tf_arena_use_queue", "1", FCVAR_REPLICATED | FCVAR_NOTIFY, "Enables the spectator queue system for Arena." );
  192. ConVar mp_teams_unbalance_limit( "mp_teams_unbalance_limit", "1", FCVAR_REPLICATED,
  193. "Teams are unbalanced when one team has this many more players than the other team. (0 disables check)",
  194. true, 0, // min value
  195. true, 30 // max value
  196. );
  197. ConVar mp_maxrounds( "mp_maxrounds", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "max number of rounds to play before server changes maps", true, 0, false, 0 );
  198. ConVar mp_winlimit( "mp_winlimit", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "Max score one team can reach before server changes maps", true, 0, false, 0
  199. #ifdef GAME_DLL
  200. , WinlimitChanged
  201. #endif // GAME_DLL
  202. );
  203. ConVar mp_disable_respawn_times( "mp_disable_respawn_times", "0", FCVAR_NOTIFY | FCVAR_REPLICATED );
  204. ConVar mp_bonusroundtime( "mp_bonusroundtime", "15", FCVAR_REPLICATED, "Time after round win until round restarts", true, 5, true, 15 );
  205. ConVar mp_stalemate_meleeonly( "mp_stalemate_meleeonly", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "Restrict everyone to melee weapons only while in Sudden Death." );
  206. ConVar mp_forceautoteam( "mp_forceautoteam", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "Automatically assign players to teams when joining." );
  207. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  208. ConVar mp_developer( "mp_developer", "0", FCVAR_ARCHIVE | FCVAR_REPLICATED | FCVAR_NOTIFY, "1: basic conveniences (instant respawn and class change, etc). 2: add combat conveniences (infinite ammo, buddha, etc)" );
  209. #endif // _DEBUG || STAGING_ONLY
  210. #ifdef GAME_DLL
  211. ConVar mp_showroundtransitions( "mp_showroundtransitions", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Show gamestate round transitions." );
  212. ConVar mp_enableroundwaittime( "mp_enableroundwaittime", "1", FCVAR_REPLICATED, "Enable timers to wait between rounds." );
  213. ConVar mp_showcleanedupents( "mp_showcleanedupents", "0", FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY, "Show entities that are removed on round respawn." );
  214. ConVar mp_restartround( "mp_restartround", "0", FCVAR_GAMEDLL, "If non-zero, the current round will restart in the specified number of seconds" );
  215. ConVar mp_stalemate_timelimit( "mp_stalemate_timelimit", "240", FCVAR_REPLICATED, "Timelimit (in seconds) of the stalemate round." );
  216. ConVar mp_autoteambalance( "mp_autoteambalance", "1", FCVAR_NOTIFY, "Automatically balance the teams based on mp_teams_unbalance_limit. 0 = off, 1 = forcibly switch, 2 = ask volunteers", true, 0, true, 2 );
  217. ConVar mp_stalemate_enable( "mp_stalemate_enable", "0", FCVAR_NOTIFY, "Enable/Disable stalemate mode." );
  218. ConVar mp_match_end_at_timelimit( "mp_match_end_at_timelimit", "0", FCVAR_NOTIFY, "Allow the match to end when mp_timelimit hits instead of waiting for the end of the current round." );
  219. ConVar mp_holiday_nogifts( "mp_holiday_nogifts", "0", FCVAR_NOTIFY, "Set to 1 to prevent holiday gifts from spawning when players are killed." );
  220. #ifdef STAGING_ONLY
  221. ConVar mp_state_debug( "mp_state_debug", "0", FCVAR_NOTIFY, "Set to 1 to print developer messages whenever game state changes." );
  222. #endif // STAGING_ONLY
  223. const char *m_pszRoundStateStrings[] =
  224. {
  225. "GR_STATE_INIT",
  226. "GR_STATE_PREGAME",
  227. "GR_STATE_STARTGAME",
  228. "GR_STATE_PREROUND",
  229. "GR_STATE_RND_RUNNING",
  230. "GR_STATE_TEAM_WIN",
  231. "GR_STATE_RESTART",
  232. "GR_STATE_STALEMATE",
  233. "GR_STATE_GAME_OVER",
  234. "GR_STATE_BONUS",
  235. "GR_STATE_BETWEEN_RNDS",
  236. };
  237. COMPILE_TIME_ASSERT( ARRAYSIZE( m_pszRoundStateStrings ) == GR_NUM_ROUND_STATES );
  238. //-----------------------------------------------------------------------------
  239. // Purpose:
  240. //-----------------------------------------------------------------------------
  241. void cc_SwitchTeams( const CCommand& args )
  242. {
  243. if ( UTIL_IsCommandIssuedByServerAdmin() )
  244. {
  245. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  246. if ( pRules )
  247. {
  248. pRules->SetSwitchTeams( true );
  249. mp_restartgame.SetValue( 5 );
  250. pRules->ShouldResetScores( false, false );
  251. pRules->ShouldResetRoundsPlayed( false );
  252. }
  253. }
  254. }
  255. static ConCommand mp_switchteams( "mp_switchteams", cc_SwitchTeams, "Switch teams and restart the game" );
  256. //-----------------------------------------------------------------------------
  257. // Purpose:
  258. //-----------------------------------------------------------------------------
  259. void cc_ScrambleTeams( const CCommand& args )
  260. {
  261. if ( UTIL_IsCommandIssuedByServerAdmin() )
  262. {
  263. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  264. if ( pRules )
  265. {
  266. pRules->SetScrambleTeams( true );
  267. mp_restartgame.SetValue( 5 );
  268. pRules->ShouldResetScores( true, false );
  269. if ( args.ArgC() == 2 )
  270. {
  271. // Don't reset the roundsplayed when mp_scrambleteams 2 is passed
  272. if ( atoi( args[1] ) == 2 )
  273. {
  274. pRules->ShouldResetRoundsPlayed( false );
  275. }
  276. }
  277. }
  278. }
  279. }
  280. static ConCommand mp_scrambleteams( "mp_scrambleteams", cc_ScrambleTeams, "Scramble the teams and restart the game" );
  281. ConVar mp_scrambleteams_auto( "mp_scrambleteams_auto", "1", FCVAR_NOTIFY, "Server will automatically scramble the teams if criteria met. Only works on dedicated servers." );
  282. ConVar mp_scrambleteams_auto_windifference( "mp_scrambleteams_auto_windifference", "2", FCVAR_NOTIFY, "Number of round wins a team must lead by in order to trigger an auto scramble." );
  283. // Classnames of entities that are preserved across round restarts
  284. static const char *s_PreserveEnts[] =
  285. {
  286. "player",
  287. "viewmodel",
  288. "worldspawn",
  289. "soundent",
  290. "ai_network",
  291. "ai_hint",
  292. "env_soundscape",
  293. "env_soundscape_proxy",
  294. "env_soundscape_triggerable",
  295. "env_sprite",
  296. "env_sun",
  297. "env_wind",
  298. "env_fog_controller",
  299. "func_wall",
  300. "func_illusionary",
  301. "info_node",
  302. "info_target",
  303. "info_node_hint",
  304. "point_commentary_node",
  305. "point_viewcontrol",
  306. "func_precipitation",
  307. "func_team_wall",
  308. "shadow_control",
  309. "sky_camera",
  310. "scene_manager",
  311. "trigger_soundscape",
  312. "commentary_auto",
  313. "point_commentary_node",
  314. "point_commentary_viewpoint",
  315. "bot_roster",
  316. "info_populator",
  317. "", // END Marker
  318. };
  319. CON_COMMAND_F( mp_forcewin, "Forces team to win", FCVAR_CHEAT )
  320. {
  321. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  322. return;
  323. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  324. if ( pRules )
  325. {
  326. int iTeam = TEAM_UNASSIGNED;
  327. if ( args.ArgC() == 1 )
  328. {
  329. // if no team specified, use player 1's team
  330. iTeam = UTIL_PlayerByIndex( 1 )->GetTeamNumber();
  331. }
  332. else if ( args.ArgC() == 2 )
  333. {
  334. // if team # specified, use that
  335. iTeam = atoi( args[1] );
  336. }
  337. else
  338. {
  339. Msg( "Usage: mp_forcewin <opt: team#>" );
  340. return;
  341. }
  342. int iWinReason = ( TEAM_UNASSIGNED == iTeam ? WINREASON_STALEMATE : WINREASON_ALL_POINTS_CAPTURED );
  343. pRules->SetWinningTeam( iTeam, iWinReason );
  344. }
  345. }
  346. #endif // GAME_DLL
  347. // Utility function
  348. bool FindInList( const char **pStrings, const char *pToFind )
  349. {
  350. int i = 0;
  351. while ( pStrings[i][0] != 0 )
  352. {
  353. if ( Q_stricmp( pStrings[i], pToFind ) == 0 )
  354. return true;
  355. i++;
  356. }
  357. return false;
  358. }
  359. //-----------------------------------------------------------------------------
  360. // Purpose:
  361. //-----------------------------------------------------------------------------
  362. CTeamplayRoundBasedRules::CTeamplayRoundBasedRules( void )
  363. {
  364. for ( int i = 0; i < MAX_TEAMS; i++ )
  365. {
  366. m_flNextRespawnWave.Set( i, 0 );
  367. m_TeamRespawnWaveTimes.Set( i, -1.0f );
  368. m_bTeamReady.Set( i, false );
  369. #ifdef GAME_DLL
  370. m_flOriginalTeamRespawnWaveTime[i] = -1.0f;
  371. #endif
  372. }
  373. m_flStopWatchTotalTime = -1.0f;
  374. m_bAllowBetweenRounds = true;
  375. m_iRoundState.Set( GR_STATE_INIT );
  376. m_bInOvertime.Set( false );
  377. m_bInSetup.Set( false );
  378. m_bSwitchedTeamsThisRound.Set( false );
  379. m_iWinningTeam.Set( TEAM_UNASSIGNED );
  380. m_iWinReason.Set( WINREASON_NONE );
  381. m_bInWaitingForPlayers.Set( false );
  382. m_bAwaitingReadyRestart.Set( false );
  383. m_flRestartRoundTime.Set( -1.0f );
  384. m_flMapResetTime.Set( 0.0f );
  385. m_bStopWatch.Set( false );
  386. m_bMultipleTrains.Set( false );
  387. m_bCheatsEnabledDuringLevel.Set( false );
  388. m_nRoundsPlayed.Set( 0 );
  389. m_flCountdownTime.Set( -1.0f );
  390. for ( int i = 0; i < MAX_PLAYERS; i++ )
  391. {
  392. m_bPlayerReady.Set( i, false );
  393. }
  394. #ifdef GAME_DLL
  395. ListenForGameEvent( "server_changelevel_failed" );
  396. m_pCurStateInfo = NULL;
  397. State_Transition( GR_STATE_PREGAME );
  398. m_bResetTeamScores = true;
  399. m_bResetPlayerScores = true;
  400. m_bResetRoundsPlayed = true;
  401. InitTeams();
  402. ResetMapTime();
  403. ResetScores();
  404. SetForceMapReset( true );
  405. SetRoundToPlayNext( NULL_STRING );
  406. m_bPrevRoundWasWaitingForPlayers = false;
  407. m_iszPreviousRounds.RemoveAll();
  408. SetFirstRoundPlayed( NULL_STRING );
  409. m_bAllowStalemateAtTimelimit = false;
  410. m_bChangelevelAfterStalemate = false;
  411. m_flRoundStartTime = 0.0f;
  412. m_flNewThrottledAlertTime = 0.0f;
  413. m_flStartBalancingTeamsAt = 0.0f;
  414. m_bPrintedUnbalanceWarning = false;
  415. m_flFoundUnbalancedTeamsTime = -1.0f;
  416. m_flWaitingForPlayersTimeEnds = -1.0f;
  417. m_flLastTeamWin = -1.0f;
  418. m_bUseAddScoreAnim = false;
  419. if ( IsInTournamentMode() )
  420. {
  421. m_bAwaitingReadyRestart.Set( true );
  422. }
  423. m_flAutoBalanceQueueTimeEnd = -1.0f;
  424. m_nAutoBalanceQueuePlayerIndex = -1;
  425. m_nAutoBalanceQueuePlayerScore = -1;
  426. SetDefLessFunc( m_GameTeams );
  427. ResetPlayerAndTeamReadyState();
  428. m_nLastEventFiredTime = 0.f;
  429. m_hWaitingForPlayersTimer = NULL;
  430. m_bStopWatchShouldBeTimedWin = false;
  431. #endif
  432. }
  433. #ifdef GAME_DLL
  434. //-----------------------------------------------------------------------------
  435. // Purpose:
  436. //-----------------------------------------------------------------------------
  437. void CTeamplayRoundBasedRules::SetTeamRespawnWaveTime( int iTeam, float flValue )
  438. {
  439. if ( flValue < 0 )
  440. {
  441. flValue = 0;
  442. }
  443. // initialized to -1 so we can try to determine if this is the first spawn time we have received for this team
  444. if ( m_flOriginalTeamRespawnWaveTime[iTeam] < 0 )
  445. {
  446. m_flOriginalTeamRespawnWaveTime[iTeam] = flValue;
  447. }
  448. m_TeamRespawnWaveTimes.Set( iTeam, flValue );
  449. }
  450. //-----------------------------------------------------------------------------
  451. // Purpose:
  452. //-----------------------------------------------------------------------------
  453. void CTeamplayRoundBasedRules::AddTeamRespawnWaveTime( int iTeam, float flValue )
  454. {
  455. float flAddAmount = flValue;
  456. float flCurrentSetting = m_TeamRespawnWaveTimes[iTeam];
  457. float flNewValue;
  458. if ( flCurrentSetting < 0 )
  459. {
  460. flCurrentSetting = mp_respawnwavetime.GetFloat();
  461. }
  462. // initialized to -1 so we can try to determine if this is the first spawn time we have received for this team
  463. if ( m_flOriginalTeamRespawnWaveTime[iTeam] < 0 )
  464. {
  465. m_flOriginalTeamRespawnWaveTime[iTeam] = flCurrentSetting;
  466. }
  467. flNewValue = flCurrentSetting + flAddAmount;
  468. if ( flNewValue < 0 )
  469. {
  470. flNewValue = 0;
  471. }
  472. m_TeamRespawnWaveTimes.Set( iTeam, flNewValue );
  473. }
  474. #endif
  475. //-----------------------------------------------------------------------------
  476. // Purpose: don't let us spawn before our freezepanel time would have ended, even if we skip it
  477. //-----------------------------------------------------------------------------
  478. float CTeamplayRoundBasedRules::GetNextRespawnWave( int iTeam, CBasePlayer *pPlayer )
  479. {
  480. if ( State_Get() == GR_STATE_STALEMATE )
  481. return 0;
  482. // If we are purely checking when the next respawn wave is for this team
  483. if ( pPlayer == NULL )
  484. {
  485. return m_flNextRespawnWave[iTeam];
  486. }
  487. // The soonest this player may spawn
  488. float flMinSpawnTime = GetMinTimeWhenPlayerMaySpawn( pPlayer );
  489. if ( ShouldRespawnQuickly( pPlayer ) )
  490. {
  491. return flMinSpawnTime;
  492. }
  493. // the next scheduled respawn wave time
  494. float flNextRespawnTime = m_flNextRespawnWave[iTeam];
  495. // the length of one respawn wave. We'll check in increments of this
  496. float flRespawnWaveMaxLen = GetRespawnWaveMaxLength( iTeam );
  497. if ( flRespawnWaveMaxLen <= 0 )
  498. {
  499. return flNextRespawnTime;
  500. }
  501. // Keep adding the length of one respawn until we find a wave that
  502. // this player will be eligible to spawn in.
  503. while ( flNextRespawnTime < flMinSpawnTime )
  504. {
  505. flNextRespawnTime += flRespawnWaveMaxLen;
  506. }
  507. return flNextRespawnTime;
  508. }
  509. //-----------------------------------------------------------------------------
  510. // Purpose: Is the player past the required delays for spawning
  511. //-----------------------------------------------------------------------------
  512. bool CTeamplayRoundBasedRules::HasPassedMinRespawnTime( CBasePlayer *pPlayer )
  513. {
  514. float flMinSpawnTime = GetMinTimeWhenPlayerMaySpawn( pPlayer );
  515. return ( gpGlobals->curtime > flMinSpawnTime );
  516. }
  517. //-----------------------------------------------------------------------------
  518. // Purpose:
  519. //-----------------------------------------------------------------------------
  520. float CTeamplayRoundBasedRules::GetMinTimeWhenPlayerMaySpawn( CBasePlayer *pPlayer )
  521. {
  522. // Min respawn time is the sum of
  523. //
  524. // a) the length of one full *unscaled* respawn wave for their team
  525. // and
  526. // b) death anim length + freeze panel length
  527. float flDeathAnimLength = 2.0 + spec_freeze_traveltime.GetFloat() + spec_freeze_time.GetFloat();
  528. float fMinDelay = flDeathAnimLength;
  529. if ( !ShouldRespawnQuickly( pPlayer ) )
  530. {
  531. fMinDelay += GetRespawnWaveMaxLength( pPlayer->GetTeamNumber(), false );
  532. }
  533. return pPlayer->GetDeathTime() + fMinDelay;
  534. }
  535. //-----------------------------------------------------------------------------
  536. // Purpose:
  537. //-----------------------------------------------------------------------------
  538. void CTeamplayRoundBasedRules::LevelInitPostEntity( void )
  539. {
  540. BaseClass::LevelInitPostEntity();
  541. #ifdef GAME_DLL
  542. m_bCheatsEnabledDuringLevel = sv_cheats && sv_cheats->GetBool();
  543. #endif // GAME_DLL
  544. }
  545. //-----------------------------------------------------------------------------
  546. // Purpose:
  547. //-----------------------------------------------------------------------------
  548. float CTeamplayRoundBasedRules::GetRespawnTimeScalar( int iTeam )
  549. {
  550. // For long respawn times, scale the time as the number of players drops
  551. int iOptimalPlayers = 8; // 16 players total, 8 per team
  552. int iNumPlayers = GetGlobalTeam(iTeam)->GetNumPlayers();
  553. float flScale = RemapValClamped( iNumPlayers, 1, iOptimalPlayers, 0.25, 1.0 );
  554. return flScale;
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Purpose:
  558. //-----------------------------------------------------------------------------
  559. void CTeamplayRoundBasedRules::FireGameEvent( IGameEvent * event )
  560. {
  561. #ifdef GAME_DLL
  562. const char *eventName = event->GetName();
  563. if ( g_fGameOver && !Q_strcmp( eventName, "server_changelevel_failed" ) )
  564. {
  565. Warning( "In gameover, but failed to load the next map. Trying next map in cycle.\n" );
  566. nextlevel.SetValue( "" );
  567. ChangeLevel();
  568. }
  569. #endif
  570. }
  571. #ifdef GAME_DLL
  572. //-----------------------------------------------------------------------------
  573. // Purpose:
  574. //-----------------------------------------------------------------------------
  575. void CTeamplayRoundBasedRules::SetForceMapReset( bool reset )
  576. {
  577. m_bForceMapReset = reset;
  578. }
  579. //-----------------------------------------------------------------------------
  580. // Purpose:
  581. //-----------------------------------------------------------------------------
  582. void CTeamplayRoundBasedRules::Think( void )
  583. {
  584. if ( g_fGameOver ) // someone else quit the game already
  585. {
  586. // check to see if we should change levels now
  587. if ( m_flIntermissionEndTime && ( m_flIntermissionEndTime < gpGlobals->curtime ) )
  588. {
  589. if ( !IsX360() )
  590. {
  591. ChangeLevel(); // intermission is over
  592. }
  593. else
  594. {
  595. IGameEvent * event = gameeventmanager->CreateEvent( "player_stats_updated" );
  596. if ( event )
  597. {
  598. event->SetBool( "forceupload", true );
  599. gameeventmanager->FireEvent( event );
  600. }
  601. engine->MultiplayerEndGame();
  602. }
  603. // Don't run this code again
  604. m_flIntermissionEndTime = 0.f;
  605. }
  606. return;
  607. }
  608. State_Think();
  609. if ( m_hWaitingForPlayersTimer )
  610. {
  611. Assert( m_bInWaitingForPlayers );
  612. }
  613. if ( gpGlobals->curtime > m_flNextPeriodicThink )
  614. {
  615. // Don't end the game during win or stalemate states
  616. if ( State_Get() != GR_STATE_TEAM_WIN && State_Get() != GR_STATE_STALEMATE && State_Get() != GR_STATE_GAME_OVER )
  617. {
  618. if ( CheckWinLimit() )
  619. return;
  620. if ( CheckMaxRounds() )
  621. return;
  622. }
  623. CheckRestartRound();
  624. CheckWaitingForPlayers();
  625. m_flNextPeriodicThink = gpGlobals->curtime + 1.0;
  626. }
  627. // Watch dog for cheats ever being enabled during a level
  628. if ( !m_bCheatsEnabledDuringLevel && sv_cheats && sv_cheats->GetBool() )
  629. {
  630. m_bCheatsEnabledDuringLevel = true;
  631. }
  632. // Bypass teamplay think.
  633. CGameRules::Think();
  634. }
  635. //-----------------------------------------------------------------------------
  636. // Purpose:
  637. //-----------------------------------------------------------------------------
  638. bool CTeamplayRoundBasedRules::TimerMayExpire( void )
  639. {
  640. #ifndef CSTRIKE_DLL
  641. // team_train_watchers can also prevent timer expiring ( overtime )
  642. CTeamTrainWatcher *pWatcher = dynamic_cast<CTeamTrainWatcher*>( gEntList.FindEntityByClassname( NULL, "team_train_watcher" ) );
  643. while ( pWatcher )
  644. {
  645. if ( !pWatcher->TimerMayExpire() )
  646. {
  647. return false;
  648. }
  649. pWatcher = dynamic_cast<CTeamTrainWatcher*>( gEntList.FindEntityByClassname( pWatcher, "team_train_watcher" ) );
  650. }
  651. #endif
  652. return BaseClass::TimerMayExpire();
  653. }
  654. //-----------------------------------------------------------------------------
  655. // Purpose:
  656. //-----------------------------------------------------------------------------
  657. void CTeamplayRoundBasedRules::CheckChatText( CBasePlayer *pPlayer, char *pText )
  658. {
  659. CheckChatForReadySignal( pPlayer, pText );
  660. BaseClass::CheckChatText( pPlayer, pText );
  661. }
  662. //-----------------------------------------------------------------------------
  663. // Purpose:
  664. //-----------------------------------------------------------------------------
  665. void CTeamplayRoundBasedRules::CheckChatForReadySignal( CBasePlayer *pPlayer, const char *chatmsg )
  666. {
  667. if ( IsInTournamentMode() == false )
  668. {
  669. if( m_bAwaitingReadyRestart && FStrEq( chatmsg, mp_clan_ready_signal.GetString() ) )
  670. {
  671. int iTeam = pPlayer->GetTeamNumber();
  672. if ( iTeam > LAST_SHARED_TEAM && iTeam < GetNumberOfTeams() )
  673. {
  674. m_bTeamReady.Set( iTeam, true );
  675. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_team_ready" );
  676. if ( event )
  677. {
  678. event->SetInt( "team", iTeam );
  679. gameeventmanager->FireEvent( event );
  680. }
  681. }
  682. }
  683. }
  684. }
  685. //-----------------------------------------------------------------------------
  686. // Purpose:
  687. //-----------------------------------------------------------------------------
  688. void CTeamplayRoundBasedRules::GoToIntermission( void )
  689. {
  690. if ( IsInTournamentMode() == true )
  691. return;
  692. BaseClass::GoToIntermission();
  693. // set all players to FL_FROZEN
  694. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  695. {
  696. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  697. if ( pPlayer )
  698. {
  699. pPlayer->AddFlag( FL_FROZEN );
  700. }
  701. }
  702. // Print out map stats to a text file
  703. //WriteStatsFile( "stats.xml" );
  704. State_Enter( GR_STATE_GAME_OVER );
  705. }
  706. //-----------------------------------------------------------------------------
  707. // Purpose:
  708. //-----------------------------------------------------------------------------
  709. void CTeamplayRoundBasedRules::SetInWaitingForPlayers( bool bWaitingForPlayers )
  710. {
  711. // never waiting for players when loading a bug report
  712. if ( IsLoadingBugBaitReport() || gpGlobals->eLoadType == MapLoad_Background )
  713. {
  714. m_bInWaitingForPlayers = false;
  715. return;
  716. }
  717. if( m_bInWaitingForPlayers == bWaitingForPlayers )
  718. return;
  719. if ( IsInArenaMode() && ( m_flWaitingForPlayersTimeEnds < 0 ) && !IsInTournamentMode() )
  720. {
  721. m_bInWaitingForPlayers = false;
  722. return;
  723. }
  724. m_bInWaitingForPlayers = bWaitingForPlayers;
  725. if ( m_bInWaitingForPlayers )
  726. {
  727. m_flWaitingForPlayersTimeEnds = gpGlobals->curtime + mp_waitingforplayers_time.GetFloat();
  728. }
  729. else
  730. {
  731. m_flWaitingForPlayersTimeEnds = -1.0f;
  732. if ( m_hWaitingForPlayersTimer )
  733. {
  734. UTIL_Remove( m_hWaitingForPlayersTimer );
  735. }
  736. RestoreActiveTimer();
  737. }
  738. }
  739. //-----------------------------------------------------------------------------
  740. // Purpose:
  741. //-----------------------------------------------------------------------------
  742. void CTeamplayRoundBasedRules::SetOvertime( bool bOvertime )
  743. {
  744. if ( m_bInOvertime == bOvertime )
  745. return;
  746. if ( bOvertime )
  747. {
  748. UTIL_LogPrintf( "World triggered \"Round_Overtime\"\n" );
  749. }
  750. m_bInOvertime = bOvertime;
  751. if ( m_bInOvertime )
  752. {
  753. // tell train watchers that we've transitioned to overtime
  754. #ifndef CSTRIKE_DLL
  755. CTeamTrainWatcher *pWatcher = dynamic_cast<CTeamTrainWatcher*>( gEntList.FindEntityByClassname( NULL, "team_train_watcher" ) );
  756. while ( pWatcher )
  757. {
  758. variant_t emptyVariant;
  759. pWatcher->AcceptInput( "OnStartOvertime", NULL, NULL, emptyVariant, 0 );
  760. pWatcher = dynamic_cast<CTeamTrainWatcher*>( gEntList.FindEntityByClassname( pWatcher, "team_train_watcher" ) );
  761. }
  762. #endif
  763. }
  764. }
  765. //-----------------------------------------------------------------------------
  766. // Purpose:
  767. //-----------------------------------------------------------------------------
  768. void CTeamplayRoundBasedRules::SetSetup( bool bSetup )
  769. {
  770. if ( m_bInSetup == bSetup )
  771. return;
  772. m_bInSetup = bSetup;
  773. }
  774. //-----------------------------------------------------------------------------
  775. // Purpose:
  776. //-----------------------------------------------------------------------------
  777. void CTeamplayRoundBasedRules::CheckWaitingForPlayers( void )
  778. {
  779. // never waiting for players when loading a bug report, or training
  780. if ( IsLoadingBugBaitReport() || gpGlobals->eLoadType == MapLoad_Background || !AllowWaitingForPlayers() )
  781. return;
  782. if ( mp_waitingforplayers_restart.GetBool() )
  783. {
  784. if ( m_bInWaitingForPlayers )
  785. {
  786. m_flWaitingForPlayersTimeEnds = gpGlobals->curtime + mp_waitingforplayers_time.GetFloat();
  787. if ( m_hWaitingForPlayersTimer )
  788. {
  789. variant_t sVariant;
  790. sVariant.SetInt( m_flWaitingForPlayersTimeEnds - gpGlobals->curtime );
  791. m_hWaitingForPlayersTimer->AcceptInput( "SetTime", NULL, NULL, sVariant, 0 );
  792. }
  793. }
  794. else
  795. {
  796. SetInWaitingForPlayers( true );
  797. }
  798. mp_waitingforplayers_restart.SetValue( 0 );
  799. }
  800. bool bCancelWait = ( mp_waitingforplayers_cancel.GetBool() || IsInItemTestingMode() ) && !IsInTournamentMode();
  801. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  802. if ( mp_developer.GetBool() )
  803. bCancelWait = true;
  804. #endif // _DEBUG || STAGING_ONLY
  805. if ( bCancelWait )
  806. {
  807. // Cancel the wait period and manually Resume() the timer if
  808. // it's not supposed to start paused at the beginning of a round.
  809. // We must do this before SetInWaitingForPlayers() is called because it will
  810. // restore the timer in the HUD and set the handle to NULL
  811. #ifndef CSTRIKE_DLL
  812. if ( m_hPreviousActiveTimer.Get() )
  813. {
  814. CTeamRoundTimer *pTimer = dynamic_cast<CTeamRoundTimer*>( m_hPreviousActiveTimer.Get() );
  815. if ( pTimer && !pTimer->StartPaused() )
  816. {
  817. pTimer->ResumeTimer();
  818. }
  819. }
  820. #endif
  821. SetInWaitingForPlayers( false );
  822. mp_waitingforplayers_cancel.SetValue( 0 );
  823. }
  824. if( m_bInWaitingForPlayers )
  825. {
  826. if ( IsInTournamentMode() == true )
  827. return;
  828. // only exit the waitingforplayers if the time is up, and we are not in a round
  829. // restart countdown already, and we are not waiting for a ready restart
  830. if( gpGlobals->curtime > m_flWaitingForPlayersTimeEnds && m_flRestartRoundTime < 0 && !m_bAwaitingReadyRestart )
  831. {
  832. m_flRestartRoundTime.Set( gpGlobals->curtime ); // reset asap
  833. if ( IsInArenaMode() == true )
  834. {
  835. if ( gpGlobals->curtime > m_flWaitingForPlayersTimeEnds )
  836. {
  837. SetInWaitingForPlayers( false );
  838. State_Transition( GR_STATE_PREROUND );
  839. }
  840. return;
  841. }
  842. // if "waiting for players" is ending and we're restarting...
  843. // keep the current round that we're already running around in as the first round after the restart
  844. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
  845. if ( pMaster && pMaster->PlayingMiniRounds() && pMaster->GetCurrentRound() )
  846. {
  847. SetRoundToPlayNext( pMaster->GetRoundToUseAfterRestart() );
  848. }
  849. }
  850. else
  851. {
  852. if ( !m_hWaitingForPlayersTimer )
  853. {
  854. // Stop any timers, and bring up a new one
  855. HideActiveTimer();
  856. #ifndef CSTRIKE_DLL
  857. variant_t sVariant;
  858. m_hWaitingForPlayersTimer = (CTeamRoundTimer*)CBaseEntity::Create( "team_round_timer", vec3_origin, vec3_angle );
  859. m_hWaitingForPlayersTimer->SetName( MAKE_STRING("zz_teamplay_waiting_timer") );
  860. m_hWaitingForPlayersTimer->KeyValue( "show_in_hud", "1" );
  861. sVariant.SetInt( m_flWaitingForPlayersTimeEnds - gpGlobals->curtime );
  862. m_hWaitingForPlayersTimer->AcceptInput( "SetTime", NULL, NULL, sVariant, 0 );
  863. m_hWaitingForPlayersTimer->AcceptInput( "Resume", NULL, NULL, sVariant, 0 );
  864. m_hWaitingForPlayersTimer->AcceptInput( "Enable", NULL, NULL, sVariant, 0 );
  865. #endif
  866. }
  867. }
  868. }
  869. }
  870. //-----------------------------------------------------------------------------
  871. // Purpose:
  872. //-----------------------------------------------------------------------------
  873. void CTeamplayRoundBasedRules::CheckRestartRound( void )
  874. {
  875. if( mp_clan_readyrestart.GetBool() && IsInTournamentMode() == false )
  876. {
  877. m_bAwaitingReadyRestart = true;
  878. for ( int i = LAST_SHARED_TEAM+1; i < GetNumberOfTeams(); i++ )
  879. {
  880. m_bTeamReady.Set( i, false );
  881. }
  882. const char *pszReadyString = mp_clan_ready_signal.GetString();
  883. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#clan_ready_rules", pszReadyString );
  884. UTIL_ClientPrintAll( HUD_PRINTTALK, "#clan_ready_rules", pszReadyString );
  885. // Don't let them put anything malicious in there
  886. if( pszReadyString == NULL || Q_strlen(pszReadyString) > 16 )
  887. {
  888. pszReadyString = "ready";
  889. }
  890. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_ready_restart" );
  891. if ( event )
  892. {
  893. gameeventmanager->FireEvent( event );
  894. }
  895. mp_clan_readyrestart.SetValue( 0 );
  896. // cancel any restart round in progress
  897. m_flRestartRoundTime.Set( -1.f );
  898. }
  899. // Restart the game if specified by the server
  900. int iRestartDelay = mp_restartround.GetInt();
  901. bool bRestartGameNow = mp_restartgame_immediate.GetBool();
  902. if ( iRestartDelay == 0 && !bRestartGameNow )
  903. {
  904. iRestartDelay = mp_restartgame.GetInt();
  905. }
  906. if ( iRestartDelay > 0 || bRestartGameNow )
  907. {
  908. int iDelayMax = 60;
  909. #ifdef TF_DLL
  910. if ( TFGameRules() && ( TFGameRules()->IsMannVsMachineMode() || TFGameRules()->IsCompetitiveMode() ) )
  911. {
  912. iDelayMax = 180;
  913. }
  914. #endif // #if defined(TF_CLIENT_DLL) || defined(TF_DLL)
  915. if ( iRestartDelay > iDelayMax )
  916. {
  917. iRestartDelay = iDelayMax;
  918. }
  919. if ( mp_restartgame.GetInt() > 0 || bRestartGameNow )
  920. {
  921. SetForceMapReset( true );
  922. }
  923. else
  924. {
  925. SetForceMapReset( false );
  926. }
  927. SetInStopWatch( false );
  928. if ( bRestartGameNow )
  929. {
  930. iRestartDelay = 0;
  931. }
  932. m_flRestartRoundTime.Set( gpGlobals->curtime + iRestartDelay );
  933. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_restart_seconds" );
  934. if ( event )
  935. {
  936. event->SetInt( "seconds", iRestartDelay );
  937. gameeventmanager->FireEvent( event );
  938. }
  939. if ( IsInTournamentMode() == false )
  940. {
  941. // let the players know
  942. const char *pFormat = NULL;
  943. if ( mp_restartgame.GetInt() > 0 )
  944. {
  945. if ( ShouldSwitchTeams() )
  946. {
  947. pFormat = ( iRestartDelay > 1 ) ? "#game_switch_in_secs" : "#game_switch_in_sec";
  948. }
  949. else if ( ShouldScrambleTeams() )
  950. {
  951. pFormat = ( iRestartDelay > 1 ) ? "#game_scramble_in_secs" : "#game_scramble_in_sec";
  952. #ifdef TF_DLL
  953. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_alert" );
  954. if ( event )
  955. {
  956. event->SetInt( "alert_type", HUD_ALERT_SCRAMBLE_TEAMS );
  957. gameeventmanager->FireEvent( event );
  958. }
  959. pFormat = NULL;
  960. #endif
  961. }
  962. }
  963. else if ( mp_restartround.GetInt() > 0 )
  964. {
  965. pFormat = ( iRestartDelay > 1 ) ? "#round_restart_in_secs" : "#round_restart_in_sec";
  966. }
  967. if ( pFormat )
  968. {
  969. char strRestartDelay[64];
  970. Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay );
  971. UTIL_ClientPrintAll( HUD_PRINTCENTER, pFormat, strRestartDelay );
  972. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, pFormat, strRestartDelay );
  973. }
  974. }
  975. mp_restartround.SetValue( 0 );
  976. mp_restartgame.SetValue( 0 );
  977. mp_restartgame_immediate.SetValue( 0 );
  978. // cancel any ready restart in progress
  979. m_bAwaitingReadyRestart = false;
  980. }
  981. }
  982. //-----------------------------------------------------------------------------
  983. // Purpose:
  984. //-----------------------------------------------------------------------------
  985. bool CTeamplayRoundBasedRules::CheckTimeLimit( bool bAllowEnd /*= true*/ )
  986. {
  987. if ( IsInPreMatch() == true )
  988. return false;
  989. if ( ( mp_timelimit.GetInt() > 0 && CanChangelevelBecauseOfTimeLimit() ) || m_bChangelevelAfterStalemate )
  990. {
  991. // If there's less than 5 minutes to go, just switch now. This avoids the problem
  992. // of sudden death modes starting shortly after a new round starts.
  993. const int iMinTime = 5;
  994. bool bSwitchDueToTime = ( mp_timelimit.GetInt() > iMinTime && GetTimeLeft() < (iMinTime * 60) );
  995. if ( IsInTournamentMode() == true )
  996. {
  997. if ( TournamentModeCanEndWithTimelimit() == false )
  998. {
  999. return false;
  1000. }
  1001. bSwitchDueToTime = false;
  1002. }
  1003. if ( IsInArenaMode() == true )
  1004. {
  1005. bSwitchDueToTime = false;
  1006. }
  1007. if ( GetTimeLeft() <= 0 || m_bChangelevelAfterStalemate || bSwitchDueToTime )
  1008. {
  1009. if ( bAllowEnd )
  1010. {
  1011. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_game_over" );
  1012. if ( event )
  1013. {
  1014. event->SetString( "reason", "Reached Time Limit" );
  1015. gameeventmanager->FireEvent( event );
  1016. }
  1017. SendTeamScoresEvent();
  1018. GoToIntermission();
  1019. }
  1020. return true;
  1021. }
  1022. }
  1023. return false;
  1024. }
  1025. //-----------------------------------------------------------------------------
  1026. // Purpose:
  1027. //-----------------------------------------------------------------------------
  1028. bool CTeamplayRoundBasedRules::IsGameUnderTimeLimit( void )
  1029. {
  1030. return ( mp_timelimit.GetInt() > 0 );
  1031. }
  1032. //-----------------------------------------------------------------------------
  1033. // Purpose:
  1034. //-----------------------------------------------------------------------------
  1035. int CTeamplayRoundBasedRules::GetTimeLeft( void )
  1036. {
  1037. float flTimeLimit = mp_timelimit.GetInt() * 60;
  1038. float flMapChangeTime = m_flMapResetTime + flTimeLimit;
  1039. // If the round timer is longer, let the round complete
  1040. // TFTODO: Do we need to worry about the timelimit running our during a round?
  1041. int iTime = (int)(flMapChangeTime - gpGlobals->curtime);
  1042. if ( iTime < 0 )
  1043. {
  1044. iTime = 0;
  1045. }
  1046. return ( iTime );
  1047. }
  1048. //-----------------------------------------------------------------------------
  1049. // Purpose:
  1050. //-----------------------------------------------------------------------------
  1051. bool CTeamplayRoundBasedRules::CheckNextLevelCvar( bool bAllowEnd /*= true*/ )
  1052. {
  1053. if ( m_bForceMapReset )
  1054. {
  1055. if ( nextlevel.GetString() && *nextlevel.GetString() )
  1056. {
  1057. if ( bAllowEnd )
  1058. {
  1059. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_game_over" );
  1060. if ( event )
  1061. {
  1062. event->SetString( "reason", "NextLevel CVAR" );
  1063. gameeventmanager->FireEvent( event );
  1064. }
  1065. GoToIntermission();
  1066. }
  1067. return true;
  1068. }
  1069. }
  1070. return false;
  1071. }
  1072. //-----------------------------------------------------------------------------
  1073. // Purpose:
  1074. //-----------------------------------------------------------------------------
  1075. bool CTeamplayRoundBasedRules::CheckWinLimit( bool bAllowEnd /*= true*/, int nAddValueWhenChecking /*= 0*/ )
  1076. {
  1077. // has one team won the specified number of rounds?
  1078. int iWinLimit = mp_winlimit.GetInt();
  1079. if ( iWinLimit > 0 )
  1080. {
  1081. for ( int i = LAST_SHARED_TEAM+1; i < GetNumberOfTeams(); i++ )
  1082. {
  1083. CTeam *pTeam = GetGlobalTeam(i);
  1084. Assert( pTeam );
  1085. if ( ( pTeam->GetScore() + nAddValueWhenChecking ) >= iWinLimit )
  1086. {
  1087. if ( bAllowEnd )
  1088. {
  1089. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_game_over" );
  1090. if ( event )
  1091. {
  1092. event->SetString( "reason", "Reached Win Limit" );
  1093. gameeventmanager->FireEvent( event );
  1094. }
  1095. GoToIntermission();
  1096. }
  1097. return true;
  1098. }
  1099. }
  1100. }
  1101. return false;
  1102. }
  1103. //-----------------------------------------------------------------------------
  1104. // Purpose:
  1105. //-----------------------------------------------------------------------------
  1106. bool CTeamplayRoundBasedRules::CheckMaxRounds( bool bAllowEnd /*= true*/, int nAddValueWhenChecking /*= 0*/ )
  1107. {
  1108. if ( mp_maxrounds.GetInt() > 0 && IsInPreMatch() == false )
  1109. {
  1110. if ( ( m_nRoundsPlayed + nAddValueWhenChecking ) >= mp_maxrounds.GetInt() )
  1111. {
  1112. if ( bAllowEnd )
  1113. {
  1114. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_game_over" );
  1115. if ( event )
  1116. {
  1117. event->SetString( "reason", "Reached Round Limit" );
  1118. gameeventmanager->FireEvent( event );
  1119. }
  1120. GoToIntermission();
  1121. }
  1122. return true;
  1123. }
  1124. }
  1125. return false;
  1126. }
  1127. //-----------------------------------------------------------------------------
  1128. // Purpose:
  1129. //-----------------------------------------------------------------------------
  1130. void CTeamplayRoundBasedRules::State_Transition( gamerules_roundstate_t newState )
  1131. {
  1132. m_prevState = State_Get();
  1133. State_Leave();
  1134. State_Enter( newState );
  1135. #ifdef STAGING_ONLY
  1136. if ( mp_state_debug.GetBool() )
  1137. {
  1138. DevMsg( "\n[STATE] -- %s (Previous: %s)\n", m_pszRoundStateStrings[(int)newState], m_pszRoundStateStrings[(int)m_prevState]);
  1139. }
  1140. #endif // STAGING_ONLY
  1141. }
  1142. //-----------------------------------------------------------------------------
  1143. // Purpose:
  1144. //-----------------------------------------------------------------------------
  1145. void CTeamplayRoundBasedRules::State_Enter( gamerules_roundstate_t newState )
  1146. {
  1147. m_iRoundState = newState;
  1148. m_pCurStateInfo = State_LookupInfo( newState );
  1149. m_flLastRoundStateChangeTime = gpGlobals->curtime;
  1150. if ( mp_showroundtransitions.GetInt() > 0 )
  1151. {
  1152. if ( m_pCurStateInfo )
  1153. Msg( "Gamerules: entering state '%s'\n", m_pCurStateInfo->m_pStateName );
  1154. else
  1155. Msg( "Gamerules: entering state #%d\n", newState );
  1156. }
  1157. // Initialize the new state.
  1158. if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState )
  1159. {
  1160. (this->*m_pCurStateInfo->pfnEnterState)();
  1161. }
  1162. }
  1163. //-----------------------------------------------------------------------------
  1164. // Purpose:
  1165. //-----------------------------------------------------------------------------
  1166. void CTeamplayRoundBasedRules::State_Leave()
  1167. {
  1168. if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState )
  1169. {
  1170. (this->*m_pCurStateInfo->pfnLeaveState)();
  1171. }
  1172. }
  1173. //-----------------------------------------------------------------------------
  1174. // Purpose:
  1175. //-----------------------------------------------------------------------------
  1176. void CTeamplayRoundBasedRules::State_Think()
  1177. {
  1178. if ( m_pCurStateInfo && m_pCurStateInfo->pfnThink )
  1179. {
  1180. (this->*m_pCurStateInfo->pfnThink)();
  1181. }
  1182. }
  1183. //-----------------------------------------------------------------------------
  1184. // Purpose:
  1185. //-----------------------------------------------------------------------------
  1186. CGameRulesRoundStateInfo* CTeamplayRoundBasedRules::State_LookupInfo( gamerules_roundstate_t state )
  1187. {
  1188. static CGameRulesRoundStateInfo playerStateInfos[] =
  1189. {
  1190. { GR_STATE_INIT, "GR_STATE_INIT", &CTeamplayRoundBasedRules::State_Enter_INIT, NULL, &CTeamplayRoundBasedRules::State_Think_INIT },
  1191. { GR_STATE_PREGAME, "GR_STATE_PREGAME", &CTeamplayRoundBasedRules::State_Enter_PREGAME, NULL, &CTeamplayRoundBasedRules::State_Think_PREGAME },
  1192. { GR_STATE_STARTGAME, "GR_STATE_STARTGAME", &CTeamplayRoundBasedRules::State_Enter_STARTGAME, NULL, &CTeamplayRoundBasedRules::State_Think_STARTGAME },
  1193. { GR_STATE_PREROUND, "GR_STATE_PREROUND", &CTeamplayRoundBasedRules::State_Enter_PREROUND, &CTeamplayRoundBasedRules::State_Leave_PREROUND, &CTeamplayRoundBasedRules::State_Think_PREROUND },
  1194. { GR_STATE_RND_RUNNING, "GR_STATE_RND_RUNNING", &CTeamplayRoundBasedRules::State_Enter_RND_RUNNING, NULL, &CTeamplayRoundBasedRules::State_Think_RND_RUNNING },
  1195. { GR_STATE_TEAM_WIN, "GR_STATE_TEAM_WIN", &CTeamplayRoundBasedRules::State_Enter_TEAM_WIN, NULL, &CTeamplayRoundBasedRules::State_Think_TEAM_WIN },
  1196. { GR_STATE_RESTART, "GR_STATE_RESTART", &CTeamplayRoundBasedRules::State_Enter_RESTART, NULL, &CTeamplayRoundBasedRules::State_Think_RESTART },
  1197. { GR_STATE_STALEMATE, "GR_STATE_STALEMATE", &CTeamplayRoundBasedRules::State_Enter_STALEMATE, &CTeamplayRoundBasedRules::State_Leave_STALEMATE, &CTeamplayRoundBasedRules::State_Think_STALEMATE },
  1198. { GR_STATE_GAME_OVER, "GR_STATE_GAME_OVER", NULL, NULL, NULL },
  1199. { GR_STATE_BONUS, "GR_STATE_BONUS", &CTeamplayRoundBasedRules::State_Enter_BONUS, &CTeamplayRoundBasedRules::State_Leave_BONUS, &CTeamplayRoundBasedRules::State_Think_BONUS },
  1200. { GR_STATE_BETWEEN_RNDS, "GR_STATE_BETWEEN_RNDS", &CTeamplayRoundBasedRules::State_Enter_BETWEEN_RNDS, &CTeamplayRoundBasedRules::State_Leave_BETWEEN_RNDS, &CTeamplayRoundBasedRules::State_Think_BETWEEN_RNDS },
  1201. };
  1202. for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ )
  1203. {
  1204. if ( playerStateInfos[i].m_iRoundState == state )
  1205. return &playerStateInfos[i];
  1206. }
  1207. return NULL;
  1208. }
  1209. //-----------------------------------------------------------------------------
  1210. // Purpose:
  1211. //-----------------------------------------------------------------------------
  1212. void CTeamplayRoundBasedRules::State_Enter_INIT( void )
  1213. {
  1214. InitTeams();
  1215. ResetMapTime();
  1216. }
  1217. //-----------------------------------------------------------------------------
  1218. // Purpose:
  1219. //-----------------------------------------------------------------------------
  1220. void CTeamplayRoundBasedRules::State_Think_INIT( void )
  1221. {
  1222. State_Transition( GR_STATE_PREGAME );
  1223. }
  1224. //-----------------------------------------------------------------------------
  1225. // Purpose: The server is idle and waiting for enough players to start up again.
  1226. // When we find an active player go to GR_STATE_STARTGAME.
  1227. //-----------------------------------------------------------------------------
  1228. void CTeamplayRoundBasedRules::State_Enter_PREGAME( void )
  1229. {
  1230. m_flNextPeriodicThink = gpGlobals->curtime + 0.1;
  1231. }
  1232. //-----------------------------------------------------------------------------
  1233. // Purpose:
  1234. //-----------------------------------------------------------------------------
  1235. void CTeamplayRoundBasedRules::State_Think_PREGAME( void )
  1236. {
  1237. CheckRespawnWaves();
  1238. // we'll just stay in pregame for the bugbait reports
  1239. if ( IsLoadingBugBaitReport() || gpGlobals->eLoadType == MapLoad_Background )
  1240. return;
  1241. // Commentary stays in this mode too
  1242. if ( IsInCommentaryMode() )
  1243. return;
  1244. if ( ( BHavePlayers() ) || ( IsInArenaMode() && ( m_flWaitingForPlayersTimeEnds == 0.f ) ) )
  1245. {
  1246. State_Transition( GR_STATE_STARTGAME );
  1247. }
  1248. }
  1249. //-----------------------------------------------------------------------------
  1250. // Purpose: Wait a bit and then spawn everyone into the preround
  1251. //-----------------------------------------------------------------------------
  1252. void CTeamplayRoundBasedRules::State_Enter_STARTGAME( void )
  1253. {
  1254. m_flStateTransitionTime = gpGlobals->curtime;
  1255. m_bInitialSpawn = true;
  1256. }
  1257. //-----------------------------------------------------------------------------
  1258. // Purpose:
  1259. //-----------------------------------------------------------------------------
  1260. void CTeamplayRoundBasedRules::State_Think_STARTGAME()
  1261. {
  1262. if( gpGlobals->curtime > m_flStateTransitionTime )
  1263. {
  1264. if ( !IsInTraining() && !IsInItemTestingMode() )
  1265. {
  1266. ConVarRef tf_bot_offline_practice( "tf_bot_offline_practice" );
  1267. if ( mp_waitingforplayers_time.GetFloat() > 0 && tf_bot_offline_practice.GetInt() == 0 )
  1268. {
  1269. // go into waitingforplayers, reset at end of it
  1270. SetInWaitingForPlayers( true );
  1271. }
  1272. }
  1273. State_Transition( GR_STATE_PREROUND );
  1274. }
  1275. }
  1276. //-----------------------------------------------------------------------------
  1277. // Purpose:
  1278. //-----------------------------------------------------------------------------
  1279. void CTeamplayRoundBasedRules::State_Enter_PREROUND( void )
  1280. {
  1281. BalanceTeams( false );
  1282. m_flStartBalancingTeamsAt = gpGlobals->curtime + 60.0;
  1283. RoundRespawn();
  1284. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_start" );
  1285. if ( event )
  1286. {
  1287. event->SetBool( "full_reset", m_bForceMapReset );
  1288. gameeventmanager->FireEvent( event );
  1289. }
  1290. if ( IsInArenaMode() )
  1291. {
  1292. if ( BHavePlayers() )
  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( tf_arena_preround_time.GetInt() );
  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
  1306. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_update_timer" );
  1307. if ( event )
  1308. {
  1309. gameeventmanager->FireEvent( event );
  1310. }
  1311. }
  1312. m_flStateTransitionTime = gpGlobals->curtime + tf_arena_preround_time.GetInt();
  1313. }
  1314. #ifdef TF_DLL
  1315. // Only allow at the very beginning of the game, or between waves in mvm
  1316. else if ( TFGameRules() && TFGameRules()->UsePlayerReadyStatusMode() && m_bAllowBetweenRounds )
  1317. {
  1318. State_Transition( GR_STATE_BETWEEN_RNDS );
  1319. m_bAllowBetweenRounds = false;
  1320. if ( TFGameRules()->IsMannVsMachineMode() )
  1321. {
  1322. TFObjectiveResource()->SetMannVsMachineBetweenWaves( true );
  1323. }
  1324. StopWatchModeThink();
  1325. return;
  1326. }
  1327. #endif // TF_DLL
  1328. else
  1329. {
  1330. float flTransitionTime = 5 * mp_enableroundwaittime.GetFloat();
  1331. #ifdef TF_DLL
  1332. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
  1333. {
  1334. flTransitionTime = tf_competitive_preround_duration.GetFloat();
  1335. m_flCountdownTime = -1.f;
  1336. if ( ( TFGameRules()->GetRoundsPlayed() > 0 ) && !( GetActiveRoundTimer() && ( GetActiveRoundTimer()->GetSetupTimeLength() > 0 ) ) )
  1337. {
  1338. // we do a countdown after the first round, so we need some extra pre-round time
  1339. flTransitionTime += tf_competitive_preround_countdown_duration.GetFloat();
  1340. m_flCountdownTime = gpGlobals->curtime + tf_competitive_preround_countdown_duration.GetFloat();
  1341. CTFPlayer *pPlayer;
  1342. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  1343. {
  1344. pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  1345. if ( !pPlayer )
  1346. continue;
  1347. if ( pPlayer->GetTeamNumber() < FIRST_GAME_TEAM )
  1348. continue;
  1349. pPlayer->TeamFortress_SetSpeed();
  1350. }
  1351. }
  1352. }
  1353. #endif // TF_DLL
  1354. m_flStateTransitionTime = gpGlobals->curtime + flTransitionTime;
  1355. }
  1356. StopWatchModeThink();
  1357. PreRound_Start();
  1358. }
  1359. //-----------------------------------------------------------------------------
  1360. // Purpose:
  1361. //-----------------------------------------------------------------------------
  1362. void CTeamplayRoundBasedRules::State_Leave_PREROUND( void )
  1363. {
  1364. PreRound_End();
  1365. }
  1366. //-----------------------------------------------------------------------------
  1367. // Purpose:
  1368. //-----------------------------------------------------------------------------
  1369. void CTeamplayRoundBasedRules::State_Think_PREROUND( void )
  1370. {
  1371. if( gpGlobals->curtime > m_flStateTransitionTime )
  1372. {
  1373. if ( IsInArenaMode() == true )
  1374. {
  1375. if ( IsInWaitingForPlayers() == true )
  1376. {
  1377. if ( IsInTournamentMode() == true )
  1378. {
  1379. // check round restart
  1380. CheckReadyRestart();
  1381. State_Transition( GR_STATE_STALEMATE );
  1382. }
  1383. return;
  1384. }
  1385. State_Transition( GR_STATE_STALEMATE );
  1386. // hide the class composition panel
  1387. }
  1388. else
  1389. {
  1390. State_Transition( GR_STATE_RND_RUNNING );
  1391. }
  1392. }
  1393. #ifdef TF_DLL
  1394. else
  1395. {
  1396. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
  1397. {
  1398. if ( ( TFGameRules()->GetRoundsPlayed() > 0 ) && ( m_flCountdownTime > 0 ) )
  1399. {
  1400. if ( gpGlobals->curtime > m_flCountdownTime )
  1401. {
  1402. CTFPlayer *pPlayer;
  1403. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  1404. {
  1405. pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  1406. if ( !pPlayer )
  1407. continue;
  1408. if ( pPlayer->GetTeamNumber() < FIRST_GAME_TEAM )
  1409. continue;
  1410. pPlayer->TeamFortress_SetSpeed();
  1411. }
  1412. }
  1413. }
  1414. }
  1415. }
  1416. #endif
  1417. CheckRespawnWaves();
  1418. }
  1419. //-----------------------------------------------------------------------------
  1420. // Purpose:
  1421. //-----------------------------------------------------------------------------
  1422. void CTeamplayRoundBasedRules::State_Enter_RND_RUNNING( void )
  1423. {
  1424. SetupOnRoundRunning();
  1425. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_active" );
  1426. if ( event )
  1427. {
  1428. gameeventmanager->FireEvent( event );
  1429. }
  1430. if( !IsInWaitingForPlayers() )
  1431. {
  1432. PlayStartRoundVoice();
  1433. }
  1434. m_bChangeLevelOnRoundEnd = false;
  1435. m_bPrevRoundWasWaitingForPlayers = false;
  1436. m_flNextBalanceTeamsTime = gpGlobals->curtime + 1.0f;
  1437. }
  1438. //-----------------------------------------------------------------------------
  1439. // Purpose:
  1440. //-----------------------------------------------------------------------------
  1441. void CTeamplayRoundBasedRules::CheckReadyRestart( void )
  1442. {
  1443. // check round restart
  1444. if ( m_flRestartRoundTime > 0 && m_flRestartRoundTime <= gpGlobals->curtime && !g_pServerBenchmark->IsBenchmarkRunning() )
  1445. {
  1446. m_flRestartRoundTime.Set( -1.f );
  1447. #ifdef TF_DLL
  1448. if ( TFGameRules() )
  1449. {
  1450. if ( TFGameRules()->IsMannVsMachineMode() )
  1451. {
  1452. if ( g_pPopulationManager && TFObjectiveResource()->GetMannVsMachineIsBetweenWaves() )
  1453. {
  1454. g_pPopulationManager->StartCurrentWave();
  1455. m_bAllowBetweenRounds = true;
  1456. return;
  1457. }
  1458. }
  1459. else if ( TFGameRules()->IsCompetitiveMode() )
  1460. {
  1461. TFGameRules()->StartCompetitiveMatch();
  1462. return;
  1463. }
  1464. else if ( mp_tournament.GetBool() )
  1465. {
  1466. // Temp
  1467. TFGameRules()->StartCompetitiveMatch();
  1468. return;
  1469. }
  1470. }
  1471. #endif // TF_DLL
  1472. // time to restart!
  1473. State_Transition( GR_STATE_RESTART );
  1474. }
  1475. bool bProcessReadyRestart = m_bAwaitingReadyRestart;
  1476. #ifdef TF_DLL
  1477. bProcessReadyRestart &= TFGameRules() && !TFGameRules()->UsePlayerReadyStatusMode();
  1478. #endif // TF_DLL
  1479. // check ready restart
  1480. if ( bProcessReadyRestart )
  1481. {
  1482. bool bTeamNotReady = false;
  1483. for ( int i = LAST_SHARED_TEAM + 1; i < GetNumberOfTeams(); i++ )
  1484. {
  1485. if ( !m_bTeamReady[i] )
  1486. {
  1487. bTeamNotReady = true;
  1488. break;
  1489. }
  1490. }
  1491. if ( !bTeamNotReady )
  1492. {
  1493. mp_restartgame.SetValue( 5 );
  1494. m_bAwaitingReadyRestart = false;
  1495. ShouldResetScores( true, true );
  1496. ShouldResetRoundsPlayed( true );
  1497. }
  1498. }
  1499. }
  1500. //-----------------------------------------------------------------------------
  1501. // Purpose:
  1502. //-----------------------------------------------------------------------------
  1503. void CTeamplayRoundBasedRules::State_Think_RND_RUNNING( void )
  1504. {
  1505. //if we don't find any active players, return to GR_STATE_PREGAME
  1506. if( !BHavePlayers() )
  1507. {
  1508. #if defined( REPLAY_ENABLED )
  1509. if ( g_pReplay )
  1510. {
  1511. // Write replay and stop recording if appropriate
  1512. g_pReplay->SV_EndRecordingSession();
  1513. }
  1514. #endif
  1515. State_Transition( GR_STATE_PREGAME );
  1516. return;
  1517. }
  1518. if ( m_flNextBalanceTeamsTime < gpGlobals->curtime )
  1519. {
  1520. BalanceTeams( true );
  1521. m_flNextBalanceTeamsTime = gpGlobals->curtime + 1.0f;
  1522. }
  1523. CheckRespawnWaves();
  1524. // check round restart
  1525. CheckReadyRestart();
  1526. // See if we're coming up to the server timelimit, in which case force a stalemate immediately.
  1527. if ( mp_timelimit.GetInt() > 0 && IsInPreMatch() == false && GetTimeLeft() <= 0 )
  1528. {
  1529. if ( m_bAllowStalemateAtTimelimit || ( mp_match_end_at_timelimit.GetBool() && !IsValveMap() ) )
  1530. {
  1531. int iDrawScoreCheck = -1;
  1532. int iWinningTeam = 0;
  1533. bool bTeamsAreDrawn = true;
  1534. for ( int i = FIRST_GAME_TEAM; (i < GetNumberOfTeams()) && bTeamsAreDrawn; i++ )
  1535. {
  1536. int iTeamScore = GetGlobalTeam(i)->GetScore();
  1537. if ( iTeamScore > iDrawScoreCheck )
  1538. {
  1539. iWinningTeam = i;
  1540. }
  1541. if ( iTeamScore != iDrawScoreCheck )
  1542. {
  1543. if ( iDrawScoreCheck == -1 )
  1544. {
  1545. iDrawScoreCheck = iTeamScore;
  1546. }
  1547. else
  1548. {
  1549. bTeamsAreDrawn = false;
  1550. }
  1551. }
  1552. }
  1553. if ( bTeamsAreDrawn )
  1554. {
  1555. if ( CanGoToStalemate() )
  1556. {
  1557. m_bChangelevelAfterStalemate = true;
  1558. SetStalemate( STALEMATE_SERVER_TIMELIMIT, m_bForceMapReset );
  1559. }
  1560. else
  1561. {
  1562. SetOvertime( true );
  1563. }
  1564. }
  1565. else
  1566. {
  1567. SetWinningTeam( iWinningTeam, WINREASON_TIMELIMIT, true, false, true );
  1568. }
  1569. }
  1570. }
  1571. StopWatchModeThink();
  1572. }
  1573. //-----------------------------------------------------------------------------
  1574. // Purpose:
  1575. //-----------------------------------------------------------------------------
  1576. void CTeamplayRoundBasedRules::State_Enter_TEAM_WIN( void )
  1577. {
  1578. // if we're forcing the map to reset it must be the end of a "full" round not a mini-round
  1579. if ( m_bForceMapReset )
  1580. {
  1581. m_nRoundsPlayed++;
  1582. }
  1583. bool bGameOver = IsGameOver();
  1584. m_flStateTransitionTime = gpGlobals->curtime + GetBonusRoundTime( bGameOver );
  1585. InternalHandleTeamWin( m_iWinningTeam );
  1586. SendWinPanelInfo( bGameOver );
  1587. #ifdef TF_DLL
  1588. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() && bGameOver )
  1589. {
  1590. TFGameRules()->StopCompetitiveMatch( CMsgGC_Match_Result_Status_MATCH_SUCCEEDED );
  1591. }
  1592. #endif // TF_DLL
  1593. }
  1594. //-----------------------------------------------------------------------------
  1595. // Purpose:
  1596. //-----------------------------------------------------------------------------
  1597. void CTeamplayRoundBasedRules::State_Think_TEAM_WIN( void )
  1598. {
  1599. if ( gpGlobals->curtime > m_flStateTransitionTime )
  1600. {
  1601. bool bDone = ( CheckTimeLimit() || CheckWinLimit() || CheckMaxRounds() || CheckNextLevelCvar() );
  1602. // check the win limit, max rounds, time limit and nextlevel cvar before starting the next round
  1603. if ( !bDone )
  1604. {
  1605. PreviousRoundEnd();
  1606. if ( ShouldGoToBonusRound() )
  1607. {
  1608. State_Transition( GR_STATE_BONUS );
  1609. }
  1610. else
  1611. {
  1612. #if defined( REPLAY_ENABLED )
  1613. if ( g_pReplay )
  1614. {
  1615. // Write replay and stop recording if appropriate
  1616. g_pReplay->SV_EndRecordingSession();
  1617. }
  1618. #endif
  1619. State_Transition( GR_STATE_PREROUND );
  1620. }
  1621. }
  1622. else if ( IsInTournamentMode() )
  1623. {
  1624. bool bShowScorboard = true;
  1625. #ifdef TF_DLL
  1626. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
  1627. {
  1628. bShowScorboard = false;
  1629. }
  1630. #endif // TF_DLL
  1631. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  1632. {
  1633. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  1634. if ( !pPlayer )
  1635. continue;
  1636. if ( bShowScorboard )
  1637. {
  1638. pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD );
  1639. }
  1640. }
  1641. RestartTournament();
  1642. if ( IsInArenaMode() )
  1643. {
  1644. #if defined( REPLAY_ENABLED )
  1645. if ( g_pReplay )
  1646. {
  1647. // Write replay and stop recording if appropriate
  1648. g_pReplay->SV_EndRecordingSession();
  1649. }
  1650. #endif
  1651. State_Transition( GR_STATE_PREROUND );
  1652. }
  1653. #ifdef TF_DLL
  1654. else if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && g_pPopulationManager )
  1655. {
  1656. // one of the convars mp_timelimit, mp_winlimit, mp_maxrounds, or nextlevel has been triggered
  1657. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  1658. {
  1659. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  1660. if ( !pPlayer )
  1661. continue;
  1662. pPlayer->AddFlag( FL_FROZEN );
  1663. }
  1664. g_fGameOver = true;
  1665. g_pPopulationManager->SetMapRestartTime( gpGlobals->curtime + 10.0f );
  1666. State_Enter( GR_STATE_GAME_OVER );
  1667. return;
  1668. }
  1669. else if ( TFGameRules() && TFGameRules()->UsePlayerReadyStatusMode() )
  1670. {
  1671. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  1672. {
  1673. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  1674. if ( !pPlayer )
  1675. continue;
  1676. pPlayer->AddFlag( FL_FROZEN );
  1677. }
  1678. g_fGameOver = true;
  1679. State_Enter( GR_STATE_GAME_OVER );
  1680. m_flStateTransitionTime = gpGlobals->curtime + GetPostMatchPeriod();
  1681. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
  1682. {
  1683. TFGameRules()->MatchSummaryStart();
  1684. }
  1685. return;
  1686. }
  1687. #endif // TF_DLL
  1688. else
  1689. {
  1690. State_Transition( GR_STATE_RND_RUNNING );
  1691. }
  1692. }
  1693. }
  1694. }
  1695. //-----------------------------------------------------------------------------
  1696. // Purpose:
  1697. //-----------------------------------------------------------------------------
  1698. void CTeamplayRoundBasedRules::State_Enter_STALEMATE( void )
  1699. {
  1700. m_flStalemateStartTime = gpGlobals->curtime;
  1701. SetupOnStalemateStart();
  1702. // Stop any timers, and bring up a new one
  1703. HideActiveTimer();
  1704. if ( m_hStalemateTimer )
  1705. {
  1706. UTIL_Remove( m_hStalemateTimer );
  1707. m_hStalemateTimer = NULL;
  1708. }
  1709. int iTimeLimit = mp_stalemate_timelimit.GetInt();
  1710. if ( IsInArenaMode() == true )
  1711. {
  1712. iTimeLimit = tf_arena_round_time.GetInt();
  1713. }
  1714. if ( iTimeLimit > 0 )
  1715. {
  1716. #ifndef CSTRIKE_DLL
  1717. variant_t sVariant;
  1718. if ( !m_hStalemateTimer )
  1719. {
  1720. m_hStalemateTimer = (CTeamRoundTimer*)CBaseEntity::Create( "team_round_timer", vec3_origin, vec3_angle );
  1721. }
  1722. m_hStalemateTimer->KeyValue( "show_in_hud", "1" );
  1723. sVariant.SetInt( iTimeLimit );
  1724. m_hStalemateTimer->AcceptInput( "SetTime", NULL, NULL, sVariant, 0 );
  1725. m_hStalemateTimer->AcceptInput( "Resume", NULL, NULL, sVariant, 0 );
  1726. m_hStalemateTimer->AcceptInput( "Enable", NULL, NULL, sVariant, 0 );
  1727. #endif
  1728. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_update_timer" );
  1729. if ( event )
  1730. {
  1731. gameeventmanager->FireEvent( event );
  1732. }
  1733. }
  1734. }
  1735. //-----------------------------------------------------------------------------
  1736. // Purpose:
  1737. //-----------------------------------------------------------------------------
  1738. void CTeamplayRoundBasedRules::State_Leave_STALEMATE( void )
  1739. {
  1740. SetupOnStalemateEnd();
  1741. if ( m_hStalemateTimer )
  1742. {
  1743. UTIL_Remove( m_hStalemateTimer );
  1744. }
  1745. if ( IsInArenaMode() == false )
  1746. {
  1747. RestoreActiveTimer();
  1748. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_update_timer" );
  1749. if ( event )
  1750. {
  1751. gameeventmanager->FireEvent( event );
  1752. }
  1753. }
  1754. }
  1755. //-----------------------------------------------------------------------------
  1756. // Purpose:
  1757. //-----------------------------------------------------------------------------
  1758. void CTeamplayRoundBasedRules::State_Enter_BONUS( void )
  1759. {
  1760. SetupOnBonusStart();
  1761. }
  1762. //-----------------------------------------------------------------------------
  1763. // Purpose:
  1764. //-----------------------------------------------------------------------------
  1765. void CTeamplayRoundBasedRules::State_Leave_BONUS( void )
  1766. {
  1767. SetupOnBonusEnd();
  1768. }
  1769. //-----------------------------------------------------------------------------
  1770. // Purpose:
  1771. //-----------------------------------------------------------------------------
  1772. void CTeamplayRoundBasedRules::State_Think_BONUS( void )
  1773. {
  1774. BonusStateThink();
  1775. }
  1776. //-----------------------------------------------------------------------------
  1777. // Purpose:
  1778. //-----------------------------------------------------------------------------
  1779. void CTeamplayRoundBasedRules::State_Enter_BETWEEN_RNDS( void )
  1780. {
  1781. BetweenRounds_Start();
  1782. }
  1783. //-----------------------------------------------------------------------------
  1784. // Purpose:
  1785. //-----------------------------------------------------------------------------
  1786. void CTeamplayRoundBasedRules::State_Leave_BETWEEN_RNDS( void )
  1787. {
  1788. BetweenRounds_End();
  1789. }
  1790. //-----------------------------------------------------------------------------
  1791. // Purpose:
  1792. //-----------------------------------------------------------------------------
  1793. void CTeamplayRoundBasedRules::State_Think_BETWEEN_RNDS( void )
  1794. {
  1795. BetweenRounds_Think();
  1796. }
  1797. //-----------------------------------------------------------------------------
  1798. // Purpose:
  1799. //-----------------------------------------------------------------------------
  1800. void CTeamplayRoundBasedRules::HideActiveTimer( void )
  1801. {
  1802. // We can't handle this, because we won't be able to restore multiple timers
  1803. Assert( m_hPreviousActiveTimer.Get() == NULL );
  1804. m_hPreviousActiveTimer = NULL;
  1805. #ifndef CSTRIKE_DLL
  1806. CBaseEntity *pEntity = NULL;
  1807. variant_t sVariant;
  1808. sVariant.SetInt( false );
  1809. while ((pEntity = gEntList.FindEntityByClassname( pEntity, "team_round_timer" )) != NULL)
  1810. {
  1811. CTeamRoundTimer *pTimer = assert_cast<CTeamRoundTimer*>(pEntity);
  1812. if ( pTimer && pTimer->ShowInHud() )
  1813. {
  1814. Assert( !m_hPreviousActiveTimer );
  1815. m_hPreviousActiveTimer = pTimer;
  1816. pEntity->AcceptInput( "ShowInHUD", NULL, NULL, sVariant, 0 );
  1817. }
  1818. }
  1819. #endif
  1820. }
  1821. //-----------------------------------------------------------------------------
  1822. // Purpose:
  1823. //-----------------------------------------------------------------------------
  1824. void CTeamplayRoundBasedRules::RestoreActiveTimer( void )
  1825. {
  1826. if ( m_hPreviousActiveTimer )
  1827. {
  1828. variant_t sVariant;
  1829. sVariant.SetInt( true );
  1830. m_hPreviousActiveTimer->AcceptInput( "ShowInHUD", NULL, NULL, sVariant, 0 );
  1831. m_hPreviousActiveTimer = NULL;
  1832. }
  1833. }
  1834. //-----------------------------------------------------------------------------
  1835. // Purpose:
  1836. //-----------------------------------------------------------------------------
  1837. void CTeamplayRoundBasedRules::State_Think_STALEMATE( void )
  1838. {
  1839. //if we don't find any active players, return to GR_STATE_PREGAME
  1840. if( !BHavePlayers() && IsInArenaMode() == false )
  1841. {
  1842. #if defined( REPLAY_ENABLED )
  1843. if ( g_pReplay )
  1844. {
  1845. // Write replay and stop recording if appropriate
  1846. g_pReplay->SV_EndRecordingSession();
  1847. }
  1848. #endif
  1849. State_Transition( GR_STATE_PREGAME );
  1850. return;
  1851. }
  1852. if ( IsInTournamentMode() == true && IsInWaitingForPlayers() == true )
  1853. {
  1854. CheckReadyRestart();
  1855. CheckRespawnWaves();
  1856. return;
  1857. }
  1858. int iDeadTeam = TEAM_UNASSIGNED;
  1859. int iAliveTeam = TEAM_UNASSIGNED;
  1860. // If a team is fully killed, the other team has won
  1861. for ( int i = LAST_SHARED_TEAM+1; i < GetNumberOfTeams(); i++ )
  1862. {
  1863. CTeam *pTeam = GetGlobalTeam(i);
  1864. Assert( pTeam );
  1865. int iPlayers = pTeam->GetNumPlayers();
  1866. if ( iPlayers )
  1867. {
  1868. bool bFoundLiveOne = false;
  1869. for ( int player = 0; player < iPlayers; player++ )
  1870. {
  1871. if ( pTeam->GetPlayer(player) && pTeam->GetPlayer(player)->IsAlive() )
  1872. {
  1873. bFoundLiveOne = true;
  1874. break;
  1875. }
  1876. }
  1877. if ( bFoundLiveOne )
  1878. {
  1879. iAliveTeam = i;
  1880. }
  1881. else
  1882. {
  1883. iDeadTeam = i;
  1884. }
  1885. }
  1886. else
  1887. {
  1888. iDeadTeam = i;
  1889. }
  1890. }
  1891. if ( iDeadTeam && iAliveTeam )
  1892. {
  1893. // The live team has won.
  1894. bool bMasterHandled = false;
  1895. if ( !m_bForceMapReset )
  1896. {
  1897. // We're not resetting the map, so give the winners control
  1898. // of all the points that were in play this round.
  1899. // Find the control point master.
  1900. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
  1901. if ( pMaster )
  1902. {
  1903. variant_t sVariant;
  1904. sVariant.SetInt( iAliveTeam );
  1905. pMaster->AcceptInput( "SetWinnerAndForceCaps", NULL, NULL, sVariant, 0 );
  1906. bMasterHandled = true;
  1907. }
  1908. }
  1909. if ( !bMasterHandled )
  1910. {
  1911. SetWinningTeam( iAliveTeam, WINREASON_OPPONENTS_DEAD, m_bForceMapReset );
  1912. }
  1913. }
  1914. else if ( ( iDeadTeam && iAliveTeam == TEAM_UNASSIGNED ) ||
  1915. ( m_hStalemateTimer && TimerMayExpire() && m_hStalemateTimer->GetTimeRemaining() <= 0 ) )
  1916. {
  1917. bool bFullReset = true;
  1918. CTeamControlPointMaster *pMaster = g_hControlPointMasters.Count() ? g_hControlPointMasters[0] : NULL;
  1919. if ( pMaster && pMaster->PlayingMiniRounds() )
  1920. {
  1921. // we don't need to do a full map reset for maps with mini-rounds
  1922. bFullReset = false;
  1923. }
  1924. // Both teams are dead. Pure stalemate.
  1925. SetWinningTeam( TEAM_UNASSIGNED, WINREASON_STALEMATE, bFullReset, false );
  1926. }
  1927. }
  1928. //-----------------------------------------------------------------------------
  1929. // Purpose: manual restart
  1930. //-----------------------------------------------------------------------------
  1931. void CTeamplayRoundBasedRules::State_Enter_RESTART( void )
  1932. {
  1933. // send scores
  1934. SendTeamScoresEvent();
  1935. // send restart event
  1936. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_restart_round" );
  1937. if ( event )
  1938. {
  1939. gameeventmanager->FireEvent( event );
  1940. }
  1941. m_bPrevRoundWasWaitingForPlayers = m_bInWaitingForPlayers;
  1942. SetInWaitingForPlayers( false );
  1943. ResetScores();
  1944. // reset the round time
  1945. ResetMapTime();
  1946. State_Transition( GR_STATE_PREROUND );
  1947. }
  1948. //-----------------------------------------------------------------------------
  1949. // Purpose:
  1950. //-----------------------------------------------------------------------------
  1951. void CTeamplayRoundBasedRules::State_Think_RESTART( void )
  1952. {
  1953. // should never get here, State_Enter_RESTART sets us into a different state
  1954. Assert( 0 );
  1955. }
  1956. //-----------------------------------------------------------------------------
  1957. // Purpose: Sorts teams by score
  1958. //-----------------------------------------------------------------------------
  1959. int TeamScoreSort( CTeam* const *pTeam1, CTeam* const *pTeam2 )
  1960. {
  1961. if ( !*pTeam1 )
  1962. return -1;
  1963. if ( !*pTeam2 )
  1964. return -1;
  1965. if ( (*pTeam1)->GetScore() > (*pTeam2)->GetScore() )
  1966. {
  1967. return 1;
  1968. }
  1969. return -1;
  1970. }
  1971. //-----------------------------------------------------------------------------
  1972. // Purpose: Input for other entities to declare a round winner.
  1973. //-----------------------------------------------------------------------------
  1974. void CTeamplayRoundBasedRules::SetWinningTeam( int team, int iWinReason, bool bForceMapReset /* = true */, bool bSwitchTeams /* = false*/, bool bDontAddScore /* = false*/, bool bFinal /*= false*/ )
  1975. {
  1976. // Commentary doesn't let anyone win
  1977. if ( IsInCommentaryMode() )
  1978. return;
  1979. if ( ( team != TEAM_UNASSIGNED ) && ( team <= LAST_SHARED_TEAM || team >= GetNumberOfTeams() ) )
  1980. {
  1981. Assert( !"SetWinningTeam() called with invalid team." );
  1982. return;
  1983. }
  1984. // are we already in this state?
  1985. if ( State_Get() == GR_STATE_TEAM_WIN )
  1986. return;
  1987. SetForceMapReset( bForceMapReset );
  1988. SetSwitchTeams( bSwitchTeams );
  1989. m_iWinningTeam = team;
  1990. m_iWinReason = iWinReason;
  1991. // 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
  1992. bool bRewardTeam = bForceMapReset || ( IsGameUnderTimeLimit() && ( GetTimeLeft() <= 0 ) );
  1993. if ( bDontAddScore == true )
  1994. {
  1995. bRewardTeam = false;
  1996. }
  1997. m_bUseAddScoreAnim = false;
  1998. if ( bRewardTeam && ( team != TEAM_UNASSIGNED ) && ShouldScorePerRound() )
  1999. {
  2000. GetGlobalTeam( team )->AddScore( TEAMPLAY_ROUND_WIN_SCORE );
  2001. m_bUseAddScoreAnim = true;
  2002. }
  2003. // this was a sudden death win if we were in stalemate then a team won it
  2004. bool bWasSuddenDeath = ( InStalemate() && m_iWinningTeam >= FIRST_GAME_TEAM );
  2005. State_Transition( GR_STATE_TEAM_WIN );
  2006. // this needs to be AFTER we add score above (for TF)
  2007. PlayWinSong( team );
  2008. m_flLastTeamWin = gpGlobals->curtime;
  2009. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_win" );
  2010. if ( event )
  2011. {
  2012. event->SetInt( "team", team );
  2013. event->SetInt( "winreason", iWinReason );
  2014. event->SetBool( "full_round", bForceMapReset );
  2015. event->SetFloat( "round_time", gpGlobals->curtime - m_flRoundStartTime );
  2016. event->SetBool( "was_sudden_death", bWasSuddenDeath );
  2017. // let derived classes add more fields to the event
  2018. FillOutTeamplayRoundWinEvent( event );
  2019. gameeventmanager->FireEvent( event );
  2020. }
  2021. // send team scores
  2022. SendTeamScoresEvent();
  2023. if ( team == TEAM_UNASSIGNED )
  2024. {
  2025. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  2026. {
  2027. CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( i ) );
  2028. if ( !pPlayer )
  2029. continue;
  2030. pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_STALEMATE );
  2031. }
  2032. }
  2033. // Auto scramble teams?
  2034. if ( bForceMapReset && mp_scrambleteams_auto.GetBool() )
  2035. {
  2036. if ( IsInArenaMode() || IsInTournamentMode() || ShouldSkipAutoScramble() )
  2037. return;
  2038. #ifndef DEBUG
  2039. // Don't bother on a listen server - usually not desirable
  2040. if ( !engine->IsDedicatedServer() )
  2041. return;
  2042. #endif // DEBUG
  2043. // Skip if we have a nextlevel set
  2044. if ( !FStrEq( nextlevel.GetString(), "" ) )
  2045. return;
  2046. // Track the team scores
  2047. if ( m_iWinningTeam != TEAM_UNASSIGNED )
  2048. {
  2049. // m_GameTeams differs from g_Teams by storing only "Real" teams
  2050. if ( m_GameTeams.Count() == 0 )
  2051. {
  2052. int iTeamIndex = FIRST_GAME_TEAM;
  2053. CTeam *pTeam;
  2054. for ( pTeam = GetGlobalTeam(iTeamIndex); pTeam != NULL; pTeam = GetGlobalTeam(++iTeamIndex) )
  2055. {
  2056. m_GameTeams.Insert( iTeamIndex, 0 );
  2057. }
  2058. }
  2059. // Safety net hack - we assume there are only two "Real" teams
  2060. // driller: need to make this work in all cases
  2061. if ( m_GameTeams.Count() != 2 )
  2062. return;
  2063. }
  2064. // Look for impending level change
  2065. if ( ( ( mp_timelimit.GetInt() > 0 && CanChangelevelBecauseOfTimeLimit() ) || m_bChangelevelAfterStalemate ) && GetTimeLeft() <= 300 )
  2066. return;
  2067. if ( mp_winlimit.GetInt() || mp_maxrounds.GetInt() )
  2068. {
  2069. int nRoundsPlayed = GetRoundsPlayed();
  2070. if ( ( mp_maxrounds.GetInt() - nRoundsPlayed ) == 1 )
  2071. {
  2072. return;
  2073. }
  2074. int nWinLimit = mp_winlimit.GetInt();
  2075. for ( int iIndex = m_GameTeams.FirstInorder(); iIndex != m_GameTeams.InvalidIndex(); iIndex = m_GameTeams.NextInorder( iIndex ) )
  2076. {
  2077. int nTeamScore = GetGlobalTeam( m_GameTeams.Key( iIndex ) )->GetScore();
  2078. if ( nWinLimit - nTeamScore == 1 )
  2079. {
  2080. return;
  2081. }
  2082. }
  2083. }
  2084. // Increment win counters
  2085. int iWinningTeamIndex = m_GameTeams.Find( m_iWinningTeam );
  2086. if ( iWinningTeamIndex != m_GameTeams.InvalidIndex() )
  2087. {
  2088. m_GameTeams[iWinningTeamIndex]++;
  2089. }
  2090. else
  2091. {
  2092. Assert( iWinningTeamIndex == m_GameTeams.InvalidIndex() );
  2093. return;
  2094. }
  2095. // Did we hit our win delta?
  2096. int nWinDelta = abs( m_GameTeams[1] - m_GameTeams[0] );
  2097. if ( nWinDelta >= mp_scrambleteams_auto_windifference.GetInt() )
  2098. {
  2099. // Let the server know we're going to scramble on round restart
  2100. #ifdef TF_DLL
  2101. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_alert" );
  2102. if ( event )
  2103. {
  2104. event->SetInt( "alert_type", HUD_ALERT_SCRAMBLE_TEAMS );
  2105. gameeventmanager->FireEvent( event );
  2106. }
  2107. #else
  2108. const char *pszMessage = "#game_scramble_onrestart";
  2109. if ( pszMessage )
  2110. {
  2111. UTIL_ClientPrintAll( HUD_PRINTCENTER, pszMessage );
  2112. UTIL_ClientPrintAll( HUD_PRINTCONSOLE, pszMessage );
  2113. }
  2114. #endif
  2115. UTIL_LogPrintf( "World triggered \"ScrambleTeams_Auto\"\n" );
  2116. SetScrambleTeams( true );
  2117. ShouldResetScores( true, false );
  2118. ShouldResetRoundsPlayed( false );
  2119. }
  2120. // If we switch teams after this win, swap scores
  2121. if ( ShouldSwitchTeams() )
  2122. {
  2123. int nTempScore = m_GameTeams[0];
  2124. m_GameTeams[0] = m_GameTeams[1];
  2125. m_GameTeams[1] = nTempScore;
  2126. }
  2127. }
  2128. }
  2129. //-----------------------------------------------------------------------------
  2130. // Purpose: Input for other entities to declare a stalemate
  2131. // Most often a team_control_point_master saying that the
  2132. // round timer expired
  2133. //-----------------------------------------------------------------------------
  2134. void CTeamplayRoundBasedRules::SetStalemate( int iReason, bool bForceMapReset /* = true */, bool bSwitchTeams /* = false */ )
  2135. {
  2136. if ( IsInTournamentMode() == true && IsInPreMatch() == true )
  2137. return;
  2138. if ( !mp_stalemate_enable.GetBool() )
  2139. {
  2140. SetWinningTeam( TEAM_UNASSIGNED, WINREASON_STALEMATE, bForceMapReset, bSwitchTeams );
  2141. return;
  2142. }
  2143. if ( InStalemate() )
  2144. return;
  2145. SetForceMapReset( bForceMapReset );
  2146. m_iWinningTeam = TEAM_UNASSIGNED;
  2147. PlaySuddenDeathSong();
  2148. State_Transition( GR_STATE_STALEMATE );
  2149. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_stalemate" );
  2150. if ( event )
  2151. {
  2152. event->SetInt( "reason", iReason );
  2153. gameeventmanager->FireEvent( event );
  2154. }
  2155. }
  2156. #ifdef GAME_DLL
  2157. void CC_CH_ForceRespawn( void )
  2158. {
  2159. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  2160. if ( pRules )
  2161. {
  2162. pRules->RespawnPlayers( true );
  2163. }
  2164. }
  2165. static ConCommand mp_forcerespawnplayers("mp_forcerespawnplayers", CC_CH_ForceRespawn, "Force all players to respawn.", FCVAR_CHEAT );
  2166. static ConVar mp_tournament_allow_non_admin_restart( "mp_tournament_allow_non_admin_restart", "1", FCVAR_NONE, "Allow mp_tournament_restart command to be issued by players other than admin.");
  2167. void CC_CH_TournamentRestart( void )
  2168. {
  2169. if ( mp_tournament_allow_non_admin_restart.GetBool() == false )
  2170. {
  2171. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  2172. return;
  2173. }
  2174. #ifdef TF_DLL
  2175. if ( TFGameRules() && ( TFGameRules()->IsMannVsMachineMode() || TFGameRules()->IsCompetitiveMode() ) )
  2176. return;
  2177. #endif // TF_DLL
  2178. CTeamplayRoundBasedRules *pRules = dynamic_cast<CTeamplayRoundBasedRules*>( GameRules() );
  2179. if ( pRules )
  2180. {
  2181. pRules->RestartTournament();
  2182. }
  2183. }
  2184. static ConCommand mp_tournament_restart("mp_tournament_restart", CC_CH_TournamentRestart, "Restart Tournament Mode on the current level." );
  2185. void CTeamplayRoundBasedRules::RestartTournament( void )
  2186. {
  2187. if ( IsInTournamentMode() == false )
  2188. return;
  2189. SetInWaitingForPlayers( true );
  2190. m_bAwaitingReadyRestart = true;
  2191. m_flStopWatchTotalTime = -1.0f;
  2192. m_bStopWatch = false;
  2193. // we might have had a stalemate during the last round
  2194. // so reset this bool each time we restart the tournament
  2195. m_bChangelevelAfterStalemate = false;
  2196. for ( int i = 0; i < MAX_TEAMS; i++ )
  2197. {
  2198. m_bTeamReady.Set( i, false );
  2199. }
  2200. for ( int i = 0; i < MAX_PLAYERS; i++ )
  2201. {
  2202. m_bPlayerReady.Set( i, false );
  2203. }
  2204. }
  2205. #endif
  2206. //-----------------------------------------------------------------------------
  2207. // Purpose:
  2208. // Input : bForceRespawn - respawn player even if dead or dying
  2209. // bTeam - if true, only respawn the passed team
  2210. // iTeam - team to respawn
  2211. //-----------------------------------------------------------------------------
  2212. void CTeamplayRoundBasedRules::RespawnPlayers( bool bForceRespawn, bool bTeam /* = false */, int iTeam/* = TEAM_UNASSIGNED */ )
  2213. {
  2214. if ( bTeam )
  2215. {
  2216. Assert( iTeam > LAST_SHARED_TEAM && iTeam < GetNumberOfTeams() );
  2217. }
  2218. CBasePlayer *pPlayer;
  2219. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  2220. {
  2221. pPlayer = ToBasePlayer( UTIL_PlayerByIndex( i ) );
  2222. if ( !pPlayer )
  2223. continue;
  2224. // Check for team specific spawn
  2225. if ( bTeam && pPlayer->GetTeamNumber() != iTeam )
  2226. continue;
  2227. // players that haven't chosen a team/class can never spawn
  2228. if ( !pPlayer->IsReadyToPlay() )
  2229. {
  2230. // Let the player spawn immediately when they do pick a class
  2231. if ( pPlayer->ShouldGainInstantSpawn() )
  2232. {
  2233. pPlayer->AllowInstantSpawn();
  2234. }
  2235. continue;
  2236. }
  2237. // If we aren't force respawning, don't respawn players that:
  2238. // - are alive
  2239. // - are still in the death anim stage of dying
  2240. if ( !bForceRespawn )
  2241. {
  2242. if ( pPlayer->IsAlive() )
  2243. continue;
  2244. if ( m_iRoundState != GR_STATE_PREROUND )
  2245. {
  2246. // If the player hasn't been dead the minimum respawn time, he
  2247. // waits until the next wave.
  2248. if ( bTeam && !HasPassedMinRespawnTime( pPlayer ) )
  2249. continue;
  2250. if ( !pPlayer->IsReadyToSpawn() )
  2251. {
  2252. // Let the player spawn immediately when they do pick a class
  2253. if ( pPlayer->ShouldGainInstantSpawn() )
  2254. {
  2255. pPlayer->AllowInstantSpawn();
  2256. }
  2257. continue;
  2258. }
  2259. }
  2260. }
  2261. // Respawn this player
  2262. pPlayer->ForceRespawn();
  2263. }
  2264. }
  2265. //-----------------------------------------------------------------------------
  2266. // Purpose:
  2267. //-----------------------------------------------------------------------------
  2268. void CTeamplayRoundBasedRules::InitTeams( void )
  2269. {
  2270. }
  2271. //-----------------------------------------------------------------------------
  2272. // Purpose:
  2273. //-----------------------------------------------------------------------------
  2274. bool CTeamplayRoundBasedRules::BHavePlayers( void )
  2275. {
  2276. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  2277. {
  2278. CBasePlayer *pPlayer = ToBasePlayer( UTIL_PlayerByIndex( i ) );
  2279. if ( pPlayer && pPlayer->IsReadyToPlay() )
  2280. {
  2281. return true;
  2282. }
  2283. }
  2284. return false;
  2285. }
  2286. //-----------------------------------------------------------------------------
  2287. // Purpose:
  2288. //-----------------------------------------------------------------------------
  2289. void CTeamplayRoundBasedRules::HandleTimeLimitChange( void )
  2290. {
  2291. // check that we have an active timer in the HUD and use mp_timelimit if we don't
  2292. if ( !MapHasActiveTimer() && ( mp_timelimit.GetInt() > 0 && GetTimeLeft() > 0 ) )
  2293. {
  2294. CreateTimeLimitTimer();
  2295. }
  2296. else
  2297. {
  2298. if ( m_hTimeLimitTimer )
  2299. {
  2300. UTIL_Remove( m_hTimeLimitTimer );
  2301. m_hTimeLimitTimer = NULL;
  2302. }
  2303. }
  2304. }
  2305. //-----------------------------------------------------------------------------
  2306. // Purpose:
  2307. //-----------------------------------------------------------------------------
  2308. void CTeamplayRoundBasedRules::ResetPlayerAndTeamReadyState( void )
  2309. {
  2310. for ( int i = 0; i < MAX_TEAMS; i++ )
  2311. {
  2312. m_bTeamReady.Set( i, false );
  2313. }
  2314. for ( int i = 0; i < MAX_PLAYERS; i++ )
  2315. {
  2316. m_bPlayerReady.Set( i, false );
  2317. }
  2318. #ifdef GAME_DLL
  2319. // Note <= MAX_PLAYERS vs < MAX_PLAYERS above
  2320. for ( int i = 0; i <= MAX_PLAYERS; i++ )
  2321. {
  2322. m_bPlayerReadyBefore[i] = false;
  2323. }
  2324. #endif // GAME_DLL
  2325. }
  2326. //-----------------------------------------------------------------------------
  2327. // Purpose:
  2328. //-----------------------------------------------------------------------------
  2329. bool CTeamplayRoundBasedRules::MapHasActiveTimer( void )
  2330. {
  2331. #ifndef CSTRIKE_DLL
  2332. CBaseEntity *pEntity = NULL;
  2333. while ( ( pEntity = gEntList.FindEntityByClassname( pEntity, "team_round_timer" ) ) != NULL )
  2334. {
  2335. CTeamRoundTimer *pTimer = assert_cast<CTeamRoundTimer*>( pEntity );
  2336. if ( pTimer && pTimer->ShowInHud() && ( Q_stricmp( STRING( pTimer->GetEntityName() ), "zz_teamplay_timelimit_timer" ) != 0 ) )
  2337. {
  2338. return true;
  2339. }
  2340. }
  2341. #endif
  2342. return false;
  2343. }
  2344. //-----------------------------------------------------------------------------
  2345. // Purpose:
  2346. //-----------------------------------------------------------------------------
  2347. void CTeamplayRoundBasedRules::CreateTimeLimitTimer( void )
  2348. {
  2349. if ( IsInArenaMode () == true || IsInKothMode() == true )
  2350. return;
  2351. // this is the same check we use in State_Think_RND_RUNNING()
  2352. // don't show the timelimit timer if we're not going to end the map when it runs out
  2353. bool bAllowStalemate = ( m_bAllowStalemateAtTimelimit || ( mp_match_end_at_timelimit.GetBool() && !IsValveMap() ) );
  2354. if ( !bAllowStalemate )
  2355. return;
  2356. #ifndef CSTRIKE_DLL
  2357. if ( !m_hTimeLimitTimer )
  2358. {
  2359. m_hTimeLimitTimer = (CTeamRoundTimer*)CBaseEntity::Create( "team_round_timer", vec3_origin, vec3_angle );
  2360. m_hTimeLimitTimer->SetName( MAKE_STRING( "zz_teamplay_timelimit_timer" ) );
  2361. }
  2362. variant_t sVariant;
  2363. m_hTimeLimitTimer->KeyValue( "show_in_hud", "1" );
  2364. sVariant.SetInt( GetTimeLeft() );
  2365. m_hTimeLimitTimer->AcceptInput( "SetTime", NULL, NULL, sVariant, 0 );
  2366. m_hTimeLimitTimer->AcceptInput( "Resume", NULL, NULL, sVariant, 0 );
  2367. m_hTimeLimitTimer->AcceptInput( "Enable", NULL, NULL, sVariant, 0 );
  2368. #endif
  2369. }
  2370. //-----------------------------------------------------------------------------
  2371. // Purpose:
  2372. //-----------------------------------------------------------------------------
  2373. void CTeamplayRoundBasedRules::RoundRespawn( void )
  2374. {
  2375. m_flRoundStartTime = gpGlobals->curtime;
  2376. if ( m_bForceMapReset || m_bPrevRoundWasWaitingForPlayers )
  2377. {
  2378. CleanUpMap();
  2379. // clear out the previously played rounds
  2380. m_iszPreviousRounds.RemoveAll();
  2381. if ( mp_timelimit.GetInt() > 0 && GetTimeLeft() > 0 )
  2382. {
  2383. // check that we have an active timer in the HUD and use mp_timelimit if we don't
  2384. if ( !MapHasActiveTimer() )
  2385. {
  2386. CreateTimeLimitTimer();
  2387. }
  2388. }
  2389. m_iLastCapPointChanged = 0;
  2390. }
  2391. // reset our spawn times to the original values
  2392. for ( int i = 0; i < MAX_TEAMS; i++ )
  2393. {
  2394. if ( m_flOriginalTeamRespawnWaveTime[i] >= 0 )
  2395. {
  2396. m_TeamRespawnWaveTimes.Set( i, m_flOriginalTeamRespawnWaveTime[i] );
  2397. }
  2398. }
  2399. if ( !IsInWaitingForPlayers() )
  2400. {
  2401. if ( m_bForceMapReset )
  2402. {
  2403. UTIL_LogPrintf( "World triggered \"Round_Start\"\n" );
  2404. }
  2405. }
  2406. // Setup before respawning players, so we can mess with spawnpoints
  2407. SetupOnRoundStart();
  2408. // Do we need to switch the teams?
  2409. m_bSwitchedTeamsThisRound = false;
  2410. if ( ShouldSwitchTeams() )
  2411. {
  2412. m_bSwitchedTeamsThisRound = true;
  2413. HandleSwitchTeams();
  2414. SetSwitchTeams( false );
  2415. }
  2416. // Do we need to switch the teams?
  2417. if ( ShouldScrambleTeams() )
  2418. {
  2419. HandleScrambleTeams();
  2420. SetScrambleTeams( false );
  2421. }
  2422. #if defined( REPLAY_ENABLED )
  2423. bool bShouldWaitToStartRecording = ShouldWaitToStartRecording();
  2424. if ( g_pReplay && g_pReplay->SV_ShouldBeginRecording( bShouldWaitToStartRecording ) )
  2425. {
  2426. // Tell the replay manager that it should begin recording the new round as soon as possible
  2427. g_pReplay->SV_GetContext()->GetSessionRecorder()->StartRecording();
  2428. }
  2429. #endif
  2430. // Free any edicts that were marked deleted. This should hopefully clear some out
  2431. // so the below function can use the now freed ones.
  2432. engine->AllowImmediateEdictReuse();
  2433. RespawnPlayers( true );
  2434. // reset per-round scores for each player
  2435. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  2436. {
  2437. CBasePlayer *pPlayer = ToBasePlayer( UTIL_PlayerByIndex( i ) );
  2438. if ( pPlayer )
  2439. {
  2440. pPlayer->ResetPerRoundStats();
  2441. }
  2442. }
  2443. }
  2444. //-----------------------------------------------------------------------------
  2445. // Purpose: Recreate all the map entities from the map data (preserving their indices),
  2446. // then remove everything else except the players.
  2447. //-----------------------------------------------------------------------------
  2448. void CTeamplayRoundBasedRules::CleanUpMap()
  2449. {
  2450. if( mp_showcleanedupents.GetInt() )
  2451. {
  2452. Msg( "CleanUpMap\n===============\n" );
  2453. Msg( " Entities: %d (%d edicts)\n", gEntList.NumberOfEntities(), gEntList.NumberOfEdicts() );
  2454. }
  2455. // Get rid of all entities except players.
  2456. CBaseEntity *pCur = gEntList.FirstEnt();
  2457. while ( pCur )
  2458. {
  2459. if ( !RoundCleanupShouldIgnore( pCur ) )
  2460. {
  2461. if( mp_showcleanedupents.GetInt() & 1 )
  2462. {
  2463. Msg( "Removed Entity: %s\n", pCur->GetClassname() );
  2464. }
  2465. UTIL_Remove( pCur );
  2466. }
  2467. pCur = gEntList.NextEnt( pCur );
  2468. }
  2469. // Clear out the event queue
  2470. g_EventQueue.Clear();
  2471. // Really remove the entities so we can have access to their slots below.
  2472. gEntList.CleanupDeleteList();
  2473. engine->AllowImmediateEdictReuse();
  2474. if ( mp_showcleanedupents.GetInt() & 2 )
  2475. {
  2476. Msg( " Entities Left:\n" );
  2477. pCur = gEntList.FirstEnt();
  2478. while ( pCur )
  2479. {
  2480. Msg( " %s (%d)\n", pCur->GetClassname(), pCur->entindex() );
  2481. pCur = gEntList.NextEnt( pCur );
  2482. }
  2483. }
  2484. // Now reload the map entities.
  2485. class CTeamplayMapEntityFilter : public IMapEntityFilter
  2486. {
  2487. public:
  2488. CTeamplayMapEntityFilter()
  2489. {
  2490. m_pRules = assert_cast<CTeamplayRoundBasedRules*>( GameRules() );
  2491. }
  2492. virtual bool ShouldCreateEntity( const char *pClassname )
  2493. {
  2494. // Don't recreate the preserved entities.
  2495. if ( m_pRules->ShouldCreateEntity( pClassname ) )
  2496. return true;
  2497. // Increment our iterator since it's not going to call CreateNextEntity for this ent.
  2498. if ( m_iIterator != g_MapEntityRefs.InvalidIndex() )
  2499. {
  2500. m_iIterator = g_MapEntityRefs.Next( m_iIterator );
  2501. }
  2502. return false;
  2503. }
  2504. virtual CBaseEntity* CreateNextEntity( const char *pClassname )
  2505. {
  2506. if ( m_iIterator == g_MapEntityRefs.InvalidIndex() )
  2507. {
  2508. // This shouldn't be possible. When we loaded the map, it should have used
  2509. // CTeamplayMapEntityFilter, which should have built the g_MapEntityRefs list
  2510. // with the same list of entities we're referring to here.
  2511. Assert( false );
  2512. return NULL;
  2513. }
  2514. else
  2515. {
  2516. CMapEntityRef &ref = g_MapEntityRefs[m_iIterator];
  2517. m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity.
  2518. if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) )
  2519. {
  2520. // Doh! The entity was delete and its slot was reused.
  2521. // Just use any old edict slot. This case sucks because we lose the baseline.
  2522. return CreateEntityByName( pClassname );
  2523. }
  2524. else
  2525. {
  2526. // Cool, the slot where this entity was is free again (most likely, the entity was
  2527. // freed above). Now create an entity with this specific index.
  2528. return CreateEntityByName( pClassname, ref.m_iEdict );
  2529. }
  2530. }
  2531. }
  2532. public:
  2533. int m_iIterator; // Iterator into g_MapEntityRefs.
  2534. CTeamplayRoundBasedRules *m_pRules;
  2535. };
  2536. CTeamplayMapEntityFilter filter;
  2537. filter.m_iIterator = g_MapEntityRefs.Head();
  2538. // DO NOT CALL SPAWN ON info_node ENTITIES!
  2539. MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
  2540. }
  2541. //-----------------------------------------------------------------------------
  2542. // Purpose:
  2543. //-----------------------------------------------------------------------------
  2544. bool CTeamplayRoundBasedRules::ShouldCreateEntity( const char *pszClassName )
  2545. {
  2546. return !FindInList( s_PreserveEnts, pszClassName );
  2547. }
  2548. //-----------------------------------------------------------------------------
  2549. // Purpose:
  2550. //-----------------------------------------------------------------------------
  2551. bool CTeamplayRoundBasedRules::RoundCleanupShouldIgnore( CBaseEntity *pEnt )
  2552. {
  2553. return FindInList( s_PreserveEnts, pEnt->GetClassname() );
  2554. }
  2555. //-----------------------------------------------------------------------------
  2556. // Purpose: Sort function for sorting players by time spent connected ( user ID )
  2557. //-----------------------------------------------------------------------------
  2558. static int SwitchPlayersSort( CBaseMultiplayerPlayer * const *p1, CBaseMultiplayerPlayer * const *p2 )
  2559. {
  2560. // sort by score
  2561. return ( (*p2)->GetTeamBalanceScore() - (*p1)->GetTeamBalanceScore() );
  2562. }
  2563. //-----------------------------------------------------------------------------
  2564. // Purpose:
  2565. //-----------------------------------------------------------------------------
  2566. void CTeamplayRoundBasedRules::CheckRespawnWaves( void )
  2567. {
  2568. for ( int team = LAST_SHARED_TEAM+1; team < GetNumberOfTeams(); team++ )
  2569. {
  2570. if ( m_flNextRespawnWave[team] && m_flNextRespawnWave[team] > gpGlobals->curtime )
  2571. continue;
  2572. RespawnTeam( team );
  2573. // Set m_flNextRespawnWave to 0 when we don't have a respawn time to reduce networking
  2574. float flNextRespawnLength = GetRespawnWaveMaxLength( team );
  2575. if ( flNextRespawnLength )
  2576. {
  2577. m_flNextRespawnWave.Set( team, gpGlobals->curtime + flNextRespawnLength );
  2578. }
  2579. else
  2580. {
  2581. m_flNextRespawnWave.Set( team, 0.0f );
  2582. }
  2583. }
  2584. }
  2585. #ifdef GAME_DLL
  2586. //-----------------------------------------------------------------------------
  2587. // Purpose:
  2588. //-----------------------------------------------------------------------------
  2589. void CTeamplayRoundBasedRules::BalanceTeams( bool bRequireSwitcheesToBeDead )
  2590. {
  2591. if ( mp_autoteambalance.GetBool() == false || ( IsInArenaMode() == true && tf_arena_use_queue.GetBool() == true ) )
  2592. {
  2593. return;
  2594. }
  2595. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  2596. if ( mp_developer.GetBool() )
  2597. return;
  2598. #endif // _DEBUG || STAGING_ONLY
  2599. if ( IsInTraining() || IsInItemTestingMode() )
  2600. {
  2601. return;
  2602. }
  2603. // we don't balance for a period of time at the start of the game
  2604. if ( gpGlobals->curtime < m_flStartBalancingTeamsAt )
  2605. {
  2606. return;
  2607. }
  2608. // wrap with this bool, indicates it's a round running switch and not a between rounds insta-switch
  2609. if ( bRequireSwitcheesToBeDead )
  2610. {
  2611. #ifndef CSTRIKE_DLL
  2612. // we don't balance if there is less than 60 seconds on the active timer
  2613. CTeamRoundTimer *pActiveTimer = GetActiveRoundTimer();
  2614. if ( pActiveTimer && pActiveTimer->GetTimeRemaining() < 60 )
  2615. {
  2616. return;
  2617. }
  2618. #endif
  2619. }
  2620. int iHeaviestTeam = TEAM_UNASSIGNED, iLightestTeam = TEAM_UNASSIGNED;
  2621. // Figure out if we're unbalanced
  2622. if ( !AreTeamsUnbalanced( iHeaviestTeam, iLightestTeam ) )
  2623. {
  2624. m_flFoundUnbalancedTeamsTime = -1;
  2625. m_bPrintedUnbalanceWarning = false;
  2626. return;
  2627. }
  2628. if ( m_flFoundUnbalancedTeamsTime < 0 )
  2629. {
  2630. m_flFoundUnbalancedTeamsTime = gpGlobals->curtime;
  2631. }
  2632. // if teams have been unbalanced for X seconds, play a warning
  2633. if ( !m_bPrintedUnbalanceWarning && ( ( gpGlobals->curtime - m_flFoundUnbalancedTeamsTime ) > 1.0 ) )
  2634. {
  2635. // print unbalance warning
  2636. UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_auto_team_balance_in", "5" );
  2637. m_bPrintedUnbalanceWarning = true;
  2638. }
  2639. // teams are unblanced, figure out some players that need to be switched
  2640. CTeam *pHeavyTeam = GetGlobalTeam( iHeaviestTeam );
  2641. CTeam *pLightTeam = GetGlobalTeam( iLightestTeam );
  2642. Assert( pHeavyTeam && pLightTeam );
  2643. int iNumSwitchesRequired = ( pHeavyTeam->GetNumPlayers() - pLightTeam->GetNumPlayers() ) / 2;
  2644. // sort the eligible players and switch the n best candidates
  2645. CUtlVector<CBaseMultiplayerPlayer *> vecPlayers;
  2646. CBaseMultiplayerPlayer *pPlayer;
  2647. int iScore;
  2648. int i;
  2649. for ( i = 0; i < pHeavyTeam->GetNumPlayers(); i++ )
  2650. {
  2651. pPlayer = ToBaseMultiplayerPlayer( pHeavyTeam->GetPlayer(i) );
  2652. if ( !pPlayer )
  2653. continue;
  2654. if ( !pPlayer->CanBeAutobalanced() )
  2655. continue;
  2656. // calculate a score for this player. higher is more likely to be switched
  2657. iScore = pPlayer->CalculateTeamBalanceScore();
  2658. pPlayer->SetTeamBalanceScore( iScore );
  2659. vecPlayers.AddToTail( pPlayer );
  2660. }
  2661. // sort the vector
  2662. vecPlayers.Sort( SwitchPlayersSort );
  2663. int iNumEligibleSwitchees = iNumSwitchesRequired + 2;
  2664. for ( int i=0; i<vecPlayers.Count() && iNumSwitchesRequired > 0 && i < iNumEligibleSwitchees; i++ )
  2665. {
  2666. pPlayer = vecPlayers.Element(i);
  2667. Assert( pPlayer );
  2668. if ( !pPlayer )
  2669. continue;
  2670. if ( bRequireSwitcheesToBeDead == false || !pPlayer->IsAlive() )
  2671. {
  2672. // We're trying to avoid picking a player that's recently
  2673. // been auto-balanced by delaying their selection in the hope
  2674. // that a better candidate comes along.
  2675. if ( bRequireSwitcheesToBeDead )
  2676. {
  2677. int nPlayerTeamBalanceScore = pPlayer->CalculateTeamBalanceScore();
  2678. // Do we already have someone in the queue?
  2679. if ( m_nAutoBalanceQueuePlayerIndex > 0 )
  2680. {
  2681. // Is this player's score worse?
  2682. if ( nPlayerTeamBalanceScore < m_nAutoBalanceQueuePlayerScore )
  2683. {
  2684. m_nAutoBalanceQueuePlayerIndex = pPlayer->entindex();
  2685. m_nAutoBalanceQueuePlayerScore = nPlayerTeamBalanceScore;
  2686. }
  2687. }
  2688. // Has this person been switched recently?
  2689. else if ( nPlayerTeamBalanceScore < -10000 )
  2690. {
  2691. // Put them in the queue
  2692. m_nAutoBalanceQueuePlayerIndex = pPlayer->entindex();
  2693. m_nAutoBalanceQueuePlayerScore = nPlayerTeamBalanceScore;
  2694. m_flAutoBalanceQueueTimeEnd = gpGlobals->curtime + 3.0f;
  2695. continue;
  2696. }
  2697. // If this is the player in the queue...
  2698. if ( m_nAutoBalanceQueuePlayerIndex == pPlayer->entindex() )
  2699. {
  2700. // Pass until their timer is up
  2701. if ( m_flAutoBalanceQueueTimeEnd > gpGlobals->curtime )
  2702. continue;
  2703. }
  2704. }
  2705. pPlayer->ChangeTeam( iLightestTeam );
  2706. pPlayer->SetLastForcedChangeTeamTimeToNow();
  2707. m_nAutoBalanceQueuePlayerScore = -1;
  2708. m_nAutoBalanceQueuePlayerIndex = -1;
  2709. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_teambalanced_player" );
  2710. if ( event )
  2711. {
  2712. event->SetInt( "player", pPlayer->entindex() );
  2713. event->SetInt( "team", iLightestTeam );
  2714. gameeventmanager->FireEvent( event );
  2715. }
  2716. // tell people that we've switched this player
  2717. UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_player_was_team_balanced", pPlayer->GetPlayerName() );
  2718. iNumSwitchesRequired--;
  2719. }
  2720. }
  2721. }
  2722. #endif // GAME_DLL
  2723. //-----------------------------------------------------------------------------
  2724. // Purpose:
  2725. //-----------------------------------------------------------------------------
  2726. void CTeamplayRoundBasedRules::ResetScores( void )
  2727. {
  2728. if ( m_bResetTeamScores )
  2729. {
  2730. for ( int i = 0; i < GetNumberOfTeams(); i++ )
  2731. {
  2732. GetGlobalTeam( i )->ResetScores();
  2733. }
  2734. }
  2735. if ( m_bResetPlayerScores )
  2736. {
  2737. CBasePlayer *pPlayer;
  2738. for( int i = 1; i <= gpGlobals->maxClients; i++ )
  2739. {
  2740. pPlayer = ToBasePlayer( UTIL_PlayerByIndex( i ) );
  2741. if (pPlayer == NULL)
  2742. continue;
  2743. if (FNullEnt( pPlayer->edict() ))
  2744. continue;
  2745. pPlayer->ResetScores();
  2746. }
  2747. #ifdef TF_DLL
  2748. IGameEvent *event = gameeventmanager->CreateEvent( "scorestats_accumulated_reset" );
  2749. if ( event )
  2750. {
  2751. gameeventmanager->FireEvent( event );
  2752. }
  2753. #endif // TF_DLL
  2754. }
  2755. if ( m_bResetRoundsPlayed )
  2756. {
  2757. m_nRoundsPlayed = 0;
  2758. }
  2759. // assume we always want to reset the scores
  2760. // unless someone tells us not to for the next reset
  2761. m_bResetTeamScores = true;
  2762. m_bResetPlayerScores = true;
  2763. m_bResetRoundsPlayed = true;
  2764. //m_flStopWatchTime = -1.0f;
  2765. }
  2766. //-----------------------------------------------------------------------------
  2767. // Purpose:
  2768. //-----------------------------------------------------------------------------
  2769. void CTeamplayRoundBasedRules::ResetMapTime( void )
  2770. {
  2771. m_flMapResetTime = gpGlobals->curtime;
  2772. // send an event with the time remaining until map change
  2773. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_map_time_remaining" );
  2774. if ( event )
  2775. {
  2776. event->SetInt( "seconds", GetTimeLeft() );
  2777. gameeventmanager->FireEvent( event );
  2778. }
  2779. }
  2780. //-----------------------------------------------------------------------------
  2781. // Purpose:
  2782. //-----------------------------------------------------------------------------
  2783. void CTeamplayRoundBasedRules::PlayStartRoundVoice( void )
  2784. {
  2785. for ( int i = LAST_SHARED_TEAM+1; i < GetNumberOfTeams(); i++ )
  2786. {
  2787. BroadcastSound( i, UTIL_VarArgs("Game.TeamRoundStart%d", i ) );
  2788. }
  2789. }
  2790. //-----------------------------------------------------------------------------
  2791. // Purpose:
  2792. //-----------------------------------------------------------------------------
  2793. void CTeamplayRoundBasedRules::PlayWinSong( int team )
  2794. {
  2795. if ( team == TEAM_UNASSIGNED )
  2796. {
  2797. PlayStalemateSong();
  2798. }
  2799. else
  2800. {
  2801. BroadcastSound( TEAM_UNASSIGNED, UTIL_VarArgs("Game.TeamWin%d", team ) );
  2802. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  2803. {
  2804. if ( i == team )
  2805. {
  2806. BroadcastSound( i, WinSongName( i ) );
  2807. }
  2808. else
  2809. {
  2810. const char *pchLoseSong = LoseSongName( i );
  2811. if ( pchLoseSong )
  2812. {
  2813. BroadcastSound( i, pchLoseSong );
  2814. }
  2815. }
  2816. }
  2817. }
  2818. }
  2819. //-----------------------------------------------------------------------------
  2820. // Purpose:
  2821. //-----------------------------------------------------------------------------
  2822. void CTeamplayRoundBasedRules::PlaySuddenDeathSong( void )
  2823. {
  2824. BroadcastSound( TEAM_UNASSIGNED, "Game.SuddenDeath" );
  2825. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  2826. {
  2827. BroadcastSound( i, "Game.SuddenDeath" );
  2828. }
  2829. }
  2830. //-----------------------------------------------------------------------------
  2831. // Purpose:
  2832. //-----------------------------------------------------------------------------
  2833. void CTeamplayRoundBasedRules::PlayStalemateSong( void )
  2834. {
  2835. BroadcastSound( TEAM_UNASSIGNED, GetStalemateSong( TEAM_UNASSIGNED ) );
  2836. for ( int i = FIRST_GAME_TEAM; i < GetNumberOfTeams(); i++ )
  2837. {
  2838. BroadcastSound( i, GetStalemateSong( i ) );
  2839. }
  2840. }
  2841. bool CTeamplayRoundBasedRules::PlayThrottledAlert( int iTeam, const char *sound, float fDelayBeforeNext )
  2842. {
  2843. if ( m_flNewThrottledAlertTime <= gpGlobals->curtime )
  2844. {
  2845. BroadcastSound( iTeam, sound );
  2846. m_flNewThrottledAlertTime = gpGlobals->curtime + fDelayBeforeNext;
  2847. return true;
  2848. }
  2849. return false;
  2850. }
  2851. //-----------------------------------------------------------------------------
  2852. // Purpose:
  2853. //-----------------------------------------------------------------------------
  2854. void CTeamplayRoundBasedRules::BroadcastSound( int iTeam, const char *sound, int iAdditionalSoundFlags )
  2855. {
  2856. //send it to everyone
  2857. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_broadcast_audio" );
  2858. if ( event )
  2859. {
  2860. event->SetInt( "team", iTeam );
  2861. event->SetString( "sound", sound );
  2862. event->SetInt( "additional_flags", iAdditionalSoundFlags );
  2863. gameeventmanager->FireEvent( event );
  2864. }
  2865. }
  2866. //-----------------------------------------------------------------------------
  2867. // Purpose:
  2868. //-----------------------------------------------------------------------------
  2869. void CTeamplayRoundBasedRules::AddPlayedRound( string_t strName )
  2870. {
  2871. if ( strName != NULL_STRING )
  2872. {
  2873. m_iszPreviousRounds.AddToHead( strName );
  2874. // we only need to store the last two rounds that we've played
  2875. if ( m_iszPreviousRounds.Count() > 2 )
  2876. {
  2877. // remove all but two of the entries (should only ever have to remove 1 when we're at 3)
  2878. for ( int i = m_iszPreviousRounds.Count() - 1 ; i > 1 ; i-- )
  2879. {
  2880. m_iszPreviousRounds.Remove( i );
  2881. }
  2882. }
  2883. }
  2884. }
  2885. //-----------------------------------------------------------------------------
  2886. // Purpose:
  2887. //-----------------------------------------------------------------------------
  2888. bool CTeamplayRoundBasedRules::IsPreviouslyPlayedRound( string_t strName )
  2889. {
  2890. return ( m_iszPreviousRounds.Find( strName ) != m_iszPreviousRounds.InvalidIndex() );
  2891. }
  2892. //-----------------------------------------------------------------------------
  2893. // Purpose:
  2894. //-----------------------------------------------------------------------------
  2895. string_t CTeamplayRoundBasedRules::GetLastPlayedRound( void )
  2896. {
  2897. return ( m_iszPreviousRounds.Count() ? m_iszPreviousRounds[0] : NULL_STRING );
  2898. }
  2899. //-----------------------------------------------------------------------------
  2900. // Purpose:
  2901. //-----------------------------------------------------------------------------
  2902. CTeamRoundTimer *CTeamplayRoundBasedRules::GetActiveRoundTimer( void )
  2903. {
  2904. #ifdef TF_DLL
  2905. int iTimerEntIndex = ObjectiveResource()->GetTimerInHUD();
  2906. return ( dynamic_cast<CTeamRoundTimer *>( UTIL_EntityByIndex( iTimerEntIndex ) ) );
  2907. #else
  2908. return NULL;
  2909. #endif
  2910. }
  2911. #endif // GAME_DLL
  2912. //-----------------------------------------------------------------------------
  2913. // Purpose: How long are the respawn waves for this team currently?
  2914. //-----------------------------------------------------------------------------
  2915. float CTeamplayRoundBasedRules::GetRespawnWaveMaxLength( int iTeam, bool bScaleWithNumPlayers /* = true */ )
  2916. {
  2917. if ( State_Get() != GR_STATE_RND_RUNNING )
  2918. return 0;
  2919. if ( mp_disable_respawn_times.GetBool() == true )
  2920. return 0.0f;
  2921. //Let's just turn off respawn times while players are messing around waiting for the tournament to start
  2922. if ( IsInTournamentMode() == true && IsInPreMatch() == true )
  2923. return 0.0f;
  2924. float flTime = ( ( m_TeamRespawnWaveTimes[iTeam] >= 0 ) ? m_TeamRespawnWaveTimes[iTeam] : mp_respawnwavetime.GetFloat() );
  2925. // For long respawn times, scale the time as the number of players drops
  2926. if ( bScaleWithNumPlayers && flTime > 5 )
  2927. {
  2928. flTime = MAX( 5, flTime * GetRespawnTimeScalar(iTeam) );
  2929. }
  2930. return flTime;
  2931. }
  2932. //-----------------------------------------------------------------------------
  2933. // Purpose: returns true if we are running tournament mode
  2934. //-----------------------------------------------------------------------------
  2935. bool CTeamplayRoundBasedRules::IsInTournamentMode( void )
  2936. {
  2937. return mp_tournament.GetBool();
  2938. }
  2939. //-----------------------------------------------------------------------------
  2940. // Purpose: returns true if we are running highlander mode
  2941. //-----------------------------------------------------------------------------
  2942. bool CTeamplayRoundBasedRules::IsInHighlanderMode( void )
  2943. {
  2944. #if defined( TF_CLIENT_DLL ) || defined( TF_DLL )
  2945. // can't use highlander mode and the queue system
  2946. if ( IsInArenaMode() == true && tf_arena_use_queue.GetBool() == true )
  2947. return false;
  2948. return mp_highlander.GetBool();
  2949. #else
  2950. return false;
  2951. #endif
  2952. }
  2953. //-----------------------------------------------------------------------------
  2954. // Purpose:
  2955. //-----------------------------------------------------------------------------
  2956. int CTeamplayRoundBasedRules::GetBonusRoundTime( bool bGameOver /* = false*/ )
  2957. {
  2958. return Max( 5, mp_bonusroundtime.GetInt() );
  2959. }
  2960. //-----------------------------------------------------------------------------
  2961. // Purpose:
  2962. //-----------------------------------------------------------------------------
  2963. int CTeamplayRoundBasedRules::GetPostMatchPeriod( void )
  2964. {
  2965. return mp_tournament_post_match_period.GetInt();
  2966. }
  2967. #ifdef CLIENT_DLL
  2968. //-----------------------------------------------------------------------------
  2969. // Purpose:
  2970. //-----------------------------------------------------------------------------
  2971. void CTeamplayRoundBasedRules::Update( float frametime )
  2972. {
  2973. BaseClass::Update( frametime );
  2974. int nTime = 0;
  2975. if ( m_flRestartRoundTime > gpGlobals->curtime )
  2976. {
  2977. nTime = ceil( m_flRestartRoundTime - gpGlobals->curtime );
  2978. }
  2979. else if ( m_flCountdownTime > gpGlobals->curtime )
  2980. {
  2981. nTime = ceil( m_flCountdownTime - gpGlobals->curtime );
  2982. }
  2983. if ( nTime != m_nLastEventFiredTime )
  2984. {
  2985. m_nLastEventFiredTime = nTime;
  2986. IGameEvent * event = gameeventmanager->CreateEvent( "restart_timer_time" );
  2987. if ( event )
  2988. {
  2989. event->SetInt( "time", nTime );
  2990. gameeventmanager->FireEventClientSide( event );
  2991. }
  2992. }
  2993. }
  2994. #endif
  2995. //-----------------------------------------------------------------------------
  2996. // Purpose: returns true if we should even bother to do balancing stuff
  2997. //-----------------------------------------------------------------------------
  2998. bool CTeamplayRoundBasedRules::ShouldBalanceTeams( void )
  2999. {
  3000. if ( IsInTournamentMode() )
  3001. return false;
  3002. if ( IsInTraining() || IsInItemTestingMode() )
  3003. return false;
  3004. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  3005. if ( mp_developer.GetBool() )
  3006. return false;
  3007. #endif // _DEBUG || STAGING_ONLY
  3008. if ( mp_teams_unbalance_limit.GetInt() <= 0 )
  3009. return false;
  3010. #if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
  3011. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
  3012. return false;
  3013. #endif // TF_DLL
  3014. return true;
  3015. }
  3016. //-----------------------------------------------------------------------------
  3017. // Purpose: returns true if the passed team change would cause unbalanced teams
  3018. //-----------------------------------------------------------------------------
  3019. bool CTeamplayRoundBasedRules::WouldChangeUnbalanceTeams( int iNewTeam, int iCurrentTeam )
  3020. {
  3021. // players are allowed to change to their own team
  3022. if ( iNewTeam == iCurrentTeam )
  3023. return false;
  3024. // if mp_teams_unbalance_limit is 0, don't check
  3025. if ( !ShouldBalanceTeams() )
  3026. return false;
  3027. #if defined( _DEBUG ) || defined( STAGING_ONLY )
  3028. if ( mp_developer.GetBool() )
  3029. return false;
  3030. #endif // _DEBUG || STAGING_ONLY
  3031. // if they are joining a non-playing team, allow
  3032. if ( iNewTeam < FIRST_GAME_TEAM )
  3033. return false;
  3034. #if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
  3035. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
  3036. return false;
  3037. #endif // TF_DLL
  3038. CTeam *pNewTeam = GetGlobalTeam( iNewTeam );
  3039. if ( !pNewTeam )
  3040. {
  3041. Assert( 0 );
  3042. return true;
  3043. }
  3044. // add one because we're joining this team
  3045. int iNewTeamPlayers = pNewTeam->GetNumPlayers() + 1;
  3046. // for each game team
  3047. int i = FIRST_GAME_TEAM;
  3048. CTeam *pTeam;
  3049. for ( pTeam = GetGlobalTeam(i); pTeam != NULL; pTeam = GetGlobalTeam(++i) )
  3050. {
  3051. if ( pTeam == pNewTeam )
  3052. continue;
  3053. int iNumPlayers = pTeam->GetNumPlayers();
  3054. if ( i == iCurrentTeam )
  3055. {
  3056. iNumPlayers = MAX( 0, iNumPlayers-1 );
  3057. }
  3058. if ( ( iNewTeamPlayers - iNumPlayers ) > mp_teams_unbalance_limit.GetInt() )
  3059. {
  3060. return true;
  3061. }
  3062. }
  3063. return false;
  3064. }
  3065. //-----------------------------------------------------------------------------
  3066. // Purpose:
  3067. //-----------------------------------------------------------------------------
  3068. bool CTeamplayRoundBasedRules::AreTeamsUnbalanced( int &iHeaviestTeam, int &iLightestTeam )
  3069. {
  3070. if ( !IsInArenaMode() || ( IsInArenaMode() && !tf_arena_use_queue.GetBool() ) )
  3071. {
  3072. if ( !ShouldBalanceTeams() )
  3073. return false;
  3074. }
  3075. #ifndef CLIENT_DLL
  3076. if ( IsInCommentaryMode() )
  3077. return false;
  3078. #endif
  3079. #if defined( TF_DLL ) || defined( TF_CLIENT_DLL )
  3080. if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() )
  3081. return false;
  3082. #endif // TF_DLL
  3083. int iMostPlayers = 0;
  3084. int iLeastPlayers = MAX_PLAYERS + 1;
  3085. int i = FIRST_GAME_TEAM;
  3086. for ( CTeam *pTeam = GetGlobalTeam(i); pTeam != NULL; pTeam = GetGlobalTeam(++i) )
  3087. {
  3088. int iNumPlayers = pTeam->GetNumPlayers();
  3089. if ( iNumPlayers < iLeastPlayers )
  3090. {
  3091. iLeastPlayers = iNumPlayers;
  3092. iLightestTeam = i;
  3093. }
  3094. if ( iNumPlayers > iMostPlayers )
  3095. {
  3096. iMostPlayers = iNumPlayers;
  3097. iHeaviestTeam = i;
  3098. }
  3099. }
  3100. if ( IsInArenaMode() && tf_arena_use_queue.GetBool() )
  3101. {
  3102. if ( iMostPlayers == 0 && iMostPlayers == iLeastPlayers )
  3103. return true;
  3104. if ( iMostPlayers != iLeastPlayers )
  3105. return true;
  3106. return false;
  3107. }
  3108. if ( ( iMostPlayers - iLeastPlayers ) > mp_teams_unbalance_limit.GetInt() )
  3109. {
  3110. return true;
  3111. }
  3112. return false;
  3113. }
  3114. #ifdef CLIENT_DLL
  3115. //-----------------------------------------------------------------------------
  3116. // Purpose:
  3117. //-----------------------------------------------------------------------------
  3118. void CTeamplayRoundBasedRules::SetRoundState( int iRoundState )
  3119. {
  3120. m_iRoundState = iRoundState;
  3121. m_flLastRoundStateChangeTime = gpGlobals->curtime;
  3122. }
  3123. //-----------------------------------------------------------------------------
  3124. // Purpose:
  3125. //-----------------------------------------------------------------------------
  3126. void CTeamplayRoundBasedRules::OnPreDataChanged( DataUpdateType_t updateType )
  3127. {
  3128. m_bOldInWaitingForPlayers = m_bInWaitingForPlayers;
  3129. m_bOldInOvertime = m_bInOvertime;
  3130. m_bOldInSetup = m_bInSetup;
  3131. }
  3132. //-----------------------------------------------------------------------------
  3133. // Purpose:
  3134. //-----------------------------------------------------------------------------
  3135. void CTeamplayRoundBasedRules::OnDataChanged( DataUpdateType_t updateType )
  3136. {
  3137. if ( updateType == DATA_UPDATE_CREATED ||
  3138. m_bOldInWaitingForPlayers != m_bInWaitingForPlayers ||
  3139. m_bOldInOvertime != m_bInOvertime ||
  3140. m_bOldInSetup != m_bInSetup )
  3141. {
  3142. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_update_timer" );
  3143. if ( event )
  3144. {
  3145. gameeventmanager->FireEventClientSide( event );
  3146. }
  3147. }
  3148. if ( updateType == DATA_UPDATE_CREATED )
  3149. {
  3150. if ( State_Get() == GR_STATE_STALEMATE )
  3151. {
  3152. IGameEvent *event = gameeventmanager->CreateEvent( "teamplay_round_stalemate" );
  3153. if ( event )
  3154. {
  3155. event->SetInt( "reason", STALEMATE_JOIN_MID );
  3156. gameeventmanager->FireEventClientSide( event );
  3157. }
  3158. }
  3159. }
  3160. if ( m_bInOvertime && ( m_bOldInOvertime != m_bInOvertime ) )
  3161. {
  3162. HandleOvertimeBegin();
  3163. }
  3164. }
  3165. #endif // CLIENT_DLL
  3166. #ifdef GAME_DLL
  3167. //-----------------------------------------------------------------------------
  3168. // Purpose:
  3169. //-----------------------------------------------------------------------------
  3170. void CTeamplayRoundBasedRules::ResetTeamsRoundWinTracking( void )
  3171. {
  3172. if ( m_GameTeams.Count() != 2 )
  3173. return;
  3174. m_GameTeams[0] = 0;
  3175. m_GameTeams[1] = 0;
  3176. }
  3177. #endif // GAME_DLL