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.
1686 lines
56 KiB
1686 lines
56 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose: TF-specific things to vote on
|
||
|
//
|
||
|
//=============================================================================
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "tf_voteissues.h"
|
||
|
#include "tf_player.h"
|
||
|
|
||
|
#include "vote_controller.h"
|
||
|
#include "fmtstr.h"
|
||
|
#include "eiface.h"
|
||
|
#include "tf_gamerules.h"
|
||
|
#include "inetchannelinfo.h"
|
||
|
#include "tf_gamestats.h"
|
||
|
|
||
|
#include "tf_gcmessages.h"
|
||
|
#include "player_vs_environment/tf_population_manager.h"
|
||
|
#include "tf_mann_vs_machine_stats.h"
|
||
|
#include "tf_objective_resource.h"
|
||
|
#include "gc_clientsystem.h"
|
||
|
#include "tf_gc_server.h"
|
||
|
#include "player_resource.h"
|
||
|
#include "tf_player_resource.h"
|
||
|
|
||
|
// memdbgon must be the last include file in a .cpp file!!!
|
||
|
#include "tier0/memdbgon.h"
|
||
|
|
||
|
extern ConVar tf_mm_trusted;
|
||
|
extern ConVar mp_autoteambalance;
|
||
|
extern ConVar tf_classlimit;
|
||
|
extern ConVar sv_vote_quorum_ratio;
|
||
|
extern ConVar tf_mm_strict;
|
||
|
|
||
|
static bool VotableMap( const char *pszMapName )
|
||
|
{
|
||
|
char szCanonName[64] = { 0 };
|
||
|
V_strncpy( szCanonName, pszMapName, sizeof( szCanonName ) );
|
||
|
IVEngineServer::eFindMapResult eResult = engine->FindMap( szCanonName, sizeof( szCanonName ) );
|
||
|
|
||
|
switch ( eResult )
|
||
|
{
|
||
|
case IVEngineServer::eFindMap_Found:
|
||
|
case IVEngineServer::eFindMap_NonCanonical:
|
||
|
case IVEngineServer::eFindMap_PossiblyAvailable:
|
||
|
case IVEngineServer::eFindMap_FuzzyMatch:
|
||
|
return true;
|
||
|
case IVEngineServer::eFindMap_NotFound:
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
AssertMsg( false, "Unhandled engine->FindMap return value\n" );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Base TF Issue
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Restart Round Issue
|
||
|
//-----------------------------------------------------------------------------
|
||
|
ConVar sv_vote_issue_restart_game_allowed( "sv_vote_issue_restart_game_allowed", "0", FCVAR_NONE, "Can players call votes to restart the game?" );
|
||
|
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?" );
|
||
|
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)." );
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CRestartGameIssue::ExecuteCommand( void )
|
||
|
{
|
||
|
if ( sv_vote_issue_restart_game_cooldown.GetInt() )
|
||
|
{
|
||
|
SetIssueCooldownDuration( sv_vote_issue_restart_game_cooldown.GetFloat() );
|
||
|
}
|
||
|
|
||
|
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
|
||
|
{
|
||
|
g_pPopulationManager->ResetMap();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
engine->ServerCommand( "mp_restartgame 1;" );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CRestartGameIssue::IsEnabled( void )
|
||
|
{
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
if ( TFGameRules()->IsMannVsMachineMode() )
|
||
|
return sv_vote_issue_restart_game_allowed_mvm.GetBool();
|
||
|
}
|
||
|
|
||
|
return sv_vote_issue_restart_game_allowed.GetBool();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CRestartGameIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
if( !IsEnabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_ISSUE_DISABLED;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CRestartGameIssue::GetDisplayString( void )
|
||
|
{
|
||
|
return "#TF_vote_restart_game";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CRestartGameIssue::GetVotePassedString( void )
|
||
|
{
|
||
|
return "#TF_vote_passed_restart_game";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CRestartGameIssue::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
if( !sv_vote_issue_restart_game_allowed.GetBool() )
|
||
|
return;
|
||
|
|
||
|
ListStandardNoArgCommand( pForWhom, GetTypeString() );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Kick Player Issue
|
||
|
//-----------------------------------------------------------------------------
|
||
|
ConVar sv_vote_issue_kick_allowed( "sv_vote_issue_kick_allowed", "0", FCVAR_NONE, "Can players call votes to kick players from the server?" );
|
||
|
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?" );
|
||
|
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)" );
|
||
|
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)." );
|
||
|
ConVar sv_vote_issue_kick_spectators_mvm( "sv_vote_issue_kick_spectators_mvm", "1", FCVAR_NONE, "Allow players to kick spectators in MvM." );
|
||
|
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)." );
|
||
|
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)" );
|
||
|
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." );
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CKickIssue::Init( void )
|
||
|
{
|
||
|
m_szTargetPlayerName[0] = 0;
|
||
|
m_hPlayerTarget = NULL;
|
||
|
m_unKickReason = kVoteKickBanPlayerReason_Other;
|
||
|
m_steamIDVoteCaller.Clear();
|
||
|
m_steamIDVoteTarget.Clear();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CKickIssue::ExecuteCommand( void )
|
||
|
{
|
||
|
PrintLogData();
|
||
|
|
||
|
engine->ServerCommand( CFmtStr( "kickid \"%s\" %s\n", m_steamIDVoteTarget.Render(), g_pszVoteKickString ) );
|
||
|
|
||
|
GTFGCClientSystem()->MatchPlayerVoteKicked( m_steamIDVoteTarget );
|
||
|
|
||
|
if ( tf_mm_strict.GetInt() == 1 )
|
||
|
{
|
||
|
// If we're in strict match mode, we're done.
|
||
|
//
|
||
|
// If the GC does send them back here, banning them would put them in a broken rejoin state. The matchmaker
|
||
|
// should just not re-match you to somewhere you got kicked from.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// If we're not in strict mode they might be here as or be able to rejoin as an adhoc player -- ban.
|
||
|
// Technically the GC might send them back here for *new* match if they get kicked as ad-hoc, the current match ends, etc.
|
||
|
engine->ServerCommand( CFmtStr( "banid %d \"%s\"\n", sv_vote_kick_ban_duration.GetInt(), m_steamIDVoteTarget.Render() ) );
|
||
|
|
||
|
// 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
|
||
|
// if they come back.
|
||
|
//
|
||
|
// XXX(JohnS): We think the original cause behind this was fixed (connecting-but-not-active race condition)
|
||
|
g_voteController->AddPlayerToKickWatchList( m_steamIDVoteTarget, ( sv_vote_kick_ban_duration.GetFloat() * 60.f ) );
|
||
|
|
||
|
NotifyGCAdHocKick( true );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CKickIssue::IsEnabled( void )
|
||
|
{
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
if ( TFGameRules()->IsMannVsMachineMode() )
|
||
|
return sv_vote_issue_kick_allowed_mvm.GetBool();
|
||
|
}
|
||
|
|
||
|
return sv_vote_issue_kick_allowed.GetBool();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: This gets calle first. If true, moves on to OnVoteStarted()
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CKickIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if ( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
// We were waiting for an answer. Return it.
|
||
|
if ( m_bGCNotified && m_bGCResponded )
|
||
|
{
|
||
|
if ( !m_bGCApproved )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_KICK_DENIED_BY_GC;
|
||
|
}
|
||
|
|
||
|
bool bReturn = m_bGCApproved;
|
||
|
|
||
|
m_bGCApproved = false;
|
||
|
m_bGCNotified = false;
|
||
|
m_bGCResponded = false;
|
||
|
|
||
|
return bReturn;
|
||
|
}
|
||
|
|
||
|
Init();
|
||
|
|
||
|
if ( !IsEnabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_ISSUE_DISABLED;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( !CreateVoteDataFromDetails( pszDetails ) )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_PLAYERNOTFOUND;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Don't kick proxies
|
||
|
if ( m_hPlayerTarget->IsReplay() || m_hPlayerTarget->IsHLTV() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_PLAYERNOTFOUND;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Don't kick the host or an admin
|
||
|
if ( ( !engine->IsDedicatedServer() && m_hPlayerTarget->entindex() == 1 ) ||
|
||
|
m_hPlayerTarget->IsAutoKickDisabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_CANNOT_KICK_ADMIN;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Store caller steamID
|
||
|
CTFPlayer *pTFVoteCaller = ToTFPlayer( UTIL_EntityByIndex( iEntIndex ) );
|
||
|
if ( !pTFVoteCaller )
|
||
|
return false;
|
||
|
|
||
|
pTFVoteCaller->GetSteamID( &m_steamIDVoteCaller );
|
||
|
if ( !m_steamIDVoteCaller.IsValid() || !m_steamIDVoteCaller.BIndividualAccount() )
|
||
|
return false;
|
||
|
|
||
|
// Store target steamID - if they're not a bot
|
||
|
bool bFakeClient = m_hPlayerTarget->IsFakeClient() || m_hPlayerTarget->IsBot();
|
||
|
if ( !bFakeClient )
|
||
|
{
|
||
|
m_hPlayerTarget->GetSteamID( &m_steamIDVoteTarget );
|
||
|
if ( !m_steamIDVoteTarget.IsValid() || !m_steamIDVoteTarget.BIndividualAccount() )
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// MvM
|
||
|
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
|
||
|
{
|
||
|
// Don't allow kicking unless we're between rounds
|
||
|
if ( TFGameRules() && TFGameRules()->State_Get() != GR_STATE_BETWEEN_RNDS )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_CANNOT_KICK_DURING_ROUND;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Allow kicking team unassigned
|
||
|
if ( m_hPlayerTarget->IsConnected() && m_hPlayerTarget->GetTeamNumber() == TEAM_UNASSIGNED )
|
||
|
return true;
|
||
|
|
||
|
// Don't allow kicking of players connected less than sv_vote_kick_min_connect_time_mvm
|
||
|
CTFPlayer *pTFVoteTarget = ToTFPlayer( m_hPlayerTarget );
|
||
|
if ( pTFVoteTarget )
|
||
|
{
|
||
|
float flTimeConnected = gpGlobals->curtime - pTFVoteTarget->GetConnectionTime();
|
||
|
|
||
|
// See if we have a lobby...
|
||
|
if ( !bFakeClient )
|
||
|
{
|
||
|
CMatchInfo::PlayerMatchData_t *pMatchPlayerTarget = GTFGCClientSystem()->GetLiveMatchPlayer( m_steamIDVoteTarget );
|
||
|
CMatchInfo::PlayerMatchData_t *pMatchPlayerCaller = GTFGCClientSystem()->GetLiveMatchPlayer( m_steamIDVoteCaller );
|
||
|
if ( pMatchPlayerTarget )
|
||
|
{
|
||
|
// Use this time instead (prevents disconnect avoidance)
|
||
|
flTimeConnected = CRTime::RTime32TimeCur() - pMatchPlayerTarget->rtJoinedMatch;
|
||
|
|
||
|
if ( sv_vote_issue_kick_limit_mvm.GetInt() )
|
||
|
{
|
||
|
if ( pMatchPlayerCaller && pMatchPlayerCaller->nVoteKickAttempts > (uint32)sv_vote_issue_kick_limit_mvm.GetInt() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_KICK_LIMIT_REACHED;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( flTimeConnected < sv_vote_issue_kick_min_connect_time_mvm.GetFloat() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_CANNOT_KICK_FOR_TIME;
|
||
|
nTime = sv_vote_issue_kick_min_connect_time_mvm.GetFloat() - flTimeConnected;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Allow kicking of spectators when this is set, except when it's a bot (invader bots are spectators between rounds)
|
||
|
if ( sv_vote_issue_kick_spectators_mvm.GetBool() && !m_hPlayerTarget->IsBot() && m_hPlayerTarget->GetTeamNumber() == TEAM_SPECTATOR )
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Don't kick players on other teams
|
||
|
if ( pTFVoteCaller->GetTeamNumber() != m_hPlayerTarget->GetTeamNumber() )
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CKickIssue::OnVoteFailed( int iEntityHoldingVote )
|
||
|
{
|
||
|
CBaseTFIssue::OnVoteFailed( iEntityHoldingVote );
|
||
|
m_bGCNotified = false;
|
||
|
m_bGCApproved = false;
|
||
|
m_bGCResponded = false;
|
||
|
|
||
|
PrintLogData();
|
||
|
NotifyGCAdHocKick( false );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CKickIssue::OnVoteStarted( void )
|
||
|
{
|
||
|
m_bGCNotified = false;
|
||
|
m_bGCApproved = false;
|
||
|
m_bGCResponded = false;
|
||
|
|
||
|
// CanCallVote() should have initialized this
|
||
|
if ( !m_hPlayerTarget )
|
||
|
{
|
||
|
m_hPlayerTarget = UTIL_PlayerBySteamID( m_steamIDVoteTarget );
|
||
|
}
|
||
|
|
||
|
if ( !m_hPlayerTarget )
|
||
|
return;
|
||
|
|
||
|
// Capture some data about the kick target now, so they can't avoid the
|
||
|
// result by doing things like drop, retry, stop sending commands, etc.
|
||
|
if ( m_steamIDVoteTarget.IsValid() && m_steamIDVoteTarget.BIndividualAccount() )
|
||
|
{
|
||
|
Q_strncpy( m_szTargetPlayerName, m_hPlayerTarget->GetPlayerName(), sizeof( m_szTargetPlayerName ) );
|
||
|
|
||
|
// Configured to block name changing when targeted for a kick?
|
||
|
if ( sv_vote_issue_kick_namelock_duration.GetFloat() > 0 )
|
||
|
{
|
||
|
g_voteController->AddPlayerToNameLockedList( m_steamIDVoteTarget, sv_vote_issue_kick_namelock_duration.GetFloat(), m_hPlayerTarget->GetUserID() );
|
||
|
}
|
||
|
|
||
|
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
|
||
|
{
|
||
|
CMatchInfo::PlayerMatchData_t *pMatchPlayerCaller = GTFGCClientSystem()->GetLiveMatchPlayer( m_steamIDVoteCaller );
|
||
|
if ( pMatchPlayerCaller )
|
||
|
{
|
||
|
pMatchPlayerCaller->nVoteKickAttempts++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Auto vote 'No' for the person being kicked unless they are idle
|
||
|
CTFPlayer *pTFVoteTarget = ToTFPlayer( m_hPlayerTarget );
|
||
|
if ( pTFVoteTarget && !pTFVoteTarget->IsAwayFromKeyboard() && ( pTFVoteTarget->GetTeamNumber() != TEAM_SPECTATOR ) )
|
||
|
{
|
||
|
g_voteController->TryCastVote( pTFVoteTarget->entindex(), "Option2" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CKickIssue::GetDisplayString( void )
|
||
|
{
|
||
|
switch ( m_unKickReason )
|
||
|
{
|
||
|
case kVoteKickBanPlayerReason_Other: return "#TF_vote_kick_player_other";
|
||
|
case kVoteKickBanPlayerReason_Cheating: return "#TF_vote_kick_player_cheating";
|
||
|
case kVoteKickBanPlayerReason_Idle: return "#TF_vote_kick_player_idle";
|
||
|
case kVoteKickBanPlayerReason_Scamming: return "#TF_vote_kick_player_scamming";
|
||
|
}
|
||
|
return "#TF_vote_kick_player_other";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CKickIssue::GetVotePassedString( void )
|
||
|
{
|
||
|
// Player left before we could finish, but still got banned
|
||
|
if ( !m_hPlayerTarget && sv_vote_kick_ban_duration.GetInt() > 0 )
|
||
|
return "#TF_vote_passed_ban_player";
|
||
|
|
||
|
// Player is still here
|
||
|
return "#TF_vote_passed_kick_player";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CKickIssue::GetDetailsString( void )
|
||
|
{
|
||
|
if ( m_hPlayerTarget )
|
||
|
return m_hPlayerTarget->GetPlayerName();
|
||
|
|
||
|
// If they left, use name stored at creation
|
||
|
if ( V_strlen( m_szTargetPlayerName ) )
|
||
|
return m_szTargetPlayerName;
|
||
|
|
||
|
return "Unnamed";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CKickIssue::NeedsPermissionFromGC( void )
|
||
|
{
|
||
|
if ( sv_vote_issue_kick_limit_gc.GetBool() )
|
||
|
{
|
||
|
// Ask the GC if this is allowed (unless we've already asked, or the player is AFK - which makes it "free")
|
||
|
if ( GTFGCClientSystem()->GetLiveMatch() && !m_bGCNotified )
|
||
|
{
|
||
|
CTFPlayer *pTFVoteTarget = ToTFPlayer( m_hPlayerTarget );
|
||
|
if ( !pTFVoteTarget || pTFVoteTarget->IsAwayFromKeyboard() )
|
||
|
return false;
|
||
|
|
||
|
GCSDK::CProtoBufMsg< CMsgGC_TFVoteKickPlayerRequest > msgRequestVote( k_EMsgGCVoteKickPlayerRequest );
|
||
|
msgRequestVote.Body().set_account_id( m_steamIDVoteCaller.GetAccountID() );
|
||
|
msgRequestVote.Body().set_target_id( pTFVoteTarget->GetSteamIDAsUInt64() );
|
||
|
GCClientSystem()->BSendMessage( msgRequestVote );
|
||
|
|
||
|
m_bGCNotified = true;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CKickIssue::NotifyGCAdHocKick( bool bKickedSuccessfully )
|
||
|
{
|
||
|
if ( m_steamIDVoteCaller.IsValid() && m_steamIDVoteTarget.IsValid() && m_steamIDVoteTarget.BIndividualAccount() )
|
||
|
{
|
||
|
GCSDK::CProtoBufMsg<CMsgTFVoteKickBanPlayerResult> msg( k_EMsgGCVoteKickBanPlayerResult );
|
||
|
msg.Body().set_account_id_initiator( m_steamIDVoteCaller.GetAccountID() );
|
||
|
msg.Body().set_account_id_subject( m_steamIDVoteTarget.GetAccountID() );
|
||
|
msg.Body().set_kick_successful( bKickedSuccessfully );
|
||
|
msg.Body().set_kick_reason( m_unKickReason );
|
||
|
msg.Body().set_num_yes_votes( m_iNumYesVotes );
|
||
|
msg.Body().set_num_no_votes( m_iNumNoVotes );
|
||
|
msg.Body().set_num_possible_votes( m_iNumPotentialVotes );
|
||
|
GCClientSystem()->BSendMessage( msg );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CKickIssue::PrintLogData( void )
|
||
|
{
|
||
|
bool bFakeClient = m_hPlayerTarget && ( m_hPlayerTarget->IsFakeClient() || m_hPlayerTarget->IsHLTV() || m_hPlayerTarget->IsReplay() );
|
||
|
|
||
|
UTIL_LogPrintf( "Kick Vote details: VoteInitiatorSteamID: %s VoteTargetSteamID: %s Valid: %i BIndividual: %i Name: %s Proxy: %i\n",
|
||
|
m_steamIDVoteCaller.IsValid() ? m_steamIDVoteCaller.Render() : "[unknown]",
|
||
|
m_steamIDVoteTarget.Render(),
|
||
|
m_steamIDVoteTarget.IsValid(),
|
||
|
m_steamIDVoteTarget.BIndividualAccount(),
|
||
|
m_hPlayerTarget ? m_szTargetPlayerName : "Disconnected",
|
||
|
bFakeClient );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CKickIssue::CreateVoteDataFromDetails( const char *pszDetails )
|
||
|
{
|
||
|
int iUserID = 0;
|
||
|
const char *pReasonString = strstr( pszDetails, " " );
|
||
|
if ( pReasonString != NULL )
|
||
|
{
|
||
|
pReasonString += 1;
|
||
|
CUtlString userID;
|
||
|
userID.SetDirect( pszDetails, pReasonString - pszDetails );
|
||
|
iUserID = atoi( userID );
|
||
|
m_unKickReason = GetKickBanPlayerReason( pReasonString );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iUserID = atoi( pszDetails );
|
||
|
}
|
||
|
|
||
|
// Try to use the steamID we stored in OnVoteStarted() (will fail for bots)
|
||
|
for ( int i = 0; i < MAX_PLAYERS; i++ )
|
||
|
{
|
||
|
CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) );
|
||
|
if ( !pPlayer )
|
||
|
continue;
|
||
|
|
||
|
CSteamID steamID;
|
||
|
if ( pPlayer->GetSteamID( &steamID ) && steamID == m_steamIDVoteTarget )
|
||
|
{
|
||
|
m_hPlayerTarget = pPlayer;
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Otherwise rely on userID
|
||
|
if ( iUserID )
|
||
|
{
|
||
|
m_hPlayerTarget = ToBasePlayer( UTIL_PlayerByUserId( iUserID ) );
|
||
|
}
|
||
|
|
||
|
return ( m_hPlayerTarget ) ? true : false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CKickIssue::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
if( !sv_vote_issue_kick_allowed.GetBool() )
|
||
|
return;
|
||
|
|
||
|
char szBuffer[MAX_COMMAND_LENGTH];
|
||
|
Q_snprintf( szBuffer, MAX_COMMAND_LENGTH, "callvote %s <userID>\n", GetTypeString() );
|
||
|
ClientPrint( pForWhom, HUD_PRINTCONSOLE, szBuffer );
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Changelevel
|
||
|
//-----------------------------------------------------------------------------
|
||
|
ConVar sv_vote_issue_changelevel_allowed( "sv_vote_issue_changelevel_allowed", "0", FCVAR_NONE, "Can players call votes to change levels?" );
|
||
|
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?" );
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CChangeLevelIssue::ExecuteCommand( void )
|
||
|
{
|
||
|
engine->ChangeLevel( m_szDetailsString, NULL );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CChangeLevelIssue::CanTeamCallVote( int iTeam ) const
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CChangeLevelIssue::IsEnabled( void )
|
||
|
{
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
if ( TFGameRules()->IsMannVsMachineMode() )
|
||
|
return sv_vote_issue_changelevel_allowed_mvm.GetBool();
|
||
|
}
|
||
|
|
||
|
return sv_vote_issue_changelevel_allowed.GetBool();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CChangeLevelIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
if( !IsEnabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_ISSUE_DISABLED;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( !Q_strcmp( pszDetails, "" ) )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_MAP_NAME_REQUIRED;
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( !VotableMap( pszDetails ) )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_MAP_NOT_FOUND;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() )
|
||
|
{
|
||
|
// We can't test if it's valid - deny
|
||
|
if ( !g_pPopulationManager )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_GENERIC;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( !g_pPopulationManager->IsValidMvMMap( pszDetails ) )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_MAP_NOT_VALID;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( MultiplayRules() && !MultiplayRules()->IsMapInMapCycle( pszDetails ) )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_MAP_NOT_VALID;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CChangeLevelIssue::GetDisplayString( void )
|
||
|
{
|
||
|
return "#TF_vote_changelevel";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CChangeLevelIssue::GetVotePassedString( void )
|
||
|
{
|
||
|
return "#TF_vote_passed_changelevel";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CChangeLevelIssue::GetDetailsString( void )
|
||
|
{
|
||
|
return m_szDetailsString;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CChangeLevelIssue::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
if( !sv_vote_issue_changelevel_allowed.GetBool() )
|
||
|
return;
|
||
|
|
||
|
char szBuffer[MAX_COMMAND_LENGTH];
|
||
|
Q_snprintf( szBuffer, MAX_COMMAND_LENGTH, "callvote %s <mapname>\n", GetTypeString() );
|
||
|
ClientPrint( pForWhom, HUD_PRINTCONSOLE, szBuffer );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CChangeLevelIssue::IsYesNoVote( void )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Nextlevel
|
||
|
//-----------------------------------------------------------------------------
|
||
|
ConVar sv_vote_issue_nextlevel_allowed( "sv_vote_issue_nextlevel_allowed", "1", FCVAR_NONE, "Can players call votes to set the next level?" );
|
||
|
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?" );
|
||
|
ConVar sv_vote_issue_nextlevel_allowextend( "sv_vote_issue_nextlevel_allowextend", "1", FCVAR_NONE, "Allow players to extend the current map?" );
|
||
|
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." );
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CNextLevelIssue::GetVoteOptions( CUtlVector <const char*> &vecNames )
|
||
|
{
|
||
|
m_IssueOptions.RemoveAll();
|
||
|
|
||
|
// Reserve the last option for "Extend current Map?"
|
||
|
int nNumOptions = sv_vote_issue_nextlevel_allowextend.GetBool() ? GetNumberVoteOptions() - 1 : GetNumberVoteOptions();
|
||
|
|
||
|
// Ask the stats system for playtime data
|
||
|
if ( CTF_GameStats.GetVoteData( "NextLevel", nNumOptions, m_IssueOptions ) )
|
||
|
{
|
||
|
FOR_EACH_VEC( m_IssueOptions, iIndex )
|
||
|
{
|
||
|
vecNames.AddToTail( m_IssueOptions[iIndex] );
|
||
|
}
|
||
|
|
||
|
if ( sv_vote_issue_nextlevel_allowextend.GetBool() || m_IssueOptions.Count() == 1 )
|
||
|
{
|
||
|
vecNames.AddToTail( "Extend current Map" );
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CNextLevelIssue::ExecuteCommand( void )
|
||
|
{
|
||
|
if ( Q_strcmp( m_szDetailsString, "Extend current Map" ) == 0 )
|
||
|
{
|
||
|
// Players want to extend the current map, so extend any existing limits
|
||
|
if ( mp_timelimit.GetInt() > 0 )
|
||
|
{
|
||
|
engine->ServerCommand( CFmtStr( "mp_timelimit %d;", mp_timelimit.GetInt() + 20 ) );
|
||
|
}
|
||
|
|
||
|
if ( mp_maxrounds.GetInt() > 0 )
|
||
|
{
|
||
|
engine->ServerCommand( CFmtStr( "mp_maxrounds %d;", mp_maxrounds.GetInt() + 2 ) );
|
||
|
}
|
||
|
|
||
|
if ( mp_winlimit.GetInt() > 0 )
|
||
|
{
|
||
|
engine->ServerCommand( CFmtStr( "mp_winlimit %d;", mp_winlimit.GetInt() + 2 ) );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
nextlevel.SetValue( m_szDetailsString );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CNextLevelIssue::CanTeamCallVote( int iTeam ) const
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CNextLevelIssue::IsEnabled( void )
|
||
|
{
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
if ( TFGameRules()->IsMannVsMachineMode() )
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return sv_vote_issue_nextlevel_allowed.GetBool();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CNextLevelIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
// TFGameRules created vote
|
||
|
if ( sv_vote_issue_nextlevel_choicesmode.GetBool() && iEntIndex == 99 )
|
||
|
{
|
||
|
// Invokes a UI down stream
|
||
|
if ( Q_strcmp( pszDetails, "" ) == 0 )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if( !IsEnabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_ISSUE_DISABLED;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( Q_strcmp( pszDetails, "" ) == 0 )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_MAP_NAME_REQUIRED;
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( !VotableMap( pszDetails ) )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_MAP_NOT_FOUND;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( MultiplayRules() && !MultiplayRules()->IsMapInMapCycle( pszDetails ) )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_MAP_NOT_VALID;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( sv_vote_issue_nextlevel_prevent_change.GetBool() )
|
||
|
{
|
||
|
if ( nextlevel.GetString() && *nextlevel.GetString() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_NEXTLEVEL_SET;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CNextLevelIssue::GetDisplayString( void )
|
||
|
{
|
||
|
// If we don't have a map passed in already...
|
||
|
if ( Q_strcmp( m_szDetailsString, "" ) == 0 )
|
||
|
{
|
||
|
if ( sv_vote_issue_nextlevel_choicesmode.GetBool() )
|
||
|
{
|
||
|
return "#TF_vote_nextlevel_choices";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return "#TF_vote_nextlevel";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CNextLevelIssue::GetVotePassedString( void )
|
||
|
{
|
||
|
if ( sv_vote_issue_nextlevel_allowextend.GetBool() )
|
||
|
{
|
||
|
if ( Q_strcmp( m_szDetailsString, "Extend current Map" ) == 0 )
|
||
|
{
|
||
|
return "#TF_vote_passed_nextlevel_extend";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return "#TF_vote_passed_nextlevel";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CNextLevelIssue::GetDetailsString( void )
|
||
|
{
|
||
|
return m_szDetailsString;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CNextLevelIssue::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
if( !sv_vote_issue_nextlevel_allowed.GetBool() )
|
||
|
return;
|
||
|
|
||
|
if ( !sv_vote_issue_nextlevel_choicesmode.GetBool() )
|
||
|
{
|
||
|
char szBuffer[MAX_COMMAND_LENGTH];
|
||
|
Q_snprintf( szBuffer, MAX_COMMAND_LENGTH, "callvote %s <mapname>\n", GetTypeString() );
|
||
|
ClientPrint( pForWhom, HUD_PRINTCONSOLE, szBuffer );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CNextLevelIssue::IsYesNoVote( void )
|
||
|
{
|
||
|
// If we don't have a map name already, this will trigger a list of choices
|
||
|
if ( Q_strcmp( m_szDetailsString, "" ) == 0 )
|
||
|
{
|
||
|
if ( sv_vote_issue_nextlevel_choicesmode.GetBool() )
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CNextLevelIssue::GetNumberVoteOptions( void )
|
||
|
{
|
||
|
// If we don't have a map name already, this will trigger a list of choices
|
||
|
if ( Q_strcmp( m_szDetailsString, "" ) == 0 )
|
||
|
{
|
||
|
if ( sv_vote_issue_nextlevel_choicesmode.GetBool() )
|
||
|
return MAX_VOTE_OPTIONS;
|
||
|
}
|
||
|
|
||
|
// Vote on a specific map - Yes, No
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CNextLevelIssue::GetQuorumRatio( void )
|
||
|
{
|
||
|
// We don't really care about a quorum in this case. If a few
|
||
|
// people have a preference on the next level, and no one else
|
||
|
// bothers to vote, just let their choice pass.
|
||
|
if ( sv_vote_issue_nextlevel_choicesmode.GetBool() )
|
||
|
return 0.1f;
|
||
|
|
||
|
// Default
|
||
|
return sv_vote_quorum_ratio.GetFloat();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Extend the current level
|
||
|
//-----------------------------------------------------------------------------
|
||
|
ConVar sv_vote_issue_extendlevel_allowed( "sv_vote_issue_extendlevel_allowed", "1", FCVAR_NONE, "Can players call votes to set the next level?" );
|
||
|
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?" );
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CExtendLevelIssue::ExecuteCommand( void )
|
||
|
{
|
||
|
// Players want to extend the current map, so extend any existing limits
|
||
|
if ( mp_timelimit.GetInt() > 0 )
|
||
|
{
|
||
|
engine->ServerCommand( CFmtStr( "mp_timelimit %d;", mp_timelimit.GetInt() + 20 ) );
|
||
|
}
|
||
|
|
||
|
if ( mp_maxrounds.GetInt() > 0 )
|
||
|
{
|
||
|
engine->ServerCommand( CFmtStr( "mp_maxrounds %d;", mp_maxrounds.GetInt() + 2 ) );
|
||
|
}
|
||
|
|
||
|
if ( mp_winlimit.GetInt() > 0 )
|
||
|
{
|
||
|
engine->ServerCommand( CFmtStr( "mp_winlimit %d;", mp_winlimit.GetInt() + 2 ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CExtendLevelIssue::IsEnabled( void )
|
||
|
{
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
if ( TFGameRules()->IsMannVsMachineMode() )
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return sv_vote_issue_extendlevel_allowed.GetBool();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CExtendLevelIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if ( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
if ( !IsEnabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_ISSUE_DISABLED;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CExtendLevelIssue::GetDisplayString( void )
|
||
|
{
|
||
|
return "#TF_vote_extendlevel";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CExtendLevelIssue::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
if ( !sv_vote_issue_extendlevel_allowed.GetBool() )
|
||
|
return;
|
||
|
|
||
|
ListStandardNoArgCommand( pForWhom, GetTypeString() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CExtendLevelIssue::GetVotePassedString( void )
|
||
|
{
|
||
|
// We already had a localized string for this, even though we never use it.
|
||
|
return "#TF_vote_passed_nextlevel_extend";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CExtendLevelIssue::GetQuorumRatio( void )
|
||
|
{
|
||
|
// Our own quorom
|
||
|
return sv_vote_issue_extendlevel_quorum.GetFloat();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Scramble Teams Issue
|
||
|
//-----------------------------------------------------------------------------
|
||
|
ConVar sv_vote_issue_scramble_teams_allowed( "sv_vote_issue_scramble_teams_allowed", "1", FCVAR_NONE, "Can players call votes to scramble the teams?" );
|
||
|
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)." );
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CScrambleTeams::ExecuteCommand( void )
|
||
|
{
|
||
|
if ( sv_vote_issue_scramble_teams_cooldown.GetInt() )
|
||
|
{
|
||
|
SetIssueCooldownDuration( sv_vote_issue_scramble_teams_cooldown.GetFloat() );
|
||
|
}
|
||
|
|
||
|
engine->ServerCommand( "mp_scrambleteams 2;" );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CScrambleTeams::IsEnabled( void )
|
||
|
{
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
if ( TFGameRules()->IsMannVsMachineMode() )
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return sv_vote_issue_scramble_teams_allowed.GetBool();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CScrambleTeams::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
if( !IsEnabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_ISSUE_DISABLED;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( TFGameRules() && TFGameRules()->ShouldScrambleTeams() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_SCRAMBLE_IN_PROGRESS;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CScrambleTeams::GetDisplayString( void )
|
||
|
{
|
||
|
return "#TF_vote_scramble_teams";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CScrambleTeams::GetVotePassedString( void )
|
||
|
{
|
||
|
return "#TF_vote_passed_scramble_teams";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CScrambleTeams::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
if( !sv_vote_issue_scramble_teams_allowed.GetBool() )
|
||
|
return;
|
||
|
|
||
|
ListStandardNoArgCommand( pForWhom, GetTypeString() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: MvM Challenge Issue
|
||
|
//-----------------------------------------------------------------------------
|
||
|
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?" );
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CMannVsMachineChangeChallengeIssue::ExecuteCommand( void )
|
||
|
{
|
||
|
if ( Q_stricmp( m_szDetailsString, "normal" ) == 0 )
|
||
|
{
|
||
|
engine->ServerCommand( CFmtStr( "tf_mvm_popfile \"%s\";", STRING(gpGlobals->mapname) ) );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
engine->ServerCommand( CFmtStr( "tf_mvm_popfile \"%s\";", m_szDetailsString ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CMannVsMachineChangeChallengeIssue::CanTeamCallVote( int iTeam ) const
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: MvM-specific issue
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CMannVsMachineChangeChallengeIssue::IsEnabled( void )
|
||
|
{
|
||
|
// Only allow in MvM
|
||
|
if ( TFGameRules() && !TFGameRules()->IsMannVsMachineMode() )
|
||
|
return false;
|
||
|
|
||
|
// But prevent on MannUp (Valve) servers
|
||
|
CMatchInfo *pMatch = GTFGCClientSystem()->GetMatch();
|
||
|
if ( pMatch && pMatch->m_eMatchGroup == k_nMatchGroup_MvM_MannUp )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return sv_vote_issue_mvm_challenge_allowed.GetBool();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CMannVsMachineChangeChallengeIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
if( !IsEnabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_ISSUE_DISABLED;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( Q_strcmp( pszDetails, "" ) == 0 )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_MAP_NAME_REQUIRED;
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Make sure it's a valid pop
|
||
|
/*if ( !HaveExactMap( pszDetails ) )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_MAP_NOT_FOUND;
|
||
|
return false;
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CMannVsMachineChangeChallengeIssue::GetDisplayString( void )
|
||
|
{
|
||
|
return "#TF_vote_changechallenge";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CMannVsMachineChangeChallengeIssue::GetVotePassedString( void )
|
||
|
{
|
||
|
return "#TF_vote_passed_changechallenge";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CMannVsMachineChangeChallengeIssue::GetDetailsString( void )
|
||
|
{
|
||
|
return m_szDetailsString;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CMannVsMachineChangeChallengeIssue::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
if( !sv_vote_issue_mvm_challenge_allowed.GetBool() )
|
||
|
return;
|
||
|
|
||
|
char szBuffer[MAX_COMMAND_LENGTH];
|
||
|
Q_snprintf( szBuffer, MAX_COMMAND_LENGTH, "callvote %s <popfile>\n", GetTypeString() );
|
||
|
ClientPrint( pForWhom, HUD_PRINTCONSOLE, szBuffer );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CMannVsMachineChangeChallengeIssue::IsYesNoVote( void )
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
int CMannVsMachineChangeChallengeIssue::GetNumberVoteOptions( void )
|
||
|
{
|
||
|
// Vote on a specific map - Yes, No
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CEnableTemporaryHalloweenIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
#ifndef STAGING_ONLY
|
||
|
// Prevent concommand calling of this vote
|
||
|
if ( iEntIndex != DEDICATED_SERVER )
|
||
|
return false;
|
||
|
#endif // !STAGING_ONLY
|
||
|
|
||
|
if( TFGameRules()->IsHolidayActive( kHoliday_HalloweenOrFullMoon ) )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_MODIFICATION_ALREADY_ACTIVE;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEnableTemporaryHalloweenIssue::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
ListStandardNoArgCommand( pForWhom, GetTypeString() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
static void SendVoteResponseToGC( bool bVoteResponse )
|
||
|
{
|
||
|
if ( GTFGCClientSystem() )
|
||
|
{
|
||
|
// Tell the GC.
|
||
|
GCSDK::CProtoBufMsg<CMsgGC_GameServer_UseServerModificationItem_Response> msgResponse( k_EMsgGC_GameServer_UseServerModificationItem_Response );
|
||
|
msgResponse.Body().set_server_response_code( bVoteResponse ? CMsgGC_GameServer_UseServerModificationItem_Response::kServerModificationItemServerResponse_Accepted : CMsgGC_GameServer_UseServerModificationItem_Response::kServerModificationItemServerResponse_VoteFailed );
|
||
|
GTFGCClientSystem()->BSendMessage( msgResponse );
|
||
|
|
||
|
// Tell the players on the server.
|
||
|
if ( bVoteResponse )
|
||
|
{
|
||
|
TFGameRules()->BroadcastSound( 255, RandomInt( 0, 100 ) <= 10 ? "Halloween.MerasmusHalloweenModeRare" : "Halloween.MerasmusHalloweenModeCommon" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEnableTemporaryHalloweenIssue::ExecuteCommand( void )
|
||
|
{
|
||
|
SendVoteResponseToGC( true );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CEnableTemporaryHalloweenIssue::OnVoteFailed( int iEntityHoldingVote )
|
||
|
{
|
||
|
CBaseTFIssue::OnVoteFailed( iEntityHoldingVote );
|
||
|
|
||
|
SendVoteResponseToGC( false );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Enable/Disable mp_autoteambalance
|
||
|
//-----------------------------------------------------------------------------
|
||
|
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?" );
|
||
|
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)." );
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CTeamAutoBalanceIssue::GetTypeStringLocalized( void )
|
||
|
{
|
||
|
// Disabled
|
||
|
if ( !mp_autoteambalance.GetInt() )
|
||
|
{
|
||
|
return "#Vote_TeamAutoBalance_Enable";
|
||
|
}
|
||
|
|
||
|
return "#Vote_TeamAutoBalance_Disable";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTeamAutoBalanceIssue::ExecuteCommand( void )
|
||
|
{
|
||
|
if ( sv_vote_issue_autobalance_cooldown.GetInt() )
|
||
|
{
|
||
|
SetIssueCooldownDuration( sv_vote_issue_autobalance_cooldown.GetFloat() );
|
||
|
}
|
||
|
|
||
|
// Disable
|
||
|
if ( mp_autoteambalance.GetInt() )
|
||
|
{
|
||
|
engine->ServerCommand( "mp_autoteambalance 0;" );
|
||
|
}
|
||
|
// Enable
|
||
|
else
|
||
|
{
|
||
|
engine->ServerCommand( "mp_autoteambalance 1;" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CTeamAutoBalanceIssue::IsEnabled( void )
|
||
|
{
|
||
|
if ( !TFGameRules() || !TFGameRules()->IsDefaultGameMode() )
|
||
|
return false;
|
||
|
|
||
|
return sv_vote_issue_autobalance_allowed.GetBool();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CTeamAutoBalanceIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if ( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
if ( !IsEnabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_ISSUE_DISABLED;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CTeamAutoBalanceIssue::GetDisplayString( void )
|
||
|
{
|
||
|
// Disable
|
||
|
if ( mp_autoteambalance.GetInt() )
|
||
|
return "#TF_vote_autobalance_disable";
|
||
|
|
||
|
// Enable
|
||
|
return "#TF_vote_autobalance_enable";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CTeamAutoBalanceIssue::GetVotePassedString( void )
|
||
|
{
|
||
|
// Disable
|
||
|
if ( mp_autoteambalance.GetInt() )
|
||
|
return "#TF_vote_passed_autobalance_disable";
|
||
|
|
||
|
// Enable
|
||
|
return "#TF_vote_passed_autobalance_enable";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CTeamAutoBalanceIssue::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
if ( !sv_vote_issue_autobalance_allowed.GetBool() )
|
||
|
return;
|
||
|
|
||
|
ListStandardNoArgCommand( pForWhom, GetTypeString() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
float CTeamAutoBalanceIssue::GetQuorumRatio( void )
|
||
|
{
|
||
|
float flRatio = sv_vote_quorum_ratio.GetFloat();
|
||
|
|
||
|
// Disable
|
||
|
if ( mp_autoteambalance.GetInt() )
|
||
|
return flRatio;
|
||
|
|
||
|
// Enable
|
||
|
return Max( 0.1f, flRatio * 0.7f );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Enable/Disable tf_classlimit
|
||
|
//-----------------------------------------------------------------------------
|
||
|
#ifdef STAGING_ONLY
|
||
|
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?" );
|
||
|
#else
|
||
|
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?" );
|
||
|
#endif
|
||
|
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?" );
|
||
|
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 );
|
||
|
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 );
|
||
|
ConVar sv_vote_issue_classlimits_cooldown( "sv_vote_issue_classlimits_cooldown", "300", FCVAR_NONE, "Minimum time before another classlimits vote can occur (in seconds)." );
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CClassLimitsIssue::GetTypeStringLocalized( void )
|
||
|
{
|
||
|
// Disabled
|
||
|
if ( !tf_classlimit.GetInt() )
|
||
|
{
|
||
|
return "#Vote_ClassLimit_Enable";
|
||
|
}
|
||
|
|
||
|
return "#Vote_ClassLimit_Disable";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CClassLimitsIssue::ExecuteCommand( void )
|
||
|
{
|
||
|
if ( sv_vote_issue_classlimits_cooldown.GetInt() )
|
||
|
{
|
||
|
SetIssueCooldownDuration( sv_vote_issue_classlimits_cooldown.GetFloat() );
|
||
|
}
|
||
|
|
||
|
// Disable
|
||
|
if ( tf_classlimit.GetInt() )
|
||
|
{
|
||
|
engine->ServerCommand( "tf_classlimit 0;" );
|
||
|
}
|
||
|
// Enable
|
||
|
else
|
||
|
{
|
||
|
int nLimit = ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) ? sv_vote_issue_classlimits_max_mvm.GetInt() : sv_vote_issue_classlimits_max.GetInt();
|
||
|
engine->ServerCommand( CFmtStr( "tf_classlimit %i;", nLimit ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CClassLimitsIssue::IsEnabled( void )
|
||
|
{
|
||
|
if ( TFGameRules() )
|
||
|
{
|
||
|
// Manages class limits already
|
||
|
if ( TFGameRules()->IsInTournamentMode() )
|
||
|
return false;
|
||
|
|
||
|
// Manages class limits already
|
||
|
if ( TFGameRules()->IsInHighlanderMode() )
|
||
|
return false;
|
||
|
|
||
|
if ( TFGameRules()->IsMannVsMachineMode() )
|
||
|
return sv_vote_issue_classlimits_allowed_mvm.GetBool();
|
||
|
}
|
||
|
|
||
|
return sv_vote_issue_classlimits_allowed.GetBool();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CClassLimitsIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if ( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
if ( !IsEnabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_ISSUE_DISABLED;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CClassLimitsIssue::GetDisplayString( void )
|
||
|
{
|
||
|
// Disable
|
||
|
if ( tf_classlimit.GetInt() )
|
||
|
return "#TF_vote_classlimits_disable";
|
||
|
|
||
|
// Enable
|
||
|
return "#TF_vote_classlimits_enable";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CClassLimitsIssue::GetVotePassedString( void )
|
||
|
{
|
||
|
// Disable
|
||
|
if ( tf_classlimit.GetInt() )
|
||
|
return "#TF_vote_passed_classlimits_disable";
|
||
|
|
||
|
// Enable
|
||
|
return "#TF_vote_passed_classlimits_enable";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CClassLimitsIssue::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && !sv_vote_issue_classlimits_allowed_mvm.GetBool() )
|
||
|
return;
|
||
|
|
||
|
if ( !sv_vote_issue_classlimits_allowed.GetBool() )
|
||
|
return;
|
||
|
|
||
|
ListStandardNoArgCommand( pForWhom, GetTypeString() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CClassLimitsIssue::GetDetailsString( void )
|
||
|
{
|
||
|
int nLimit = ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) ? sv_vote_issue_classlimits_max_mvm.GetInt() : sv_vote_issue_classlimits_max.GetInt();
|
||
|
m_sRetString = CFmtStr( "%i", nLimit );
|
||
|
return m_sRetString.String();
|
||
|
}
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose: Pause Game
|
||
|
//-----------------------------------------------------------------------------
|
||
|
ConVar sv_vote_issue_pause_game_allowed( "sv_vote_issue_pause_game_allowed", "0", FCVAR_HIDDEN, "Can players call votes to pause the game?" );
|
||
|
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)." );
|
||
|
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)." );
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CPauseGameIssue::ExecuteCommand( void )
|
||
|
{
|
||
|
if ( sv_vote_issue_pause_game_cooldown.GetInt() )
|
||
|
{
|
||
|
SetIssueCooldownDuration( sv_vote_issue_pause_game_cooldown.GetFloat() );
|
||
|
}
|
||
|
|
||
|
engine->SetPausedForced( true, sv_vote_issue_pause_game_timer.GetFloat() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CPauseGameIssue::IsEnabled( void )
|
||
|
{
|
||
|
if ( engine->IsPaused() )
|
||
|
return false;
|
||
|
|
||
|
return sv_vote_issue_pause_game_allowed.GetBool();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CPauseGameIssue::CanCallVote( int iEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime )
|
||
|
{
|
||
|
if ( !CBaseTFIssue::CanCallVote( iEntIndex, pszDetails, nFailCode, nTime ) )
|
||
|
return false;
|
||
|
|
||
|
if ( !IsEnabled() )
|
||
|
{
|
||
|
nFailCode = VOTE_FAILED_ISSUE_DISABLED;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CPauseGameIssue::GetDisplayString( void )
|
||
|
{
|
||
|
return "#TF_vote_pause_game";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CPauseGameIssue::GetVotePassedString( void )
|
||
|
{
|
||
|
return "#TF_vote_passed_pause_game";
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CPauseGameIssue::ListIssueDetails( CBasePlayer *pForWhom )
|
||
|
{
|
||
|
if ( !sv_vote_issue_pause_game_allowed.GetBool() )
|
||
|
return;
|
||
|
|
||
|
ListStandardNoArgCommand( pForWhom, GetTypeString() );
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
const char *CPauseGameIssue::GetDetailsString( void )
|
||
|
{
|
||
|
m_sRetString = CFmtStr( "%i", sv_vote_issue_pause_game_timer.GetInt() );
|
||
|
return (m_sRetString.String());
|
||
|
}
|