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.

1685 lines
56 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: TF-specific things to vote on
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_voteissues.h"
  8. #include "tf_player.h"
  9. #include "vote_controller.h"
  10. #include "fmtstr.h"
  11. #include "eiface.h"
  12. #include "tf_gamerules.h"
  13. #include "inetchannelinfo.h"
  14. #include "tf_gamestats.h"
  15. #include "tf_gcmessages.h"
  16. #include "player_vs_environment/tf_population_manager.h"
  17. #include "tf_mann_vs_machine_stats.h"
  18. #include "tf_objective_resource.h"
  19. #include "gc_clientsystem.h"
  20. #include "tf_gc_server.h"
  21. #include "player_resource.h"
  22. #include "tf_player_resource.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. extern ConVar tf_mm_trusted;
  26. extern ConVar mp_autoteambalance;
  27. extern ConVar tf_classlimit;
  28. extern ConVar sv_vote_quorum_ratio;
  29. extern ConVar tf_mm_strict;
  30. static bool VotableMap( const char *pszMapName )
  31. {
  32. char szCanonName[64] = { 0 };
  33. V_strncpy( szCanonName, pszMapName, sizeof( szCanonName ) );
  34. IVEngineServer::eFindMapResult eResult = engine->FindMap( szCanonName, sizeof( szCanonName ) );
  35. switch ( eResult )
  36. {
  37. case IVEngineServer::eFindMap_Found:
  38. case IVEngineServer::eFindMap_NonCanonical:
  39. case IVEngineServer::eFindMap_PossiblyAvailable:
  40. case IVEngineServer::eFindMap_FuzzyMatch:
  41. return true;
  42. case IVEngineServer::eFindMap_NotFound:
  43. return false;
  44. }
  45. AssertMsg( false, "Unhandled engine->FindMap return value\n" );
  46. return false;
  47. }
  48. //-----------------------------------------------------------------------------
  49. // Purpose: Base TF Issue
  50. //-----------------------------------------------------------------------------
  51. //-----------------------------------------------------------------------------
  52. // Purpose: Restart Round Issue
  53. //-----------------------------------------------------------------------------
  54. ConVar sv_vote_issue_restart_game_allowed( "sv_vote_issue_restart_game_allowed", "0", FCVAR_NONE, "Can players call votes to restart the game?" );
  55. ConVar sv_vote_issue_restart_game_allowed_mvm( "sv_vote_issue_restart_game_allowed_mvm", "1", FCVAR_NONE, "Can players call votes to restart the game in Mann-Vs-Machine?" );
  56. ConVar sv_vote_issue_restart_game_cooldown( "sv_vote_issue_restart_game_cooldown", "300", FCVAR_NONE, "Minimum time before another restart vote can occur (in seconds)." );
  57. //-----------------------------------------------------------------------------
  58. // Purpose:
  59. //-----------------------------------------------------------------------------
  60. void CRestartGameIssue::ExecuteCommand( void )
  61. {
  62. if ( sv_vote_issue_restart_game_cooldown.GetInt() )
  63. {
  64. SetIssueCooldownDuration( sv_vote_issue_restart_game_cooldown.GetFloat() );
  65. }
  66. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  67. {
  68. g_pPopulationManager->ResetMap();
  69. return;
  70. }
  71. engine->ServerCommand( "mp_restartgame 1;" );
  72. }
  73. //-----------------------------------------------------------------------------
  74. // Purpose:
  75. //-----------------------------------------------------------------------------
  76. bool CRestartGameIssue::IsEnabled( void )
  77. {
  78. if ( TFGameRules() )
  79. {
  80. if ( TFGameRules()->IsMannVsMachineMode() )
  81. return sv_vote_issue_restart_game_allowed_mvm.GetBool();
  82. }
  83. return sv_vote_issue_restart_game_allowed.GetBool();
  84. }
  85. //-----------------------------------------------------------------------------
  86. // Purpose:
  87. //-----------------------------------------------------------------------------
  88. bool CRestartGameIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  89. {
  90. if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  91. return false;
  92. if( !IsEnabled() )
  93. {
  94. nFailCode = VOTE_FAILED_ISSUE_DISABLED;
  95. return false;
  96. }
  97. return true;
  98. }
  99. //-----------------------------------------------------------------------------
  100. // Purpose:
  101. //-----------------------------------------------------------------------------
  102. const char *CRestartGameIssue::GetDisplayString( void )
  103. {
  104. return "#TF_vote_restart_game";
  105. }
  106. //-----------------------------------------------------------------------------
  107. // Purpose:
  108. //-----------------------------------------------------------------------------
  109. const char *CRestartGameIssue::GetVotePassedString( void )
  110. {
  111. return "#TF_vote_passed_restart_game";
  112. }
  113. //-----------------------------------------------------------------------------
  114. // Purpose:
  115. //-----------------------------------------------------------------------------
  116. void CRestartGameIssue::ListIssueDetails( CBasePlayer *pForWhom )
  117. {
  118. if( !sv_vote_issue_restart_game_allowed.GetBool() )
  119. return;
  120. ListStandardNoArgCommand( pForWhom, GetTypeString() );
  121. }
  122. //-----------------------------------------------------------------------------
  123. // Purpose: Kick Player Issue
  124. //-----------------------------------------------------------------------------
  125. ConVar sv_vote_issue_kick_allowed( "sv_vote_issue_kick_allowed", "0", FCVAR_NONE, "Can players call votes to kick players from the server?" );
  126. ConVar sv_vote_issue_kick_allowed_mvm( "sv_vote_issue_kick_allowed_mvm", "1", FCVAR_NONE, "Can players call votes to kick players from the server in MvM?" );
  127. ConVar sv_vote_kick_ban_duration( "sv_vote_kick_ban_duration", "20", FCVAR_NONE, "The number of minutes a vote ban should last. (0 = Disabled)" );
  128. ConVar sv_vote_issue_kick_min_connect_time_mvm( "sv_vote_issue_kick_min_connect_time_mvm", "300", FCVAR_NONE, "How long a player must be connected before they can be kicked (in seconds)." );
  129. ConVar sv_vote_issue_kick_spectators_mvm( "sv_vote_issue_kick_spectators_mvm", "1", FCVAR_NONE, "Allow players to kick spectators in MvM." );
  130. ConVar sv_vote_issue_kick_namelock_duration( "sv_vote_issue_kick_namelock_duration", "120", FCVAR_NONE, "How long to prevent kick targets from changing their name (in seconds)." );
  131. ConVar sv_vote_issue_kick_limit_mvm( "sv_vote_issue_kick_limit_mvm", "0", FCVAR_HIDDEN, "The maximum number of kick votes a player can call during an MvM mission started by matchmaking. (0 = disabled)" );
  132. ConVar sv_vote_issue_kick_limit_gc( "sv_vote_issue_kick_limit_gc", "0", FCVAR_HIDDEN, "Ask the GC if a kick vote can be called by this player. Official servers only." );
  133. //-----------------------------------------------------------------------------
  134. // Purpose:
  135. //-----------------------------------------------------------------------------
  136. void CKickIssue::Init( void )
  137. {
  138. m_szTargetPlayerName[0] = 0;
  139. m_hPlayerTarget = NULL;
  140. m_unKickReason = kVoteKickBanPlayerReason_Other;
  141. m_steamIDVoteCaller.Clear();
  142. m_steamIDVoteTarget.Clear();
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Purpose:
  146. //-----------------------------------------------------------------------------
  147. void CKickIssue::ExecuteCommand( void )
  148. {
  149. PrintLogData();
  150. engine->ServerCommand( CFmtStr( "kickid \"%s\" %s\n", m_steamIDVoteTarget.Render(), g_pszVoteKickString ) );
  151. GTFGCClientSystem()->MatchPlayerVoteKicked( m_steamIDVoteTarget );
  152. if ( tf_mm_strict.GetInt() == 1 )
  153. {
  154. // If we're in strict match mode, we're done.
  155. //
  156. // If the GC does send them back here, banning them would put them in a broken rejoin state. The matchmaker
  157. // should just not re-match you to somewhere you got kicked from.
  158. return;
  159. }
  160. // If we're not in strict mode they might be here as or be able to rejoin as an adhoc player -- ban.
  161. // Technically the GC might send them back here for *new* match if they get kicked as ad-hoc, the current match ends, etc.
  162. engine->ServerCommand( CFmtStr( "banid %d \"%s\"\n", sv_vote_kick_ban_duration.GetInt(), m_steamIDVoteTarget.Render() ) );
  163. // Band-aid: Hacks are able to avoid kick+ban, and we're not yet sure how they're doing it. This code checks to see
  164. // if they come back.
  165. //
  166. // XXX(JohnS): We think the original cause behind this was fixed (connecting-but-not-active race condition)
  167. g_voteController->AddPlayerToKickWatchList( m_steamIDVoteTarget, ( sv_vote_kick_ban_duration.GetFloat() * 60.f ) );
  168. NotifyGCAdHocKick( true );
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Purpose:
  172. //-----------------------------------------------------------------------------
  173. bool CKickIssue::IsEnabled( void )
  174. {
  175. if ( TFGameRules() )
  176. {
  177. if ( TFGameRules()->IsMannVsMachineMode() )
  178. return sv_vote_issue_kick_allowed_mvm.GetBool();
  179. }
  180. return sv_vote_issue_kick_allowed.GetBool();
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Purpose: This gets calle first. If true, moves on to OnVoteStarted()
  184. //-----------------------------------------------------------------------------
  185. bool CKickIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  186. {
  187. if ( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  188. return false;
  189. // We were waiting for an answer. Return it.
  190. if ( m_bGCNotified && m_bGCResponded )
  191. {
  192. if ( !m_bGCApproved )
  193. {
  194. nFailCode = VOTE_FAILED_KICK_DENIED_BY_GC;
  195. }
  196. bool bReturn = m_bGCApproved;
  197. m_bGCApproved = false;
  198. m_bGCNotified = false;
  199. m_bGCResponded = false;
  200. return bReturn;
  201. }
  202. Init();
  203. if ( !IsEnabled() )
  204. {
  205. nFailCode = VOTE_FAILED_ISSUE_DISABLED;
  206. return false;
  207. }
  208. if ( !CreateVoteDataFromDetails( pszDetails ) )
  209. {
  210. nFailCode = VOTE_FAILED_PLAYERNOTFOUND;
  211. return false;
  212. }
  213. // Don't kick proxies
  214. if ( m_hPlayerTarget->IsReplay() || m_hPlayerTarget->IsHLTV() )
  215. {
  216. nFailCode = VOTE_FAILED_PLAYERNOTFOUND;
  217. return false;
  218. }
  219. // Don't kick the host or an admin
  220. if ( ( !engine->IsDedicatedServer() && m_hPlayerTarget->entindex() == 1 ) ||
  221. m_hPlayerTarget->IsAutoKickDisabled() )
  222. {
  223. nFailCode = VOTE_FAILED_CANNOT_KICK_ADMIN;
  224. return false;
  225. }
  226. // Store caller steamID
  227. CTFPlayer *pTFVoteCaller = ToTFPlayer( UTIL_EntityByIndex( iEntIndex ) );
  228. if ( !pTFVoteCaller )
  229. return false;
  230. pTFVoteCaller->GetSteamID( &m_steamIDVoteCaller );
  231. if ( !m_steamIDVoteCaller.IsValid() || !m_steamIDVoteCaller.BIndividualAccount() )
  232. return false;
  233. // Store target steamID - if they're not a bot
  234. bool bFakeClient = m_hPlayerTarget->IsFakeClient() || m_hPlayerTarget->IsBot();
  235. if ( !bFakeClient )
  236. {
  237. m_hPlayerTarget->GetSteamID( &m_steamIDVoteTarget );
  238. if ( !m_steamIDVoteTarget.IsValid() || !m_steamIDVoteTarget.BIndividualAccount() )
  239. return false;
  240. }
  241. // MvM
  242. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  243. {
  244. // Don't allow kicking unless we're between rounds
  245. if ( TFGameRules() && TFGameRules()->State_Get() != GR_STATE_BETWEEN_RNDS )
  246. {
  247. nFailCode = VOTE_FAILED_CANNOT_KICK_DURING_ROUND;
  248. return false;
  249. }
  250. // Allow kicking team unassigned
  251. if ( m_hPlayerTarget->IsConnected() && m_hPlayerTarget->GetTeamNumber() == TEAM_UNASSIGNED )
  252. return true;
  253. // Don't allow kicking of players connected less than sv_vote_kick_min_connect_time_mvm
  254. CTFPlayer *pTFVoteTarget = ToTFPlayer( m_hPlayerTarget );
  255. if ( pTFVoteTarget )
  256. {
  257. float flTimeConnected = gpGlobals->curtime - pTFVoteTarget->GetConnectionTime();
  258. // See if we have a lobby...
  259. if ( !bFakeClient )
  260. {
  261. CMatchInfo::PlayerMatchData_t *pMatchPlayerTarget = GTFGCClientSystem()->GetLiveMatchPlayer( m_steamIDVoteTarget );
  262. CMatchInfo::PlayerMatchData_t *pMatchPlayerCaller = GTFGCClientSystem()->GetLiveMatchPlayer( m_steamIDVoteCaller );
  263. if ( pMatchPlayerTarget )
  264. {
  265. // Use this time instead (prevents disconnect avoidance)
  266. flTimeConnected = CRTime::RTime32TimeCur() - pMatchPlayerTarget->rtJoinedMatch;
  267. if ( sv_vote_issue_kick_limit_mvm.GetInt() )
  268. {
  269. if ( pMatchPlayerCaller && pMatchPlayerCaller->nVoteKickAttempts > (uint32)sv_vote_issue_kick_limit_mvm.GetInt() )
  270. {
  271. nFailCode = VOTE_FAILED_KICK_LIMIT_REACHED;
  272. return false;
  273. }
  274. }
  275. }
  276. }
  277. if ( flTimeConnected < sv_vote_issue_kick_min_connect_time_mvm.GetFloat() )
  278. {
  279. nFailCode = VOTE_FAILED_CANNOT_KICK_FOR_TIME;
  280. nTime = sv_vote_issue_kick_min_connect_time_mvm.GetFloat() - flTimeConnected;
  281. return false;
  282. }
  283. }
  284. // Allow kicking of spectators when this is set, except when it's a bot (invader bots are spectators between rounds)
  285. if ( sv_vote_issue_kick_spectators_mvm.GetBool() && !m_hPlayerTarget->IsBot() && m_hPlayerTarget->GetTeamNumber() == TEAM_SPECTATOR )
  286. return true;
  287. }
  288. // Don't kick players on other teams
  289. if ( pTFVoteCaller->GetTeamNumber() != m_hPlayerTarget->GetTeamNumber() )
  290. return false;
  291. return true;
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Purpose:
  295. //-----------------------------------------------------------------------------
  296. void CKickIssue::OnVoteFailed( int iEntityHoldingVote )
  297. {
  298. CBaseTFIssue::OnVoteFailed( iEntityHoldingVote );
  299. m_bGCNotified = false;
  300. m_bGCApproved = false;
  301. m_bGCResponded = false;
  302. PrintLogData();
  303. NotifyGCAdHocKick( false );
  304. }
  305. //-----------------------------------------------------------------------------
  306. // Purpose:
  307. //-----------------------------------------------------------------------------
  308. void CKickIssue::OnVoteStarted( void )
  309. {
  310. m_bGCNotified = false;
  311. m_bGCApproved = false;
  312. m_bGCResponded = false;
  313. // CanCallVote() should have initialized this
  314. if ( !m_hPlayerTarget )
  315. {
  316. m_hPlayerTarget = UTIL_PlayerBySteamID( m_steamIDVoteTarget );
  317. }
  318. if ( !m_hPlayerTarget )
  319. return;
  320. // Capture some data about the kick target now, so they can't avoid the
  321. // result by doing things like drop, retry, stop sending commands, etc.
  322. if ( m_steamIDVoteTarget.IsValid() && m_steamIDVoteTarget.BIndividualAccount() )
  323. {
  324. Q_strncpy( m_szTargetPlayerName, m_hPlayerTarget->GetPlayerName(), sizeof( m_szTargetPlayerName ) );
  325. // Configured to block name changing when targeted for a kick?
  326. if ( sv_vote_issue_kick_namelock_duration.GetFloat() > 0 )
  327. {
  328. g_voteController->AddPlayerToNameLockedList( m_steamIDVoteTarget, sv_vote_issue_kick_namelock_duration.GetFloat(), m_hPlayerTarget->GetUserID() );
  329. }
  330. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  331. {
  332. CMatchInfo::PlayerMatchData_t *pMatchPlayerCaller = GTFGCClientSystem()->GetLiveMatchPlayer( m_steamIDVoteCaller );
  333. if ( pMatchPlayerCaller )
  334. {
  335. pMatchPlayerCaller->nVoteKickAttempts++;
  336. }
  337. }
  338. }
  339. // Auto vote 'No' for the person being kicked unless they are idle
  340. CTFPlayer *pTFVoteTarget = ToTFPlayer( m_hPlayerTarget );
  341. if ( pTFVoteTarget && !pTFVoteTarget->IsAwayFromKeyboard() && ( pTFVoteTarget->GetTeamNumber() != TEAM_SPECTATOR ) )
  342. {
  343. g_voteController->TryCastVote( pTFVoteTarget->entindex(), "Option2" );
  344. }
  345. }
  346. //-----------------------------------------------------------------------------
  347. // Purpose:
  348. //-----------------------------------------------------------------------------
  349. const char *CKickIssue::GetDisplayString( void )
  350. {
  351. switch ( m_unKickReason )
  352. {
  353. case kVoteKickBanPlayerReason_Other: return "#TF_vote_kick_player_other";
  354. case kVoteKickBanPlayerReason_Cheating: return "#TF_vote_kick_player_cheating";
  355. case kVoteKickBanPlayerReason_Idle: return "#TF_vote_kick_player_idle";
  356. case kVoteKickBanPlayerReason_Scamming: return "#TF_vote_kick_player_scamming";
  357. }
  358. return "#TF_vote_kick_player_other";
  359. }
  360. //-----------------------------------------------------------------------------
  361. // Purpose:
  362. //-----------------------------------------------------------------------------
  363. const char *CKickIssue::GetVotePassedString( void )
  364. {
  365. // Player left before we could finish, but still got banned
  366. if ( !m_hPlayerTarget && sv_vote_kick_ban_duration.GetInt() > 0 )
  367. return "#TF_vote_passed_ban_player";
  368. // Player is still here
  369. return "#TF_vote_passed_kick_player";
  370. }
  371. //-----------------------------------------------------------------------------
  372. // Purpose:
  373. //-----------------------------------------------------------------------------
  374. const char *CKickIssue::GetDetailsString( void )
  375. {
  376. if ( m_hPlayerTarget )
  377. return m_hPlayerTarget->GetPlayerName();
  378. // If they left, use name stored at creation
  379. if ( V_strlen( m_szTargetPlayerName ) )
  380. return m_szTargetPlayerName;
  381. return "Unnamed";
  382. }
  383. //-----------------------------------------------------------------------------
  384. // Purpose:
  385. //-----------------------------------------------------------------------------
  386. bool CKickIssue::NeedsPermissionFromGC( void )
  387. {
  388. if ( sv_vote_issue_kick_limit_gc.GetBool() )
  389. {
  390. // Ask the GC if this is allowed (unless we've already asked, or the player is AFK - which makes it "free")
  391. if ( GTFGCClientSystem()->GetLiveMatch() && !m_bGCNotified )
  392. {
  393. CTFPlayer *pTFVoteTarget = ToTFPlayer( m_hPlayerTarget );
  394. if ( !pTFVoteTarget || pTFVoteTarget->IsAwayFromKeyboard() )
  395. return false;
  396. GCSDK::CProtoBufMsg< CMsgGC_TFVoteKickPlayerRequest > msgRequestVote( k_EMsgGCVoteKickPlayerRequest );
  397. msgRequestVote.Body().set_account_id( m_steamIDVoteCaller.GetAccountID() );
  398. msgRequestVote.Body().set_target_id( pTFVoteTarget->GetSteamIDAsUInt64() );
  399. GCClientSystem()->BSendMessage( msgRequestVote );
  400. m_bGCNotified = true;
  401. return true;
  402. }
  403. }
  404. return false;
  405. }
  406. //-----------------------------------------------------------------------------
  407. // Purpose:
  408. //-----------------------------------------------------------------------------
  409. void CKickIssue::NotifyGCAdHocKick( bool bKickedSuccessfully )
  410. {
  411. if ( m_steamIDVoteCaller.IsValid() && m_steamIDVoteTarget.IsValid() && m_steamIDVoteTarget.BIndividualAccount() )
  412. {
  413. GCSDK::CProtoBufMsg<CMsgTFVoteKickBanPlayerResult> msg( k_EMsgGCVoteKickBanPlayerResult );
  414. msg.Body().set_account_id_initiator( m_steamIDVoteCaller.GetAccountID() );
  415. msg.Body().set_account_id_subject( m_steamIDVoteTarget.GetAccountID() );
  416. msg.Body().set_kick_successful( bKickedSuccessfully );
  417. msg.Body().set_kick_reason( m_unKickReason );
  418. msg.Body().set_num_yes_votes( m_iNumYesVotes );
  419. msg.Body().set_num_no_votes( m_iNumNoVotes );
  420. msg.Body().set_num_possible_votes( m_iNumPotentialVotes );
  421. GCClientSystem()->BSendMessage( msg );
  422. }
  423. }
  424. //-----------------------------------------------------------------------------
  425. // Purpose:
  426. //-----------------------------------------------------------------------------
  427. void CKickIssue::PrintLogData( void )
  428. {
  429. bool bFakeClient = m_hPlayerTarget && ( m_hPlayerTarget->IsFakeClient() || m_hPlayerTarget->IsHLTV() || m_hPlayerTarget->IsReplay() );
  430. UTIL_LogPrintf( "Kick Vote details: VoteInitiatorSteamID: %s VoteTargetSteamID: %s Valid: %i BIndividual: %i Name: %s Proxy: %i\n",
  431. m_steamIDVoteCaller.IsValid() ? m_steamIDVoteCaller.Render() : "[unknown]",
  432. m_steamIDVoteTarget.Render(),
  433. m_steamIDVoteTarget.IsValid(),
  434. m_steamIDVoteTarget.BIndividualAccount(),
  435. m_hPlayerTarget ? m_szTargetPlayerName : "Disconnected",
  436. bFakeClient );
  437. }
  438. //-----------------------------------------------------------------------------
  439. // Purpose:
  440. //-----------------------------------------------------------------------------
  441. bool CKickIssue::CreateVoteDataFromDetails( const char *pszDetails )
  442. {
  443. int iUserID = 0;
  444. const char *pReasonString = strstr( pszDetails, " " );
  445. if ( pReasonString != NULL )
  446. {
  447. pReasonString += 1;
  448. CUtlString userID;
  449. userID.SetDirect( pszDetails, pReasonString - pszDetails );
  450. iUserID = atoi( userID );
  451. m_unKickReason = GetKickBanPlayerReason( pReasonString );
  452. }
  453. else
  454. {
  455. iUserID = atoi( pszDetails );
  456. }
  457. // Try to use the steamID we stored in OnVoteStarted() (will fail for bots)
  458. for ( int i = 0; i < MAX_PLAYERS; i++ )
  459. {
  460. CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
  461. if ( !pPlayer )
  462. continue;
  463. CSteamID steamID;
  464. if ( pPlayer->GetSteamID( &steamID ) && steamID == m_steamIDVoteTarget )
  465. {
  466. m_hPlayerTarget = pPlayer;
  467. return true;
  468. }
  469. }
  470. // Otherwise rely on userID
  471. if ( iUserID )
  472. {
  473. m_hPlayerTarget = ToBasePlayer( UTIL_PlayerByUserId( iUserID ) );
  474. }
  475. return ( m_hPlayerTarget ) ? true : false;
  476. }
  477. //-----------------------------------------------------------------------------
  478. // Purpose:
  479. //-----------------------------------------------------------------------------
  480. void CKickIssue::ListIssueDetails( CBasePlayer *pForWhom )
  481. {
  482. if( !sv_vote_issue_kick_allowed.GetBool() )
  483. return;
  484. char szBuffer[MAX_COMMAND_LENGTH];
  485. Q_snprintf( szBuffer, MAX_COMMAND_LENGTH, "callvote %s <userID>\n", GetTypeString() );
  486. ClientPrint( pForWhom, HUD_PRINTCONSOLE, szBuffer );
  487. }
  488. //-----------------------------------------------------------------------------
  489. // Purpose: Changelevel
  490. //-----------------------------------------------------------------------------
  491. ConVar sv_vote_issue_changelevel_allowed( "sv_vote_issue_changelevel_allowed", "0", FCVAR_NONE, "Can players call votes to change levels?" );
  492. ConVar sv_vote_issue_changelevel_allowed_mvm( "sv_vote_issue_changelevel_allowed_mvm", "0", FCVAR_NONE, "Can players call votes to change levels in MvM?" );
  493. //-----------------------------------------------------------------------------
  494. // Purpose:
  495. //-----------------------------------------------------------------------------
  496. void CChangeLevelIssue::ExecuteCommand( void )
  497. {
  498. engine->ChangeLevel( m_szDetailsString, NULL );
  499. }
  500. //-----------------------------------------------------------------------------
  501. // Purpose:
  502. //-----------------------------------------------------------------------------
  503. bool CChangeLevelIssue::CanTeamCallVote( int iTeam ) const
  504. {
  505. return true;
  506. }
  507. //-----------------------------------------------------------------------------
  508. // Purpose:
  509. //-----------------------------------------------------------------------------
  510. bool CChangeLevelIssue::IsEnabled( void )
  511. {
  512. if ( TFGameRules() )
  513. {
  514. if ( TFGameRules()->IsMannVsMachineMode() )
  515. return sv_vote_issue_changelevel_allowed_mvm.GetBool();
  516. }
  517. return sv_vote_issue_changelevel_allowed.GetBool();
  518. }
  519. //-----------------------------------------------------------------------------
  520. // Purpose:
  521. //-----------------------------------------------------------------------------
  522. bool CChangeLevelIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  523. {
  524. if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  525. return false;
  526. if( !IsEnabled() )
  527. {
  528. nFailCode = VOTE_FAILED_ISSUE_DISABLED;
  529. return false;
  530. }
  531. if ( !Q_strcmp( pszDetails, "" ) )
  532. {
  533. nFailCode = VOTE_FAILED_MAP_NAME_REQUIRED;
  534. return false;
  535. }
  536. else
  537. {
  538. if ( !VotableMap( pszDetails ) )
  539. {
  540. nFailCode = VOTE_FAILED_MAP_NOT_FOUND;
  541. return false;
  542. }
  543. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
  544. {
  545. // We can't test if it's valid - deny
  546. if ( !g_pPopulationManager )
  547. {
  548. nFailCode = VOTE_FAILED_GENERIC;
  549. return false;
  550. }
  551. if ( !g_pPopulationManager->IsValidMvMMap( pszDetails ) )
  552. {
  553. nFailCode = VOTE_FAILED_MAP_NOT_VALID;
  554. return false;
  555. }
  556. }
  557. else
  558. {
  559. if ( MultiplayRules() && !MultiplayRules()->IsMapInMapCycle( pszDetails ) )
  560. {
  561. nFailCode = VOTE_FAILED_MAP_NOT_VALID;
  562. return false;
  563. }
  564. }
  565. }
  566. return true;
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Purpose:
  570. //-----------------------------------------------------------------------------
  571. const char *CChangeLevelIssue::GetDisplayString( void )
  572. {
  573. return "#TF_vote_changelevel";
  574. }
  575. //-----------------------------------------------------------------------------
  576. // Purpose:
  577. //-----------------------------------------------------------------------------
  578. const char *CChangeLevelIssue::GetVotePassedString( void )
  579. {
  580. return "#TF_vote_passed_changelevel";
  581. }
  582. //-----------------------------------------------------------------------------
  583. // Purpose:
  584. //-----------------------------------------------------------------------------
  585. const char *CChangeLevelIssue::GetDetailsString( void )
  586. {
  587. return m_szDetailsString;
  588. }
  589. //-----------------------------------------------------------------------------
  590. // Purpose:
  591. //-----------------------------------------------------------------------------
  592. void CChangeLevelIssue::ListIssueDetails( CBasePlayer *pForWhom )
  593. {
  594. if( !sv_vote_issue_changelevel_allowed.GetBool() )
  595. return;
  596. char szBuffer[MAX_COMMAND_LENGTH];
  597. Q_snprintf( szBuffer, MAX_COMMAND_LENGTH, "callvote %s <mapname>\n", GetTypeString() );
  598. ClientPrint( pForWhom, HUD_PRINTCONSOLE, szBuffer );
  599. }
  600. //-----------------------------------------------------------------------------
  601. // Purpose:
  602. //-----------------------------------------------------------------------------
  603. bool CChangeLevelIssue::IsYesNoVote( void )
  604. {
  605. return true;
  606. }
  607. //-----------------------------------------------------------------------------
  608. // Purpose: Nextlevel
  609. //-----------------------------------------------------------------------------
  610. ConVar sv_vote_issue_nextlevel_allowed( "sv_vote_issue_nextlevel_allowed", "1", FCVAR_NONE, "Can players call votes to set the next level?" );
  611. ConVar sv_vote_issue_nextlevel_choicesmode( "sv_vote_issue_nextlevel_choicesmode", "0", FCVAR_NONE, "Present players with a list of lowest playtime maps to choose from?" );
  612. ConVar sv_vote_issue_nextlevel_allowextend( "sv_vote_issue_nextlevel_allowextend", "1", FCVAR_NONE, "Allow players to extend the current map?" );
  613. ConVar sv_vote_issue_nextlevel_prevent_change( "sv_vote_issue_nextlevel_prevent_change", "1", FCVAR_NONE, "Not allowed to vote for a nextlevel if one has already been set." );
  614. //-----------------------------------------------------------------------------
  615. // Purpose:
  616. //-----------------------------------------------------------------------------
  617. bool CNextLevelIssue::GetVoteOptions( CUtlVector <const char*> &vecNames )
  618. {
  619. m_IssueOptions.RemoveAll();
  620. // Reserve the last option for "Extend current Map?"
  621. int nNumOptions = sv_vote_issue_nextlevel_allowextend.GetBool() ? GetNumberVoteOptions() - 1 : GetNumberVoteOptions();
  622. // Ask the stats system for playtime data
  623. if ( CTF_GameStats.GetVoteData( "NextLevel", nNumOptions, m_IssueOptions ) )
  624. {
  625. FOR_EACH_VEC( m_IssueOptions, iIndex )
  626. {
  627. vecNames.AddToTail( m_IssueOptions[iIndex] );
  628. }
  629. if ( sv_vote_issue_nextlevel_allowextend.GetBool() || m_IssueOptions.Count() == 1 )
  630. {
  631. vecNames.AddToTail( "Extend current Map" );
  632. }
  633. return true;
  634. }
  635. return false;
  636. }
  637. //-----------------------------------------------------------------------------
  638. // Purpose:
  639. //-----------------------------------------------------------------------------
  640. void CNextLevelIssue::ExecuteCommand( void )
  641. {
  642. if ( Q_strcmp( m_szDetailsString, "Extend current Map" ) == 0 )
  643. {
  644. // Players want to extend the current map, so extend any existing limits
  645. if ( mp_timelimit.GetInt() > 0 )
  646. {
  647. engine->ServerCommand( CFmtStr( "mp_timelimit %d;", mp_timelimit.GetInt() + 20 ) );
  648. }
  649. if ( mp_maxrounds.GetInt() > 0 )
  650. {
  651. engine->ServerCommand( CFmtStr( "mp_maxrounds %d;", mp_maxrounds.GetInt() + 2 ) );
  652. }
  653. if ( mp_winlimit.GetInt() > 0 )
  654. {
  655. engine->ServerCommand( CFmtStr( "mp_winlimit %d;", mp_winlimit.GetInt() + 2 ) );
  656. }
  657. }
  658. else
  659. {
  660. nextlevel.SetValue( m_szDetailsString );
  661. }
  662. }
  663. //-----------------------------------------------------------------------------
  664. // Purpose:
  665. //-----------------------------------------------------------------------------
  666. bool CNextLevelIssue::CanTeamCallVote( int iTeam ) const
  667. {
  668. return true;
  669. }
  670. //-----------------------------------------------------------------------------
  671. // Purpose:
  672. //-----------------------------------------------------------------------------
  673. bool CNextLevelIssue::IsEnabled( void )
  674. {
  675. if ( TFGameRules() )
  676. {
  677. if ( TFGameRules()->IsMannVsMachineMode() )
  678. return false;
  679. }
  680. return sv_vote_issue_nextlevel_allowed.GetBool();
  681. }
  682. //-----------------------------------------------------------------------------
  683. // Purpose:
  684. //-----------------------------------------------------------------------------
  685. bool CNextLevelIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  686. {
  687. if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  688. return false;
  689. // TFGameRules created vote
  690. if ( sv_vote_issue_nextlevel_choicesmode.GetBool() && iEntIndex == 99 )
  691. {
  692. // Invokes a UI down stream
  693. if ( Q_strcmp( pszDetails, "" ) == 0 )
  694. {
  695. return true;
  696. }
  697. return false;
  698. }
  699. if( !IsEnabled() )
  700. {
  701. nFailCode = VOTE_FAILED_ISSUE_DISABLED;
  702. return false;
  703. }
  704. if ( Q_strcmp( pszDetails, "" ) == 0 )
  705. {
  706. nFailCode = VOTE_FAILED_MAP_NAME_REQUIRED;
  707. return false;
  708. }
  709. else
  710. {
  711. if ( !VotableMap( pszDetails ) )
  712. {
  713. nFailCode = VOTE_FAILED_MAP_NOT_FOUND;
  714. return false;
  715. }
  716. if ( MultiplayRules() && !MultiplayRules()->IsMapInMapCycle( pszDetails ) )
  717. {
  718. nFailCode = VOTE_FAILED_MAP_NOT_VALID;
  719. return false;
  720. }
  721. }
  722. if ( sv_vote_issue_nextlevel_prevent_change.GetBool() )
  723. {
  724. if ( nextlevel.GetString() && *nextlevel.GetString() )
  725. {
  726. nFailCode = VOTE_FAILED_NEXTLEVEL_SET;
  727. return false;
  728. }
  729. }
  730. return true;
  731. }
  732. //-----------------------------------------------------------------------------
  733. // Purpose:
  734. //-----------------------------------------------------------------------------
  735. const char *CNextLevelIssue::GetDisplayString( void )
  736. {
  737. // If we don't have a map passed in already...
  738. if ( Q_strcmp( m_szDetailsString, "" ) == 0 )
  739. {
  740. if ( sv_vote_issue_nextlevel_choicesmode.GetBool() )
  741. {
  742. return "#TF_vote_nextlevel_choices";
  743. }
  744. }
  745. return "#TF_vote_nextlevel";
  746. }
  747. //-----------------------------------------------------------------------------
  748. // Purpose:
  749. //-----------------------------------------------------------------------------
  750. const char *CNextLevelIssue::GetVotePassedString( void )
  751. {
  752. if ( sv_vote_issue_nextlevel_allowextend.GetBool() )
  753. {
  754. if ( Q_strcmp( m_szDetailsString, "Extend current Map" ) == 0 )
  755. {
  756. return "#TF_vote_passed_nextlevel_extend";
  757. }
  758. }
  759. return "#TF_vote_passed_nextlevel";
  760. }
  761. //-----------------------------------------------------------------------------
  762. // Purpose:
  763. //-----------------------------------------------------------------------------
  764. const char *CNextLevelIssue::GetDetailsString( void )
  765. {
  766. return m_szDetailsString;
  767. }
  768. //-----------------------------------------------------------------------------
  769. // Purpose:
  770. //-----------------------------------------------------------------------------
  771. void CNextLevelIssue::ListIssueDetails( CBasePlayer *pForWhom )
  772. {
  773. if( !sv_vote_issue_nextlevel_allowed.GetBool() )
  774. return;
  775. if ( !sv_vote_issue_nextlevel_choicesmode.GetBool() )
  776. {
  777. char szBuffer[MAX_COMMAND_LENGTH];
  778. Q_snprintf( szBuffer, MAX_COMMAND_LENGTH, "callvote %s <mapname>\n", GetTypeString() );
  779. ClientPrint( pForWhom, HUD_PRINTCONSOLE, szBuffer );
  780. }
  781. }
  782. //-----------------------------------------------------------------------------
  783. // Purpose:
  784. //-----------------------------------------------------------------------------
  785. bool CNextLevelIssue::IsYesNoVote( void )
  786. {
  787. // If we don't have a map name already, this will trigger a list of choices
  788. if ( Q_strcmp( m_szDetailsString, "" ) == 0 )
  789. {
  790. if ( sv_vote_issue_nextlevel_choicesmode.GetBool() )
  791. return false;
  792. }
  793. return true;
  794. }
  795. //-----------------------------------------------------------------------------
  796. // Purpose:
  797. //-----------------------------------------------------------------------------
  798. int CNextLevelIssue::GetNumberVoteOptions( void )
  799. {
  800. // If we don't have a map name already, this will trigger a list of choices
  801. if ( Q_strcmp( m_szDetailsString, "" ) == 0 )
  802. {
  803. if ( sv_vote_issue_nextlevel_choicesmode.GetBool() )
  804. return MAX_VOTE_OPTIONS;
  805. }
  806. // Vote on a specific map - Yes, No
  807. return 2;
  808. }
  809. //-----------------------------------------------------------------------------
  810. // Purpose:
  811. //-----------------------------------------------------------------------------
  812. float CNextLevelIssue::GetQuorumRatio( void )
  813. {
  814. // We don't really care about a quorum in this case. If a few
  815. // people have a preference on the next level, and no one else
  816. // bothers to vote, just let their choice pass.
  817. if ( sv_vote_issue_nextlevel_choicesmode.GetBool() )
  818. return 0.1f;
  819. // Default
  820. return sv_vote_quorum_ratio.GetFloat();
  821. }
  822. //-----------------------------------------------------------------------------
  823. // Purpose: Extend the current level
  824. //-----------------------------------------------------------------------------
  825. ConVar sv_vote_issue_extendlevel_allowed( "sv_vote_issue_extendlevel_allowed", "1", FCVAR_NONE, "Can players call votes to set the next level?" );
  826. ConVar sv_vote_issue_extendlevel_quorum( "sv_vote_issue_extendlevel_quorum", "0.6", FCVAR_NONE, "What is the ratio of voters needed to reach quorum?" );
  827. //-----------------------------------------------------------------------------
  828. // Purpose:
  829. //-----------------------------------------------------------------------------
  830. void CExtendLevelIssue::ExecuteCommand( void )
  831. {
  832. // Players want to extend the current map, so extend any existing limits
  833. if ( mp_timelimit.GetInt() > 0 )
  834. {
  835. engine->ServerCommand( CFmtStr( "mp_timelimit %d;", mp_timelimit.GetInt() + 20 ) );
  836. }
  837. if ( mp_maxrounds.GetInt() > 0 )
  838. {
  839. engine->ServerCommand( CFmtStr( "mp_maxrounds %d;", mp_maxrounds.GetInt() + 2 ) );
  840. }
  841. if ( mp_winlimit.GetInt() > 0 )
  842. {
  843. engine->ServerCommand( CFmtStr( "mp_winlimit %d;", mp_winlimit.GetInt() + 2 ) );
  844. }
  845. }
  846. //-----------------------------------------------------------------------------
  847. // Purpose:
  848. //-----------------------------------------------------------------------------
  849. bool CExtendLevelIssue::IsEnabled( void )
  850. {
  851. if ( TFGameRules() )
  852. {
  853. if ( TFGameRules()->IsMannVsMachineMode() )
  854. return false;
  855. }
  856. return sv_vote_issue_extendlevel_allowed.GetBool();
  857. }
  858. //-----------------------------------------------------------------------------
  859. // Purpose:
  860. //-----------------------------------------------------------------------------
  861. bool CExtendLevelIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  862. {
  863. if ( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  864. return false;
  865. if ( !IsEnabled() )
  866. {
  867. nFailCode = VOTE_FAILED_ISSUE_DISABLED;
  868. return false;
  869. }
  870. return true;
  871. }
  872. //-----------------------------------------------------------------------------
  873. // Purpose:
  874. //-----------------------------------------------------------------------------
  875. const char *CExtendLevelIssue::GetDisplayString( void )
  876. {
  877. return "#TF_vote_extendlevel";
  878. }
  879. //-----------------------------------------------------------------------------
  880. // Purpose:
  881. //-----------------------------------------------------------------------------
  882. void CExtendLevelIssue::ListIssueDetails( CBasePlayer *pForWhom )
  883. {
  884. if ( !sv_vote_issue_extendlevel_allowed.GetBool() )
  885. return;
  886. ListStandardNoArgCommand( pForWhom, GetTypeString() );
  887. }
  888. //-----------------------------------------------------------------------------
  889. // Purpose:
  890. //-----------------------------------------------------------------------------
  891. const char *CExtendLevelIssue::GetVotePassedString( void )
  892. {
  893. // We already had a localized string for this, even though we never use it.
  894. return "#TF_vote_passed_nextlevel_extend";
  895. }
  896. //-----------------------------------------------------------------------------
  897. // Purpose:
  898. //-----------------------------------------------------------------------------
  899. float CExtendLevelIssue::GetQuorumRatio( void )
  900. {
  901. // Our own quorom
  902. return sv_vote_issue_extendlevel_quorum.GetFloat();
  903. }
  904. //-----------------------------------------------------------------------------
  905. // Purpose: Scramble Teams Issue
  906. //-----------------------------------------------------------------------------
  907. ConVar sv_vote_issue_scramble_teams_allowed( "sv_vote_issue_scramble_teams_allowed", "1", FCVAR_NONE, "Can players call votes to scramble the teams?" );
  908. ConVar sv_vote_issue_scramble_teams_cooldown( "sv_vote_issue_scramble_teams_cooldown", "1200", FCVAR_NONE, "Minimum time before another scramble vote can occur (in seconds)." );
  909. //-----------------------------------------------------------------------------
  910. // Purpose:
  911. //-----------------------------------------------------------------------------
  912. void CScrambleTeams::ExecuteCommand( void )
  913. {
  914. if ( sv_vote_issue_scramble_teams_cooldown.GetInt() )
  915. {
  916. SetIssueCooldownDuration( sv_vote_issue_scramble_teams_cooldown.GetFloat() );
  917. }
  918. engine->ServerCommand( "mp_scrambleteams 2;" );
  919. }
  920. //-----------------------------------------------------------------------------
  921. // Purpose:
  922. //-----------------------------------------------------------------------------
  923. bool CScrambleTeams::IsEnabled( void )
  924. {
  925. if ( TFGameRules() )
  926. {
  927. if ( TFGameRules()->IsMannVsMachineMode() )
  928. return false;
  929. }
  930. return sv_vote_issue_scramble_teams_allowed.GetBool();
  931. }
  932. //-----------------------------------------------------------------------------
  933. // Purpose:
  934. //-----------------------------------------------------------------------------
  935. bool CScrambleTeams::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  936. {
  937. if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  938. return false;
  939. if( !IsEnabled() )
  940. {
  941. nFailCode = VOTE_FAILED_ISSUE_DISABLED;
  942. return false;
  943. }
  944. if ( TFGameRules() && TFGameRules()->ShouldScrambleTeams() )
  945. {
  946. nFailCode = VOTE_FAILED_SCRAMBLE_IN_PROGRESS;
  947. return false;
  948. }
  949. return true;
  950. }
  951. //-----------------------------------------------------------------------------
  952. // Purpose:
  953. //-----------------------------------------------------------------------------
  954. const char *CScrambleTeams::GetDisplayString( void )
  955. {
  956. return "#TF_vote_scramble_teams";
  957. }
  958. //-----------------------------------------------------------------------------
  959. // Purpose:
  960. //-----------------------------------------------------------------------------
  961. const char *CScrambleTeams::GetVotePassedString( void )
  962. {
  963. return "#TF_vote_passed_scramble_teams";
  964. }
  965. //-----------------------------------------------------------------------------
  966. // Purpose:
  967. //-----------------------------------------------------------------------------
  968. void CScrambleTeams::ListIssueDetails( CBasePlayer *pForWhom )
  969. {
  970. if( !sv_vote_issue_scramble_teams_allowed.GetBool() )
  971. return;
  972. ListStandardNoArgCommand( pForWhom, GetTypeString() );
  973. }
  974. //-----------------------------------------------------------------------------
  975. // Purpose: MvM Challenge Issue
  976. //-----------------------------------------------------------------------------
  977. ConVar sv_vote_issue_mvm_challenge_allowed( "sv_vote_issue_mvm_challenge_allowed", "1", FCVAR_NONE, "Can players call votes to set the challenge level?" );
  978. //-----------------------------------------------------------------------------
  979. // Purpose:
  980. //-----------------------------------------------------------------------------
  981. void CMannVsMachineChangeChallengeIssue::ExecuteCommand( void )
  982. {
  983. if ( Q_stricmp( m_szDetailsString, "normal" ) == 0 )
  984. {
  985. engine->ServerCommand( CFmtStr( "tf_mvm_popfile \"%s\";", STRING(gpGlobals->mapname) ) );
  986. }
  987. else
  988. {
  989. engine->ServerCommand( CFmtStr( "tf_mvm_popfile \"%s\";", m_szDetailsString ) );
  990. }
  991. }
  992. //-----------------------------------------------------------------------------
  993. // Purpose:
  994. //-----------------------------------------------------------------------------
  995. bool CMannVsMachineChangeChallengeIssue::CanTeamCallVote( int iTeam ) const
  996. {
  997. return true;
  998. }
  999. //-----------------------------------------------------------------------------
  1000. // Purpose: MvM-specific issue
  1001. //-----------------------------------------------------------------------------
  1002. bool CMannVsMachineChangeChallengeIssue::IsEnabled( void )
  1003. {
  1004. // Only allow in MvM
  1005. if ( TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
  1006. return false;
  1007. // But prevent on MannUp (Valve) servers
  1008. CMatchInfo *pMatch = GTFGCClientSystem()->GetMatch();
  1009. if ( pMatch && pMatch->m_eMatchGroup == k_nMatchGroup_MvM_MannUp )
  1010. {
  1011. return false;
  1012. }
  1013. return sv_vote_issue_mvm_challenge_allowed.GetBool();
  1014. }
  1015. //-----------------------------------------------------------------------------
  1016. // Purpose:
  1017. //-----------------------------------------------------------------------------
  1018. bool CMannVsMachineChangeChallengeIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  1019. {
  1020. if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  1021. return false;
  1022. if( !IsEnabled() )
  1023. {
  1024. nFailCode = VOTE_FAILED_ISSUE_DISABLED;
  1025. return false;
  1026. }
  1027. if ( Q_strcmp( pszDetails, "" ) == 0 )
  1028. {
  1029. nFailCode = VOTE_FAILED_MAP_NAME_REQUIRED;
  1030. return false;
  1031. }
  1032. else
  1033. {
  1034. // Make sure it's a valid pop
  1035. /*if ( !HaveExactMap( pszDetails ) )
  1036. {
  1037. nFailCode = VOTE_FAILED_MAP_NOT_FOUND;
  1038. return false;
  1039. }*/
  1040. }
  1041. return true;
  1042. }
  1043. //-----------------------------------------------------------------------------
  1044. // Purpose:
  1045. //-----------------------------------------------------------------------------
  1046. const char *CMannVsMachineChangeChallengeIssue::GetDisplayString( void )
  1047. {
  1048. return "#TF_vote_changechallenge";
  1049. }
  1050. //-----------------------------------------------------------------------------
  1051. // Purpose:
  1052. //-----------------------------------------------------------------------------
  1053. const char *CMannVsMachineChangeChallengeIssue::GetVotePassedString( void )
  1054. {
  1055. return "#TF_vote_passed_changechallenge";
  1056. }
  1057. //-----------------------------------------------------------------------------
  1058. // Purpose:
  1059. //-----------------------------------------------------------------------------
  1060. const char *CMannVsMachineChangeChallengeIssue::GetDetailsString( void )
  1061. {
  1062. return m_szDetailsString;
  1063. }
  1064. //-----------------------------------------------------------------------------
  1065. // Purpose:
  1066. //-----------------------------------------------------------------------------
  1067. void CMannVsMachineChangeChallengeIssue::ListIssueDetails( CBasePlayer *pForWhom )
  1068. {
  1069. if( !sv_vote_issue_mvm_challenge_allowed.GetBool() )
  1070. return;
  1071. char szBuffer[MAX_COMMAND_LENGTH];
  1072. Q_snprintf( szBuffer, MAX_COMMAND_LENGTH, "callvote %s <popfile>\n", GetTypeString() );
  1073. ClientPrint( pForWhom, HUD_PRINTCONSOLE, szBuffer );
  1074. }
  1075. //-----------------------------------------------------------------------------
  1076. // Purpose:
  1077. //-----------------------------------------------------------------------------
  1078. bool CMannVsMachineChangeChallengeIssue::IsYesNoVote( void )
  1079. {
  1080. return true;
  1081. }
  1082. //-----------------------------------------------------------------------------
  1083. // Purpose:
  1084. //-----------------------------------------------------------------------------
  1085. int CMannVsMachineChangeChallengeIssue::GetNumberVoteOptions( void )
  1086. {
  1087. // Vote on a specific map - Yes, No
  1088. return 2;
  1089. }
  1090. //-----------------------------------------------------------------------------
  1091. // Purpose:
  1092. //-----------------------------------------------------------------------------
  1093. bool CEnableTemporaryHalloweenIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  1094. {
  1095. if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  1096. return false;
  1097. #ifndef STAGING_ONLY
  1098. // Prevent concommand calling of this vote
  1099. if ( iEntIndex != DEDICATED_SERVER )
  1100. return false;
  1101. #endif // !STAGING_ONLY
  1102. if( TFGameRules()->IsHolidayActive( kHoliday_HalloweenOrFullMoon ) )
  1103. {
  1104. nFailCode = VOTE_FAILED_MODIFICATION_ALREADY_ACTIVE;
  1105. return false;
  1106. }
  1107. return true;
  1108. }
  1109. //-----------------------------------------------------------------------------
  1110. // Purpose:
  1111. //-----------------------------------------------------------------------------
  1112. void CEnableTemporaryHalloweenIssue::ListIssueDetails( CBasePlayer *pForWhom )
  1113. {
  1114. ListStandardNoArgCommand( pForWhom, GetTypeString() );
  1115. }
  1116. //-----------------------------------------------------------------------------
  1117. // Purpose:
  1118. //-----------------------------------------------------------------------------
  1119. static void SendVoteResponseToGC( bool bVoteResponse )
  1120. {
  1121. if ( GTFGCClientSystem() )
  1122. {
  1123. // Tell the GC.
  1124. GCSDK::CProtoBufMsg<CMsgGC_GameServer_UseServerModificationItem_Response> msgResponse( k_EMsgGC_GameServer_UseServerModificationItem_Response );
  1125. msgResponse.Body().set_server_response_code( bVoteResponse ? CMsgGC_GameServer_UseServerModificationItem_Response::kServerModificationItemServerResponse_Accepted : CMsgGC_GameServer_UseServerModificationItem_Response::kServerModificationItemServerResponse_VoteFailed );
  1126. GTFGCClientSystem()->BSendMessage( msgResponse );
  1127. // Tell the players on the server.
  1128. if ( bVoteResponse )
  1129. {
  1130. TFGameRules()->BroadcastSound( 255, RandomInt( 0, 100 ) <= 10 ? "Halloween.MerasmusHalloweenModeRare" : "Halloween.MerasmusHalloweenModeCommon" );
  1131. }
  1132. }
  1133. }
  1134. //-----------------------------------------------------------------------------
  1135. // Purpose:
  1136. //-----------------------------------------------------------------------------
  1137. void CEnableTemporaryHalloweenIssue::ExecuteCommand( void )
  1138. {
  1139. SendVoteResponseToGC( true );
  1140. }
  1141. //-----------------------------------------------------------------------------
  1142. // Purpose:
  1143. //-----------------------------------------------------------------------------
  1144. void CEnableTemporaryHalloweenIssue::OnVoteFailed( int iEntityHoldingVote )
  1145. {
  1146. CBaseTFIssue::OnVoteFailed( iEntityHoldingVote );
  1147. SendVoteResponseToGC( false );
  1148. }
  1149. //-----------------------------------------------------------------------------
  1150. // Purpose: Enable/Disable mp_autoteambalance
  1151. //-----------------------------------------------------------------------------
  1152. ConVar sv_vote_issue_autobalance_allowed( "sv_vote_issue_autobalance_allowed", "0", FCVAR_NONE, "Can players call votes to enable or disable auto team balance?" );
  1153. ConVar sv_vote_issue_autobalance_cooldown( "sv_vote_issue_autobalance_cooldown", "300", FCVAR_NONE, "Minimum time before another auto team balance vote can occur (in seconds)." );
  1154. //-----------------------------------------------------------------------------
  1155. // Purpose:
  1156. //-----------------------------------------------------------------------------
  1157. const char *CTeamAutoBalanceIssue::GetTypeStringLocalized( void )
  1158. {
  1159. // Disabled
  1160. if ( !mp_autoteambalance.GetInt() )
  1161. {
  1162. return "#Vote_TeamAutoBalance_Enable";
  1163. }
  1164. return "#Vote_TeamAutoBalance_Disable";
  1165. }
  1166. //-----------------------------------------------------------------------------
  1167. // Purpose:
  1168. //-----------------------------------------------------------------------------
  1169. void CTeamAutoBalanceIssue::ExecuteCommand( void )
  1170. {
  1171. if ( sv_vote_issue_autobalance_cooldown.GetInt() )
  1172. {
  1173. SetIssueCooldownDuration( sv_vote_issue_autobalance_cooldown.GetFloat() );
  1174. }
  1175. // Disable
  1176. if ( mp_autoteambalance.GetInt() )
  1177. {
  1178. engine->ServerCommand( "mp_autoteambalance 0;" );
  1179. }
  1180. // Enable
  1181. else
  1182. {
  1183. engine->ServerCommand( "mp_autoteambalance 1;" );
  1184. }
  1185. }
  1186. //-----------------------------------------------------------------------------
  1187. // Purpose:
  1188. //-----------------------------------------------------------------------------
  1189. bool CTeamAutoBalanceIssue::IsEnabled( void )
  1190. {
  1191. if ( !TFGameRules() || !TFGameRules()->IsDefaultGameMode() )
  1192. return false;
  1193. return sv_vote_issue_autobalance_allowed.GetBool();
  1194. }
  1195. //-----------------------------------------------------------------------------
  1196. // Purpose:
  1197. //-----------------------------------------------------------------------------
  1198. bool CTeamAutoBalanceIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  1199. {
  1200. if ( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  1201. return false;
  1202. if ( !IsEnabled() )
  1203. {
  1204. nFailCode = VOTE_FAILED_ISSUE_DISABLED;
  1205. return false;
  1206. }
  1207. return true;
  1208. }
  1209. //-----------------------------------------------------------------------------
  1210. // Purpose:
  1211. //-----------------------------------------------------------------------------
  1212. const char *CTeamAutoBalanceIssue::GetDisplayString( void )
  1213. {
  1214. // Disable
  1215. if ( mp_autoteambalance.GetInt() )
  1216. return "#TF_vote_autobalance_disable";
  1217. // Enable
  1218. return "#TF_vote_autobalance_enable";
  1219. }
  1220. //-----------------------------------------------------------------------------
  1221. // Purpose:
  1222. //-----------------------------------------------------------------------------
  1223. const char *CTeamAutoBalanceIssue::GetVotePassedString( void )
  1224. {
  1225. // Disable
  1226. if ( mp_autoteambalance.GetInt() )
  1227. return "#TF_vote_passed_autobalance_disable";
  1228. // Enable
  1229. return "#TF_vote_passed_autobalance_enable";
  1230. }
  1231. //-----------------------------------------------------------------------------
  1232. // Purpose:
  1233. //-----------------------------------------------------------------------------
  1234. void CTeamAutoBalanceIssue::ListIssueDetails( CBasePlayer *pForWhom )
  1235. {
  1236. if ( !sv_vote_issue_autobalance_allowed.GetBool() )
  1237. return;
  1238. ListStandardNoArgCommand( pForWhom, GetTypeString() );
  1239. }
  1240. //-----------------------------------------------------------------------------
  1241. // Purpose:
  1242. //-----------------------------------------------------------------------------
  1243. float CTeamAutoBalanceIssue::GetQuorumRatio( void )
  1244. {
  1245. float flRatio = sv_vote_quorum_ratio.GetFloat();
  1246. // Disable
  1247. if ( mp_autoteambalance.GetInt() )
  1248. return flRatio;
  1249. // Enable
  1250. return Max( 0.1f, flRatio * 0.7f );
  1251. }
  1252. //-----------------------------------------------------------------------------
  1253. // Purpose: Enable/Disable tf_classlimit
  1254. //-----------------------------------------------------------------------------
  1255. #ifdef STAGING_ONLY
  1256. ConVar sv_vote_issue_classlimits_allowed( "sv_vote_issue_classlimits_allowed", "1", FCVAR_NONE, "Can players call votes to enable or disable per-class limits?" );
  1257. #else
  1258. ConVar sv_vote_issue_classlimits_allowed( "sv_vote_issue_classlimits_allowed", "0", FCVAR_NONE, "Can players call votes to enable or disable per-class limits?" );
  1259. #endif
  1260. ConVar sv_vote_issue_classlimits_allowed_mvm( "sv_vote_issue_classlimits_allowed_mvm", "0", FCVAR_NONE, "Can players call votes in Mann-Vs-Machine to enable or disable per-class limits?" );
  1261. ConVar sv_vote_issue_classlimits_max( "sv_vote_issue_classlimits_max", "4", FCVAR_NONE, "Maximum number of players (per-team) that can be any one class.", true, 1.f, false, 16.f );
  1262. ConVar sv_vote_issue_classlimits_max_mvm( "sv_vote_issue_classlimits_max_mvm", "2", FCVAR_NONE, "Maximum number of players (per-team) that can be any one class.", true, 1.f, false, 16.f );
  1263. ConVar sv_vote_issue_classlimits_cooldown( "sv_vote_issue_classlimits_cooldown", "300", FCVAR_NONE, "Minimum time before another classlimits vote can occur (in seconds)." );
  1264. //-----------------------------------------------------------------------------
  1265. // Purpose:
  1266. //-----------------------------------------------------------------------------
  1267. const char *CClassLimitsIssue::GetTypeStringLocalized( void )
  1268. {
  1269. // Disabled
  1270. if ( !tf_classlimit.GetInt() )
  1271. {
  1272. return "#Vote_ClassLimit_Enable";
  1273. }
  1274. return "#Vote_ClassLimit_Disable";
  1275. }
  1276. //-----------------------------------------------------------------------------
  1277. // Purpose:
  1278. //-----------------------------------------------------------------------------
  1279. void CClassLimitsIssue::ExecuteCommand( void )
  1280. {
  1281. if ( sv_vote_issue_classlimits_cooldown.GetInt() )
  1282. {
  1283. SetIssueCooldownDuration( sv_vote_issue_classlimits_cooldown.GetFloat() );
  1284. }
  1285. // Disable
  1286. if ( tf_classlimit.GetInt() )
  1287. {
  1288. engine->ServerCommand( "tf_classlimit 0;" );
  1289. }
  1290. // Enable
  1291. else
  1292. {
  1293. int nLimit = ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) ? sv_vote_issue_classlimits_max_mvm.GetInt() : sv_vote_issue_classlimits_max.GetInt();
  1294. engine->ServerCommand( CFmtStr( "tf_classlimit %i;", nLimit ) );
  1295. }
  1296. }
  1297. //-----------------------------------------------------------------------------
  1298. // Purpose:
  1299. //-----------------------------------------------------------------------------
  1300. bool CClassLimitsIssue::IsEnabled( void )
  1301. {
  1302. if ( TFGameRules() )
  1303. {
  1304. // Manages class limits already
  1305. if ( TFGameRules()->IsInTournamentMode() )
  1306. return false;
  1307. // Manages class limits already
  1308. if ( TFGameRules()->IsInHighlanderMode() )
  1309. return false;
  1310. if ( TFGameRules()->IsMannVsMachineMode() )
  1311. return sv_vote_issue_classlimits_allowed_mvm.GetBool();
  1312. }
  1313. return sv_vote_issue_classlimits_allowed.GetBool();
  1314. }
  1315. //-----------------------------------------------------------------------------
  1316. // Purpose:
  1317. //-----------------------------------------------------------------------------
  1318. bool CClassLimitsIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  1319. {
  1320. if ( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  1321. return false;
  1322. if ( !IsEnabled() )
  1323. {
  1324. nFailCode = VOTE_FAILED_ISSUE_DISABLED;
  1325. return false;
  1326. }
  1327. return true;
  1328. }
  1329. //-----------------------------------------------------------------------------
  1330. // Purpose:
  1331. //-----------------------------------------------------------------------------
  1332. const char *CClassLimitsIssue::GetDisplayString( void )
  1333. {
  1334. // Disable
  1335. if ( tf_classlimit.GetInt() )
  1336. return "#TF_vote_classlimits_disable";
  1337. // Enable
  1338. return "#TF_vote_classlimits_enable";
  1339. }
  1340. //-----------------------------------------------------------------------------
  1341. // Purpose:
  1342. //-----------------------------------------------------------------------------
  1343. const char *CClassLimitsIssue::GetVotePassedString( void )
  1344. {
  1345. // Disable
  1346. if ( tf_classlimit.GetInt() )
  1347. return "#TF_vote_passed_classlimits_disable";
  1348. // Enable
  1349. return "#TF_vote_passed_classlimits_enable";
  1350. }
  1351. //-----------------------------------------------------------------------------
  1352. // Purpose:
  1353. //-----------------------------------------------------------------------------
  1354. void CClassLimitsIssue::ListIssueDetails( CBasePlayer *pForWhom )
  1355. {
  1356. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && !sv_vote_issue_classlimits_allowed_mvm.GetBool() )
  1357. return;
  1358. if ( !sv_vote_issue_classlimits_allowed.GetBool() )
  1359. return;
  1360. ListStandardNoArgCommand( pForWhom, GetTypeString() );
  1361. }
  1362. //-----------------------------------------------------------------------------
  1363. // Purpose:
  1364. //-----------------------------------------------------------------------------
  1365. const char *CClassLimitsIssue::GetDetailsString( void )
  1366. {
  1367. int nLimit = ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) ? sv_vote_issue_classlimits_max_mvm.GetInt() : sv_vote_issue_classlimits_max.GetInt();
  1368. m_sRetString = CFmtStr( "%i", nLimit );
  1369. return m_sRetString.String();
  1370. }
  1371. //-----------------------------------------------------------------------------
  1372. // Purpose: Pause Game
  1373. //-----------------------------------------------------------------------------
  1374. ConVar sv_vote_issue_pause_game_allowed( "sv_vote_issue_pause_game_allowed", "0", FCVAR_HIDDEN, "Can players call votes to pause the game?" );
  1375. ConVar sv_vote_issue_pause_game_timer( "sv_vote_issue_pause_game_timer", "120", FCVAR_HIDDEN, "How long to pause the game for when this vote passes (in seconds)." );
  1376. ConVar sv_vote_issue_pause_game_cooldown( "sv_vote_issue_pause_game_cooldown", "1200", FCVAR_HIDDEN, "Minimum time before another pause vote can occur (in seconds)." );
  1377. //-----------------------------------------------------------------------------
  1378. // Purpose:
  1379. //-----------------------------------------------------------------------------
  1380. void CPauseGameIssue::ExecuteCommand( void )
  1381. {
  1382. if ( sv_vote_issue_pause_game_cooldown.GetInt() )
  1383. {
  1384. SetIssueCooldownDuration( sv_vote_issue_pause_game_cooldown.GetFloat() );
  1385. }
  1386. engine->SetPausedForced( true, sv_vote_issue_pause_game_timer.GetFloat() );
  1387. }
  1388. //-----------------------------------------------------------------------------
  1389. // Purpose:
  1390. //-----------------------------------------------------------------------------
  1391. bool CPauseGameIssue::IsEnabled( void )
  1392. {
  1393. if ( engine->IsPaused() )
  1394. return false;
  1395. return sv_vote_issue_pause_game_allowed.GetBool();
  1396. }
  1397. //-----------------------------------------------------------------------------
  1398. // Purpose:
  1399. //-----------------------------------------------------------------------------
  1400. bool CPauseGameIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
  1401. {
  1402. if ( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
  1403. return false;
  1404. if ( !IsEnabled() )
  1405. {
  1406. nFailCode = VOTE_FAILED_ISSUE_DISABLED;
  1407. return false;
  1408. }
  1409. return true;
  1410. }
  1411. //-----------------------------------------------------------------------------
  1412. // Purpose:
  1413. //-----------------------------------------------------------------------------
  1414. const char *CPauseGameIssue::GetDisplayString( void )
  1415. {
  1416. return "#TF_vote_pause_game";
  1417. }
  1418. //-----------------------------------------------------------------------------
  1419. // Purpose:
  1420. //-----------------------------------------------------------------------------
  1421. const char *CPauseGameIssue::GetVotePassedString( void )
  1422. {
  1423. return "#TF_vote_passed_pause_game";
  1424. }
  1425. //-----------------------------------------------------------------------------
  1426. // Purpose:
  1427. //-----------------------------------------------------------------------------
  1428. void CPauseGameIssue::ListIssueDetails( CBasePlayer *pForWhom )
  1429. {
  1430. if ( !sv_vote_issue_pause_game_allowed.GetBool() )
  1431. return;
  1432. ListStandardNoArgCommand( pForWhom, GetTypeString() );
  1433. }
  1434. //-----------------------------------------------------------------------------
  1435. // Purpose:
  1436. //-----------------------------------------------------------------------------
  1437. const char *CPauseGameIssue::GetDetailsString( void )
  1438. {
  1439. m_sRetString = CFmtStr( "%i", sv_vote_issue_pause_game_timer.GetInt() );
  1440. return (m_sRetString.String());
  1441. }