Difference between revisions of "Left 4 Voting 2"

From AlliedModders Wiki
Jump to: navigation, search
m (Example voting plugin: Update syntax, highlighting, methodmap, and formatting)
(Example plugin corrections: added timer for vote end(it was endless if someone doesn't vote),а fixed number of voters replaced via max players number, fixed bug when 1 player could vote unlimited number of times, corrected VoteFailed user msg to VoteFail)
Line 104: Line 104:
 
int g_iYesVotes;
 
int g_iYesVotes;
 
int g_iNoVotes;
 
int g_iNoVotes;
 +
int g_iPlayersCount;
 +
bool VoteInProgress;
 +
bool CanPlayerVote[MAXPLAYERS + 1];
  
#define MAX_VOTES 4
 
  
 
public void OnPluginStart()
 
public void OnPluginStart()
Line 123: Line 125:
 
bf.WriteString("Server");
 
bf.WriteString("Server");
 
EndMessage();
 
EndMessage();
+
 
 
g_iYesVotes = 0;
 
g_iYesVotes = 0;
 
g_iNoVotes = 0;
 
g_iNoVotes = 0;
 +
g_iPlayersCount = 0;
 +
VoteInProgress = true;
 +
 +
for(int i = 1; i <= MaxClients; i++)
 +
{
 +
if(IsValidClient(i) && !IsFakeClient(i))
 +
{
 +
CanPlayerVote[i] = true;
 +
g_iPlayersCount ++;
 +
}
 +
}
  
 
UpdateVotes();
 
UpdateVotes();
 +
CreateTimer(10.0, Timer_VoteCheck, TIMER_FLAG_NO_MAPCHANGE);
 
 
 
return Plugin_Handled;
 
return Plugin_Handled;
 +
}
 +
public Action Timer_VoteCheck(Handle timer)
 +
{
 +
if(VoteInProgress)
 +
{
 +
VoteInProgress = false;
 +
UpdateVotes();
 +
}
 
}
 
}
  
Line 137: Line 159:
 
event.SetInt("yesVotes", g_iYesVotes);
 
event.SetInt("yesVotes", g_iYesVotes);
 
event.SetInt("noVotes", g_iNoVotes);
 
event.SetInt("noVotes", g_iNoVotes);
event.SetInt("potentialVotes", MAX_VOTES);
+
event.SetInt("potentialVotes", g_iPlayersCount);
 
event.Fire();
 
event.Fire();
 
 
if (g_iYesVotes + g_iNoVotes == MAX_VOTES)
+
if ((g_iYesVotes + g_iNoVotes == g_iPlayersCount) || !VoteInProgress)
 
{
 
{
 
PrintToServer("voting complete!");
 
PrintToServer("voting complete!");
 +
 +
for(int i = 1; i <= MaxClients; i++)
 +
{
 +
if(IsValidClient(i) && !IsFakeClient(i))
 +
{
 +
CanPlayerVote[i] = false;
 +
}
 +
}
 +
 +
VoteInProgress = false;
  
 
if (g_iYesVotes > g_iNoVotes)
 
if (g_iYesVotes > g_iNoVotes)
Line 154: Line 186:
 
else
 
else
 
{
 
{
BfWrite bf = UserMessageToBfWrite(StartMessageAll("VoteFailed"));
+
BfWrite bf = UserMessageToBfWrite(StartMessageAll("VoteFail"));
 
bf.WriteByte(L4D2_TEAM_ALL);
 
bf.WriteByte(L4D2_TEAM_ALL);
 
EndMessage();
 
EndMessage();
Line 163: Line 195:
 
public Action vote(int client, int args)
 
public Action vote(int client, int args)
 
{
 
{
char arg[8];
+
if(VoteInProgress && CanPlayerVote[client] == true)
GetCmdArg(1, arg, sizeof arg);
+
{
 +
char arg[8];
 +
GetCmdArg(1, arg, sizeof arg);
  
PrintToServer("Got vote %s from %i", arg, client);
+
PrintToServer("Got vote %s from %i", arg, client);
  
if (strcmp(arg, "Yes", true) == 0)
+
if (strcmp(arg, "Yes", true) == 0)
{
+
{
g_iYesVotes++;
+
g_iYesVotes++;
}
+
}
else if (strcmp(arg, "No", true) == 0)
+
else if (strcmp(arg, "No", true) == 0)
{
+
{
g_iNoVotes++;
+
g_iNoVotes++;
 +
}
 +
 +
UpdateVotes();
 
}
 
}
 
UpdateVotes();
 
  
 
return Plugin_Continue;
 
return Plugin_Continue;
}</sourcepawn>
+
}
 +
 
 +
stock bool IsValidClient(int client){
 +
return (client > 0 && client <= MaxClients && IsClientInGame(client));
 +
}
 +
</sourcepawn>
  
 
==See Also==
 
==See Also==

Revision as of 11:52, 15 November 2021

Left 4 Dead 2 has a VGUI voting system controller by a bunch of Events and UserMessages. You can use either a string from the resource file, or L4D_TargetID_Player which will let you create any vote you want.

How voting works

  1. A client issues a callvote with the vote type and argument.
  2. The VoteStart User Message is sent.
  3. Clients use the "Vote" command to register their votes ("Yes" or "No"), after which the server sends a VoteRegistered UserMessage to that player to acknowledge their vote. A vote_changed event is sent to all players with updated numbers.
  4. When the vote is complete, the server sends either a VotePass or VoteFail UserMessage.

Server Entity

The server should update this as appropriate. Unfortunately, the valid values for m_iActiveIssueIndex is unknown.

Name: vote_controller (CVoteController)
Structure:
int m_activeIssueIndex Number of the active issue
int m_onlyTeamToVote Corresponds to VoteStart's team argument.
int m_votesYes Current Yes votes
int m_votesNo Current No votes
int m_potentialVotes Number of players eligible to vote


Console Commands

Vote

Note: This command is only valid when a vote is ongoing.

Name: Vote
Structure:
string option "Yes" or "No"


User Messages

Note: These user messages use unsigned bytes for the team number. Since Valve represents no team as -1, no team is instead represented as its two's complement, 255.

VoteStart

Note: Sent to all players in the vote group, corresponding with teams. The default implementation also sends it to bots.

Name: VoteStart
Structure:
byte team Team index or 255 for all
byte initiator Client index (NOT USERID) of person who started the vote, or 99 for the server.
string issue Vote issue translation string
string param1 Vote issue text
string initiatorName Name of person who started the vote


Values for "issue" and "param1" in standard votes

Vote type issue param1
Kick #L4D_vote_kick_player Nickname of the person to be kicked without "#"
ReturnToLobby #L4D_vote_return_to_lobby
ChangeCampaign #L4D_vote_mission_change #L4D360UI_CampaignName_C5
RestartChapter #L4D_vote_passed_versus_level_restart
ChangeDifficulty (easy) #L4D_vote_change_difficulty #L4D_DifficultyEasy
ChangeDifficulty (hard) #L4D_vote_change_difficulty #L4D_DifficultyHard
Alltalk On #L4D_vote_alltalk_change #L4D_vote_alltalk_enable
Alltalk Off #L4D_vote_alltalk_change #L4D_vote_alltalk_disable
  • Campaigns aren't specified by map name but by the index the campaign's entry has in the game menus. Example: "5" specifies the campaign "The Parish"

VoteRegistered

Note: Only sent to player who voted

Name: VoteRegistered
Structure:
byte vote 0 for No, 1 for Yes


VotePass

Note: Sent to all players after a vote passes.

Name: VotePass
Structure:
byte team Team index or 255 for all
string details Vote success translation string
string param1 Vote winner


VoteFail

Note: Sent to all players after a vote fails.

Name: VoteFail
Structure:
byte team Team index or 255 for all


Events

Note: team is -1 when sent to all players)

vote_changed

Name: vote_changed
Structure:
byte yesVotes
byte noVotes
byte potentialVotes


Example voting plugin

This is a basic plugin that starts a vote, "Is gaben fat?". It does not ensure the same client does not vote multiple times, nor does it actually kick the user.

#include <sourcemod>
 
#define L4D2_TEAM_ALL -1
 
int g_iYesVotes;
int g_iNoVotes;
int g_iPlayersCount;
bool VoteInProgress;
bool CanPlayerVote[MAXPLAYERS + 1];
 
 
public void OnPluginStart()
{
	RegConsoleCmd("testvote", Callvote_Handler);
	RegConsoleCmd("Vote", vote);
}
 
public Action Callvote_Handler(int client, int args)
{
	BfWrite bf = UserMessageToBfWrite(StartMessageAll("VoteStart", USERMSG_RELIABLE));
 
	bf.WriteByte(L4D2_TEAM_ALL);
	bf.WriteByte(0);
	bf.WriteString("#L4D_TargetID_Player");
	bf.WriteString("Is gaben fat?");
	bf.WriteString("Server");
	EndMessage();
 
	g_iYesVotes = 0;
	g_iNoVotes = 0;
	g_iPlayersCount = 0;
	VoteInProgress = true;
 
	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsValidClient(i) && !IsFakeClient(i))
		{
			CanPlayerVote[i] = true;
			g_iPlayersCount ++;
		}
	}
 
	UpdateVotes();
	CreateTimer(10.0, Timer_VoteCheck, TIMER_FLAG_NO_MAPCHANGE);
 
	return Plugin_Handled;
}
public Action Timer_VoteCheck(Handle timer)
{
	if(VoteInProgress)
	{
		VoteInProgress = false;
		UpdateVotes();
	}
}
 
public void UpdateVotes()
{
	Event event = CreateEvent("vote_changed");
	event.SetInt("yesVotes", g_iYesVotes);
	event.SetInt("noVotes", g_iNoVotes);
	event.SetInt("potentialVotes", g_iPlayersCount);
	event.Fire();
 
	if ((g_iYesVotes + g_iNoVotes == g_iPlayersCount) || !VoteInProgress)
	{
		PrintToServer("voting complete!");
 
		for(int i = 1; i <= MaxClients; i++)
		{
			if(IsValidClient(i) && !IsFakeClient(i))
			{
				CanPlayerVote[i] = false;
			}
		}
 
		VoteInProgress = false;
 
		if (g_iYesVotes > g_iNoVotes)
		{
			BfWrite bf = UserMessageToBfWrite(StartMessageAll("VotePass"));
			bf.WriteByte(L4D2_TEAM_ALL);
			bf.WriteString("#L4D_TargetID_Player");
			bf.WriteString("Gaben is fat");
			EndMessage();
		}
		else
		{
			BfWrite bf = UserMessageToBfWrite(StartMessageAll("VoteFail"));
			bf.WriteByte(L4D2_TEAM_ALL);
			EndMessage();
		}
	}
}
 
public Action vote(int client, int args)
{
	if(VoteInProgress && CanPlayerVote[client] == true)
	{
		char arg[8];
		GetCmdArg(1, arg, sizeof arg);
 
		PrintToServer("Got vote %s from %i", arg, client);
 
		if (strcmp(arg, "Yes", true) == 0)
		{
			g_iYesVotes++;
		}
		else if (strcmp(arg, "No", true) == 0)
		{
			g_iNoVotes++;
		}
 
		UpdateVotes();
	}
 
	return Plugin_Continue;
}
 
stock bool IsValidClient(int client){
	return (client > 0 && client <= MaxClients && IsClientInGame(client));
}

See Also