Files
sm-plugins/Leader3/scripting/leader3/vote.sp
T
2026-10-22 16:17:56 +01:00

137 lines
3.4 KiB
SourcePawn

//----------------------------------------------------------------------------------------------------
// Leader3 vote: !voteleader / !vl <name>. Passes at sm_leader_vote_ratio of eligible voters.
// Eligible = in-game, not a bot, not AFK (AFKManager, when loaded).
//----------------------------------------------------------------------------------------------------
int g_iVoteCount[MAXPLAYERS + 1]; // votes received, per candidate
int g_iVotedForSerial[MAXPLAYERS + 1]; // per voter: client serial of their candidate
public Action Cmd_VoteLeader(int client, int args)
{
if (client == 0)
{
ReplyToCommand(client, "%s Cannot use this from the console.", PREFIX_PLAIN);
return Plugin_Handled;
}
if (!g_cvAllowVotes.BoolValue)
{
ReplyToCommand(client, "%s Voting for leader is disabled.", PREFIX_PLAIN);
return Plugin_Handled;
}
if (args < 1)
{
ReplyToCommand(client, "%s Usage: sm_voteleader <player>", PREFIX_PLAIN);
return Plugin_Handled;
}
char arg[65];
GetCmdArg(1, arg, sizeof(arg));
int target = FindTarget(client, arg, true, false);
if (target == -1)
return Plugin_Handled;
if (g_bIsLeader[target])
{
ReplyToCommand(client, "%s %N is already a leader!", PREFIX_PLAIN, target);
return Plugin_Handled;
}
if (g_alLeaders.Length >= g_cvMaxLeaders.IntValue)
{
ReplyToCommand(client, "%s The maximum of %d leaders is already reached.", PREFIX_PLAIN, g_cvMaxLeaders.IntValue);
return Plugin_Handled;
}
int previous = GetClientFromSerial(g_iVotedForSerial[client]);
if (previous == target)
{
ReplyToCommand(client, "%s You have already voted for this player!", PREFIX_PLAIN);
return Plugin_Handled;
}
if (previous != 0)
g_iVoteCount[previous]--;
g_iVoteCount[target]++;
g_iVotedForSerial[client] = GetClientSerial(target);
PrintToChatAll("%s\x04%N\x01 voted for \x04%N\x01 to become a leader (\x04%d/%d\x01 votes).",
PREFIX, client, target, g_iVoteCount[target], Vote_Needed());
Vote_Check(target);
return Plugin_Handled;
}
int Vote_EligibleCount()
{
int afkTime = g_cvVoteAfkTime.IntValue;
int count = 0;
for (int i = 1; i <= MaxClients; i++)
{
if (!IsClientInGame(i) || IsFakeClient(i))
continue;
if (g_bAFKManager && afkTime > 0 && GetClientIdleTime(i) >= afkTime)
continue;
count++;
}
return count;
}
int Vote_Needed()
{
int needed = RoundToCeil(float(Vote_EligibleCount()) * g_cvVoteRatio.FloatValue);
return needed < 2 ? 2 : needed;
}
void Vote_Check(int candidate)
{
if (!IsValidClient(candidate) || g_iVoteCount[candidate] <= 0)
return;
if (g_iVoteCount[candidate] < Vote_Needed())
return;
Vote_ClearCandidate(candidate);
PrintToChatAll("%s\x04%N\x01 has been voted to become a leader!", PREFIX, candidate);
if (AddLeader(candidate))
ShowLeaderMenu(candidate);
}
void Vote_ClearCandidate(int candidate)
{
g_iVoteCount[candidate] = 0;
int serial = GetClientSerial(candidate);
for (int i = 1; i <= MaxClients; i++)
{
if (g_iVotedForSerial[i] == serial)
g_iVotedForSerial[i] = 0;
}
}
// A shrinking voter pool can push a pending candidate over the threshold.
void Vote_RecheckAll()
{
for (int i = 1; i <= MaxClients; i++)
{
if (g_iVoteCount[i] > 0)
Vote_Check(i);
}
}
void Vote_OnClientDisconnect(int client)
{
int previous = GetClientFromSerial(g_iVotedForSerial[client]);
if (previous != 0)
g_iVoteCount[previous]--;
g_iVotedForSerial[client] = 0;
if (g_iVoteCount[client] > 0)
Vote_ClearCandidate(client);
Vote_RecheckAll();
}