aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server/vote_controller.h
blob: a3f82ff698c5aae45924d21edfde0166645ec3f5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Player-driven Voting System for Multiplayer Source games (currently implemented for TF2)
//
// $NoKeywords: $
//=============================================================================//

#ifndef VOTE_CONTROLLER_H
#define VOTE_CONTROLLER_H

#ifdef _WIN32
#pragma once
#endif

#include "shareddefs.h"

#define MAX_COMMAND_LENGTH 64
#define MAX_CREATE_ERROR_STRING 96

class CBaseIssue	// Base class concept for vote issues (i.e. Kick Player).  Created per level-load and destroyed by CVoteController's dtor.
{
public:
	CBaseIssue( const char *typeString );
	virtual				 ~CBaseIssue();
	const char			*GetTypeString( void );						// Connection between console command and specific type of issue
	virtual const char	*GetTypeStringLocalized( void ) { return ""; }	// When empty, the client uses the classname string and prepends "#Vote_"
	virtual const char	*GetDetailsString( void );
	virtual void		SetIssueDetails( const char *pszDetails );	// We need to know the details part of the con command for later
	virtual void		OnVoteFailed( int iEntityHoldingVote );		// The moment the vote fails, also has some time for feedback before the window goes away
	virtual void		OnVoteStarted( void ) {}					// Called as soon as the vote starts
	virtual bool		IsEnabled( void ) { return false; }			// Query the issue to see if it's enabled
	virtual bool		CanTeamCallVote( int iTeam ) const;			// Can someone on the given team call this vote?
	virtual bool		CanCallVote( int nEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime ); // Can this guy hold a vote on this issue?
	virtual bool		IsTeamRestrictedVote( void );				// Restrict access and visibility of this vote to a specific team?
	virtual const char *GetDisplayString( void ) = 0;				// The string that will be passed to the client for display
	virtual void		ExecuteCommand( void ) = 0;					// Where the magic happens.  Do your thing.
	virtual void		ListIssueDetails( CBasePlayer *pForWhom ) = 0;	// Someone would like to know all your valid details
	virtual const char *GetVotePassedString( void );				// Get the string an issue would like to display when it passes.
	virtual int			CountPotentialVoters( void );
	virtual int			GetNumberVoteOptions( void );				// How many choices this vote will have.  i.e. Returns 2 on a Yes/No issue (the default).
	virtual bool		IsYesNoVote( void );
	virtual void		SetYesNoVoteCount( int iNumYesVotes, int iNumNoVotes, int iNumPotentialVotes );
	virtual bool		GetVoteOptions( CUtlVector <const char*> &vecNames );	// We use this to generate options for voting
	virtual bool		BRecordVoteFailureEventForEntity( int iVoteCallingEntityIndex ) const { return iVoteCallingEntityIndex != DEDICATED_SERVER; }
	void				SetIssueCooldownDuration( float flDuration ) { m_flNextCallTime = gpGlobals->curtime + flDuration; }	// The issue can not be raised again for this period of time (in seconds)
	virtual float		GetQuorumRatio( void );						// Each issue can decide the required ratio of voted-vs-abstained

	CHandle< CBasePlayer > m_hPlayerTarget;							// If the target of the issue is a player, we should store them here

protected:
	static void			ListStandardNoArgCommand( CBasePlayer *forWhom, const char *issueString );		// List a Yes vote command

	struct FailedVote
	{
		char	szFailedVoteParameter[MAX_VOTE_DETAILS_LENGTH];
		float	flLockoutTime;					
	};

	CUtlVector< FailedVote* > m_FailedVotes;
	char m_szTypeString[MAX_COMMAND_LENGTH];
	char m_szDetailsString[MAX_VOTE_DETAILS_LENGTH];
	int m_iNumYesVotes;
	int m_iNumNoVotes;
	int m_iNumPotentialVotes;
	float m_flNextCallTime;
};

class CVoteController : public CBaseEntity
{
	DECLARE_CLASS( CVoteController, CBaseEntity );
	
public:
	DECLARE_SERVERCLASS();
	DECLARE_DATADESC();

	virtual ~CVoteController();

	enum TryCastVoteResult
	{
		CAST_OK,
		CAST_FAIL_SERVER_DISABLE,
		CAST_FAIL_NO_ACTIVE_ISSUE,
		CAST_FAIL_TEAM_RESTRICTED,
		CAST_FAIL_NO_CHANGES,
		CAST_FAIL_DUPLICATE,
		CAST_FAIL_VOTE_CLOSED,
		CAST_FAIL_SYSTEM_ERROR
	};

	virtual void	Spawn( void );
	virtual int		UpdateTransmitState( void );
	virtual bool	IsVoteSystemEnabled( void );

	bool			SetupVote( int iEntIndex );	// This creates a list of issues for the UI
	bool			CreateVote( int iEntIndex, const char *pszTypeString, const char *pszDetailString );	// This is what the UI passes in
	TryCastVoteResult TryCastVote( int iEntIndex, const char *pszVoteString );
	void			RegisterIssue( CBaseIssue *pNewIssue );
	void			ListIssues( CBasePlayer *pForWhom );
	bool			IsValidVoter( CBasePlayer *pWhom );
	bool			CanTeamCastVote( int iTeam ) const;
	void			SendVoteCreationFailedMessage( vote_create_failed_t nReason, CBasePlayer *pVoteCaller, int nTime = -1 );
	void			SendVoteFailedToPassMessage( vote_create_failed_t nReason );
	void			VoteChoice_Increment( int nVoteChoice );
	void			VoteChoice_Decrement( int nVoteChoice );
	int				GetVoteIssueIndexWithHighestCount( void );
	void			TrackVoteCaller( CBasePlayer *pPlayer );
	bool			CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown, vote_create_failed_t &nErrorCode );
	bool			IsVoteActive( void ) { return m_iActiveIssueIndex != INVALID_ISSUE; }
	int				GetNumVotesCast( void );

	void			AddPlayerToKickWatchList( CSteamID steamID, float flDuration );		// Band-aid until we figure out how player's avoid kick votes
	void			AddPlayerToNameLockedList( CSteamID steamID, float flDuration, int nUserID );
	bool			IsPlayerBeingKicked( CBasePlayer *pPlayer );

protected:
	void			ResetData( void );
	void			VoteControllerThink( void );
	void			CheckForEarlyVoteClose( void ); // If everyone has voted (and changing votes is not allowed) then end early

	CNetworkVar( int, m_iActiveIssueIndex );					// Type of thing being voted on
	CNetworkVar( int, m_iOnlyTeamToVote );						// If an Ally restricted vote, the team number that is allowed to vote
	CNetworkArray( int, m_nVoteOptionCount, MAX_VOTE_OPTIONS );	// Vote options counter
	CNetworkVar( int, m_nPotentialVotes );						// How many votes could come in, so we can close ballot early
	CNetworkVar( bool, m_bIsYesNoVote );						// Is the current issue Yes/No?
	CountdownTimer	m_acceptingVotesTimer;						// How long from vote start until we count the ballots
	CountdownTimer	m_executeCommandTimer;						// How long after end of vote time until we execute a passed vote
	CountdownTimer	m_resetVoteTimer;							// when the current vote will end 
	int				m_nVotesCast[MAX_PLAYERS + 1];				// arrays are zero-based and player indices are one-based
	int				m_iEntityHoldingVote;
	
	CUtlVector <CBaseIssue *>	m_potentialIssues;
	CUtlVector <const char *>	m_VoteOptions;
	CUtlMap <uint64, float>		m_VoteCallers;					// History of SteamIDs that have tried to call votes.

	friend class CVoteControllerSystem;
};

extern CVoteController *g_voteController;

#endif // VOTE_CONTROLLER_H