1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/plugins/basevotes.sp Sun Mar 30 07:00:22 2008 +0000
1.3 @@ -0,0 +1,430 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod Basic Votes Plugin
1.8 + * Implements basic vote commands.
1.9 + *
1.10 + * SourceMod (C)2004-2008 AlliedModders LLC. All rights reserved.
1.11 + * =============================================================================
1.12 + *
1.13 + * This program is free software; you can redistribute it and/or modify it under
1.14 + * the terms of the GNU General Public License, version 3.0, as published by the
1.15 + * Free Software Foundation.
1.16 + *
1.17 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.18 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.19 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.20 + * details.
1.21 + *
1.22 + * You should have received a copy of the GNU General Public License along with
1.23 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.24 + *
1.25 + * As a special exception, AlliedModders LLC gives you permission to link the
1.26 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.27 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.28 + * by the Valve Corporation. You must obey the GNU General Public License in
1.29 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.30 + * this exception to all derivative works. AlliedModders LLC defines further
1.31 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.32 + * or <http://www.sourcemod.net/license.php>.
1.33 + *
1.34 + * Version: $Id$
1.35 + */
1.36 +
1.37 +#pragma semicolon 1
1.38 +
1.39 +#include <sourcemod>
1.40 +#undef REQUIRE_PLUGIN
1.41 +#include <adminmenu>
1.42 +
1.43 +public Plugin:myinfo =
1.44 +{
1.45 + name = "Basic Votes",
1.46 + author = "AlliedModders LLC",
1.47 + description = "Basic Vote Commands",
1.48 + version = SOURCEMOD_VERSION,
1.49 + url = "http://www.sourcemod.net/"
1.50 +};
1.51 +
1.52 +#define VOTE_NO "###no###"
1.53 +#define VOTE_YES "###yes###"
1.54 +
1.55 +new Handle:g_hVoteMenu = INVALID_HANDLE;
1.56 +
1.57 +new Handle:g_Cvar_Limits[3] = {INVALID_HANDLE, ...};
1.58 +//new Handle:g_Cvar_VoteSay = INVALID_HANDLE;
1.59 +
1.60 +enum voteType
1.61 +{
1.62 + map,
1.63 + kick,
1.64 + ban,
1.65 + question
1.66 +}
1.67 +
1.68 +new voteType:g_voteType = voteType:question;
1.69 +
1.70 +// Menu API does not provide us with a way to pass multiple peices of data with a single
1.71 +// choice, so some globals are used to hold stuff.
1.72 +//
1.73 +#define VOTE_CLIENTID 0
1.74 +#define VOTE_USERID 1
1.75 +new g_voteClient[2]; /* Holds the target's client id and user id */
1.76 +
1.77 +#define VOTE_NAME 0
1.78 +#define VOTE_AUTHID 1
1.79 +#define VOTE_IP 2
1.80 +new String:g_voteInfo[3][65]; /* Holds the target's name, authid, and IP */
1.81 +
1.82 +new String:g_voteArg[256]; /* Used to hold ban/kick reasons or vote questions */
1.83 +
1.84 +
1.85 +new Handle:hTopMenu = INVALID_HANDLE;
1.86 +
1.87 +#include "basevotes/votekick.sp"
1.88 +#include "basevotes/voteban.sp"
1.89 +#include "basevotes/votemap.sp"
1.90 +
1.91 +public OnPluginStart()
1.92 +{
1.93 + LoadTranslations("common.phrases");
1.94 + LoadTranslations("basevotes.phrases");
1.95 + LoadTranslations("plugin.basecommands");
1.96 + LoadTranslations("basebans.phrases");
1.97 +
1.98 + RegAdminCmd("sm_votemap", Command_Votemap, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP, "sm_votemap <mapname> [mapname2] ... [mapname5] ");
1.99 + RegAdminCmd("sm_votekick", Command_Votekick, ADMFLAG_VOTE|ADMFLAG_KICK, "sm_votekick <player> [reason]");
1.100 + RegAdminCmd("sm_voteban", Command_Voteban, ADMFLAG_VOTE|ADMFLAG_BAN, "sm_voteban <player> [reason]");
1.101 + RegAdminCmd("sm_vote", Command_Vote, ADMFLAG_VOTE, "sm_vote <question> [Answer1] [Answer2] ... [Answer5]");
1.102 +
1.103 + /*
1.104 + g_Cvar_Show = FindConVar("sm_vote_show");
1.105 + if (g_Cvar_Show == INVALID_HANDLE)
1.106 + {
1.107 + g_Cvar_Show = CreateConVar("sm_vote_show", "1", "Show player's votes? Default on.", 0, true, 0.0, true, 1.0);
1.108 + }
1.109 + */
1.110 +
1.111 + g_Cvar_Limits[0] = CreateConVar("sm_vote_map", "0.60", "percent required for successful map vote.", 0, true, 0.05, true, 1.0);
1.112 + g_Cvar_Limits[1] = CreateConVar("sm_vote_kick", "0.60", "percent required for successful kick vote.", 0, true, 0.05, true, 1.0);
1.113 + g_Cvar_Limits[2] = CreateConVar("sm_vote_ban", "0.60", "percent required for successful ban vote.", 0, true, 0.05, true, 1.0);
1.114 +
1.115 + /* Account for late loading */
1.116 + new Handle:topmenu;
1.117 + if (LibraryExists("adminmenu") && ((topmenu = GetAdminTopMenu()) != INVALID_HANDLE))
1.118 + {
1.119 + OnAdminMenuReady(topmenu);
1.120 + }
1.121 +
1.122 + g_SelectedMaps = CreateArray(33);
1.123 +
1.124 + g_MapList = CreateMenu(MenuHandler_Map, MenuAction_DrawItem);
1.125 + SetMenuTitle(g_MapList, "Please select a map");
1.126 + SetMenuExitBackButton(g_MapList, true);
1.127 +
1.128 + decl String:mapListPath[PLATFORM_MAX_PATH];
1.129 + BuildPath(Path_SM, mapListPath, sizeof(mapListPath), "configs/adminmenu_maplist.ini");
1.130 + SetMapListCompatBind("sm_votemap menu", mapListPath);
1.131 +}
1.132 +
1.133 +public OnConfigsExecuted()
1.134 +{
1.135 + g_mapCount = LoadMapList(g_MapList);
1.136 +}
1.137 +
1.138 +public OnAdminMenuReady(Handle:topmenu)
1.139 +{
1.140 + /* Block us from being called twice */
1.141 + if (topmenu == hTopMenu)
1.142 + {
1.143 + return;
1.144 + }
1.145 +
1.146 + /* Save the Handle */
1.147 + hTopMenu = topmenu;
1.148 +
1.149 + /* Build the "Voting Commands" category */
1.150 + new TopMenuObject:voting_commands = FindTopMenuCategory(hTopMenu, ADMINMENU_VOTINGCOMMANDS);
1.151 +
1.152 + if (voting_commands != INVALID_TOPMENUOBJECT)
1.153 + {
1.154 + AddToTopMenu(hTopMenu,
1.155 + "sm_votekick",
1.156 + TopMenuObject_Item,
1.157 + AdminMenu_VoteKick,
1.158 + voting_commands,
1.159 + "sm_votekick",
1.160 + ADMFLAG_VOTE|ADMFLAG_KICK);
1.161 +
1.162 + AddToTopMenu(hTopMenu,
1.163 + "sm_voteban",
1.164 + TopMenuObject_Item,
1.165 + AdminMenu_VoteBan,
1.166 + voting_commands,
1.167 + "sm_voteban",
1.168 + ADMFLAG_VOTE|ADMFLAG_BAN);
1.169 +
1.170 + AddToTopMenu(hTopMenu,
1.171 + "sm_votemap",
1.172 + TopMenuObject_Item,
1.173 + AdminMenu_VoteMap,
1.174 + voting_commands,
1.175 + "sm_votemap",
1.176 + ADMFLAG_VOTE|ADMFLAG_CHANGEMAP);
1.177 + }
1.178 +}
1.179 +
1.180 +public Action:Command_Vote(client, args)
1.181 +{
1.182 + if (args < 1)
1.183 + {
1.184 + ReplyToCommand(client, "[SM] Usage: sm_vote <question> [Answer1] [Answer2] ... [Answer5]");
1.185 + return Plugin_Handled;
1.186 + }
1.187 +
1.188 + if (IsVoteInProgress())
1.189 + {
1.190 + ReplyToCommand(client, "[SM] %t", "Vote in Progress");
1.191 + return Plugin_Handled;
1.192 + }
1.193 +
1.194 + if (!TestVoteDelay(client))
1.195 + {
1.196 + return Plugin_Handled;
1.197 + }
1.198 +
1.199 + decl String:text[256];
1.200 + GetCmdArgString(text, sizeof(text));
1.201 +
1.202 + decl String:answers[5][64];
1.203 + new answerCount;
1.204 + new len = BreakString(text, g_voteArg, sizeof(g_voteArg));
1.205 + new pos = len;
1.206 +
1.207 + while (args > 1 && pos != -1 && answerCount < 5)
1.208 + {
1.209 + pos = BreakString(text[len], answers[answerCount], sizeof(answers[]));
1.210 + answerCount++;
1.211 +
1.212 + if (pos != -1)
1.213 + {
1.214 + len += pos;
1.215 + }
1.216 + }
1.217 +
1.218 + LogAction(client, -1, "\"%L\" initiated a generic vote.", client);
1.219 + ShowActivity2(client, "[SM] ", "%t", "Initiate Vote", g_voteArg);
1.220 +
1.221 + g_voteType = voteType:question;
1.222 +
1.223 + g_hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
1.224 + SetMenuTitle(g_hVoteMenu, "%s?", g_voteArg);
1.225 +
1.226 + if (answerCount < 2)
1.227 + {
1.228 + AddMenuItem(g_hVoteMenu, VOTE_YES, "Yes");
1.229 + AddMenuItem(g_hVoteMenu, VOTE_NO, "No");
1.230 + }
1.231 + else
1.232 + {
1.233 + for (new i = 0; i < answerCount; i++)
1.234 + {
1.235 + AddMenuItem(g_hVoteMenu, answers[i], answers[i]);
1.236 + }
1.237 + }
1.238 +
1.239 + SetMenuExitButton(g_hVoteMenu, false);
1.240 + VoteMenuToAll(g_hVoteMenu, 20);
1.241 +
1.242 + return Plugin_Handled;
1.243 +}
1.244 +
1.245 +public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2)
1.246 +{
1.247 + if (action == MenuAction_End)
1.248 + {
1.249 + VoteMenuClose();
1.250 + }
1.251 + else if (action == MenuAction_Display)
1.252 + {
1.253 + if (g_voteType != voteType:question)
1.254 + {
1.255 + decl String:title[64];
1.256 + GetMenuTitle(menu, title, sizeof(title));
1.257 +
1.258 + decl String:buffer[255];
1.259 + Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]);
1.260 +
1.261 + new Handle:panel = Handle:param2;
1.262 + SetPanelTitle(panel, buffer);
1.263 + }
1.264 + }
1.265 + else if (action == MenuAction_DisplayItem)
1.266 + {
1.267 + decl String:display[64];
1.268 + GetMenuItem(menu, param2, "", 0, _, display, sizeof(display));
1.269 +
1.270 + if (strcmp(display, "No") == 0 || strcmp(display, "Yes") == 0)
1.271 + {
1.272 + decl String:buffer[255];
1.273 + Format(buffer, sizeof(buffer), "%T", display, param1);
1.274 +
1.275 + return RedrawMenuItem(buffer);
1.276 + }
1.277 + }
1.278 + /* else if (action == MenuAction_Select)
1.279 + {
1.280 + VoteSelect(menu, param1, param2);
1.281 + }*/
1.282 + else if (action == MenuAction_VoteCancel && param1 == VoteCancel_NoVotes)
1.283 + {
1.284 + PrintToChatAll("[SM] %t", "No Votes Cast");
1.285 + }
1.286 + else if (action == MenuAction_VoteEnd)
1.287 + {
1.288 + decl String:item[64], String:display[64];
1.289 + new Float:percent, Float:limit, votes, totalVotes;
1.290 +
1.291 + GetMenuVoteInfo(param2, votes, totalVotes);
1.292 + GetMenuItem(menu, param1, item, sizeof(item), _, display, sizeof(display));
1.293 +
1.294 + if (strcmp(item, VOTE_NO) == 0 && param1 == 1)
1.295 + {
1.296 + votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option.
1.297 + }
1.298 +
1.299 + percent = GetVotePercent(votes, totalVotes);
1.300 +
1.301 + if (g_voteType != voteType:question)
1.302 + {
1.303 + limit = GetConVarFloat(g_Cvar_Limits[g_voteType]);
1.304 + }
1.305 +
1.306 + /* :TODO: g_voteClient[userid] needs to be checked */
1.307 +
1.308 + // A multi-argument vote is "always successful", but have to check if its a Yes/No vote.
1.309 + if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1))
1.310 + {
1.311 + /* :TODO: g_voteClient[userid] should be used here and set to -1 if not applicable.
1.312 + */
1.313 + LogAction(-1, -1, "Vote failed.");
1.314 + PrintToChatAll("[SM] %t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes);
1.315 + }
1.316 + else
1.317 + {
1.318 + PrintToChatAll("[SM] %t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes);
1.319 +
1.320 + switch (g_voteType)
1.321 + {
1.322 + case (voteType:question):
1.323 + {
1.324 + if (strcmp(item, VOTE_NO) == 0 || strcmp(item, VOTE_YES) == 0)
1.325 + {
1.326 + strcopy(item, sizeof(item), display);
1.327 + }
1.328 +
1.329 + PrintToChatAll("[SM] %t", "Vote End", g_voteArg, item);
1.330 + }
1.331 +
1.332 + case (voteType:map):
1.333 + {
1.334 + LogAction(-1, -1, "Changing map to %s due to vote.", item);
1.335 + PrintToChatAll("[SM] %t", "Changing map", item);
1.336 + new Handle:dp;
1.337 + CreateDataTimer(5.0, Timer_ChangeMap, dp);
1.338 + WritePackString(dp, item);
1.339 + }
1.340 +
1.341 + case (voteType:kick):
1.342 + {
1.343 + if (g_voteArg[0] == '\0')
1.344 + {
1.345 + strcopy(g_voteArg, sizeof(g_voteArg), "Votekicked");
1.346 + }
1.347 +
1.348 + PrintToChatAll("[SM] %t", "Kicked target", "_s", g_voteInfo[VOTE_NAME]);
1.349 + LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote kick successful, kicked \"%L\" (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg);
1.350 +
1.351 + ServerCommand("kickid %d \"%s\"", g_voteClient[VOTE_USERID], g_voteArg);
1.352 + }
1.353 +
1.354 + case (voteType:ban):
1.355 + {
1.356 + if (g_voteArg[0] == '\0')
1.357 + {
1.358 + strcopy(g_voteArg, sizeof(g_voteArg), "Votebanned");
1.359 + }
1.360 +
1.361 + PrintToChatAll("[SM] %t", "Banned player", g_voteInfo[VOTE_NAME], 30);
1.362 + LogAction(-1, g_voteClient[VOTE_CLIENTID], "Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg);
1.363 +
1.364 + BanClient(g_voteClient[VOTE_CLIENTID],
1.365 + 30,
1.366 + BANFLAG_AUTO,
1.367 + g_voteArg,
1.368 + "Banned by vote",
1.369 + "sm_voteban");
1.370 + }
1.371 + }
1.372 + }
1.373 + }
1.374 +
1.375 + return 0;
1.376 +}
1.377 +
1.378 +/*
1.379 +VoteSelect(Handle:menu, param1, param2 = 0)
1.380 +{
1.381 + if (GetConVarInt(g_Cvar_VoteShow) == 1)
1.382 + {
1.383 + decl String:voter[64], String:junk[64], String:choice[64];
1.384 + GetClientName(param1, voter, sizeof(voter));
1.385 + GetMenuItem(menu, param2, junk, sizeof(junk), _, choice, sizeof(choice));
1.386 + PrintToChatAll("[SM] %T", "Vote Select", LANG_SERVER, voter, choice);
1.387 + }
1.388 +}
1.389 +*/
1.390 +
1.391 +VoteMenuClose()
1.392 +{
1.393 + CloseHandle(g_hVoteMenu);
1.394 + g_hVoteMenu = INVALID_HANDLE;
1.395 +}
1.396 +
1.397 +Float:GetVotePercent(votes, totalVotes)
1.398 +{
1.399 + return FloatDiv(float(votes),float(totalVotes));
1.400 +}
1.401 +
1.402 +bool:TestVoteDelay(client)
1.403 +{
1.404 + new delay = CheckVoteDelay();
1.405 +
1.406 + if (delay > 0)
1.407 + {
1.408 + if (delay > 60)
1.409 + {
1.410 + ReplyToCommand(client, "[SM] %t", "Vote Delay Minutes", delay % 60);
1.411 + }
1.412 + else
1.413 + {
1.414 + ReplyToCommand(client, "[SM] %t", "Vote Delay Seconds", delay);
1.415 + }
1.416 +
1.417 + return false;
1.418 + }
1.419 +
1.420 + return true;
1.421 +}
1.422 +
1.423 +public Action:Timer_ChangeMap(Handle:timer, Handle:dp)
1.424 +{
1.425 + decl String:mapname[65];
1.426 +
1.427 + ResetPack(dp);
1.428 + ReadPackString(dp, mapname, sizeof(mapname));
1.429 +
1.430 + ServerCommand("changelevel \"%s\"", mapname);
1.431 +
1.432 + return Plugin_Stop;
1.433 +}