plugins/basevotes.sp
author Michael McKoy <ferret99gt@gmail.com>
Fri Aug 10 01:07:07 2007 +0000 (2007-08-10)
changeset 1297 423d171fafac
parent 1295 957df1c7b80a
child 1330 ab94316bb52b
permissions -rw-r--r--
VoteCancel_NoVotes implemented in vote plugins
     1 /**
     2  * basevotes.sp
     3  * Implements basic vote commands.
     4  * This file is part of SourceMod, Copyright (C) 2004-2007 AlliedModders LLC
     5  *
     6  * This program is free software; you can redistribute it and/or
     7  * modify it under the terms of the GNU General Public License
     8  * as published by the Free Software Foundation; either version 2
     9  * of the License, or (at your option) any later version.
    10  *
    11  * This program is distributed in the hope that it will be useful,
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  * GNU General Public License for more details.
    15  *
    16  * You should have received a copy of the GNU General Public License
    17  * along with this program; if not, write to the Free Software
    18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
    19  *
    20  * Version: $Id$
    21  */
    22 
    23 #include <sourcemod>
    24 
    25 #pragma semicolon 1
    26 
    27 public Plugin:myinfo =
    28 {
    29 	name = "Basic Votes",
    30 	author = "AlliedModders LLC",
    31 	description = "Basic Vote Commands",
    32 	version = SOURCEMOD_VERSION,
    33 	url = "http://www.sourcemod.net/"
    34 };
    35 
    36 #define VOTE_NO "###no###"
    37 #define VOTE_YES "###yes###"
    38 
    39 new Handle:g_hVoteMenu = INVALID_HANDLE;
    40 
    41 new Handle:g_hBanForward = INVALID_HANDLE;
    42 
    43 new Handle:g_Cvar_Limits[3] = {INVALID_HANDLE, ...};
    44 //new Handle:g_Cvar_VoteSay = INVALID_HANDLE;
    45 
    46 enum voteType
    47 {
    48 	map,
    49 	kick,
    50 	ban,
    51 	question
    52 }
    53 
    54 new voteType:g_voteType = voteType:question;
    55 
    56 // Menu API does not provide us with a way to pass multiple peices of data with a single
    57 // choice, so some globals are used to hold stuff.
    58 //
    59 #define VOTE_CLIENTID	0
    60 #define VOTE_USERID	1
    61 new g_voteClient[2];		/* Holds the target's client id and user id */
    62 
    63 #define VOTE_NAME	0
    64 #define VOTE_AUTHID	1
    65 #define	VOTE_IP		2
    66 new String:g_voteInfo[3][65];	/* Holds the target's name, authid, and IP */
    67 
    68 new String:g_voteArg[256];	/* Used to hold ban/kick reasons or vote questions */
    69 
    70 
    71 public OnPluginStart()
    72 {
    73 	LoadTranslations("common.phrases");
    74 	LoadTranslations("basevotes.phrases");
    75 	
    76 	RegAdminCmd("sm_votemap", Command_Votemap, ADMFLAG_VOTE|ADMFLAG_CHANGEMAP, "sm_votemap <mapname> [mapname2] ... [mapname5] ");
    77 	RegAdminCmd("sm_votekick", Command_Votekick, ADMFLAG_VOTE|ADMFLAG_KICK, "sm_votekick <player> [reason]");
    78 	RegAdminCmd("sm_voteban", Command_Voteban, ADMFLAG_VOTE|ADMFLAG_BAN, "sm_voteban <player> [reason]");
    79 	RegAdminCmd("sm_vote", Command_Vote, ADMFLAG_VOTE, "sm_vote <question> [Answer1] [Answer2] ... [Answer5]");
    80 
    81 	/*
    82 	g_Cvar_Show = FindConVar("sm_vote_show");
    83 	if (g_Cvar_Show == INVALID_HANDLE)
    84 	{
    85 		g_Cvar_Show = CreateConVar("sm_vote_show", "1", "Show player's votes? Default on.", 0, true, 0.0, true, 1.0);
    86 	}
    87 	*/
    88 
    89 	g_Cvar_Limits[0] = CreateConVar("sm_vote_map", "0.60", "percent required for successful map vote.", 0, true, 0.05, true, 1.0);
    90 	g_Cvar_Limits[1] = CreateConVar("sm_vote_kick", "0.60", "percent required for successful kick vote.", 0, true, 0.05, true, 1.0);	
    91 	g_Cvar_Limits[2] = CreateConVar("sm_vote_ban", "0.60", "percent required for successful ban vote.", 0, true, 0.05, true, 1.0);		
    92 	
    93 	g_hBanForward = CreateGlobalForward("OnClientBanned", ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_String);
    94 }
    95 
    96 public Action:Command_Votemap(client, args)
    97 {
    98 	if (args < 1)
    99 	{
   100 		ReplyToCommand(client, "[SM] Usage: sm_votemap <mapname> [mapname2] ... [mapname5]");
   101 		return Plugin_Handled;	
   102 	}
   103 	
   104 	if (IsVoteInProgress())
   105 	{
   106 		ReplyToCommand(client, "[SM] %t", "Vote in Progress");
   107 		return Plugin_Handled;
   108 	}
   109 		
   110 	if (!TestVoteDelay(client))
   111 	{
   112 		return Plugin_Handled;
   113 	}
   114 	
   115 	decl String:text[256];
   116 	GetCmdArgString(text, sizeof(text));
   117 
   118 	decl String:maps[5][64];
   119 	new mapCount;	
   120 	new len, pos;
   121 	
   122 	while (pos != -1 && mapCount < 5)
   123 	{	
   124 		pos = BreakString(text[len], maps[mapCount], sizeof(maps[]));
   125 		
   126 		if (!IsMapValid(maps[mapCount]))
   127 		{
   128 			ReplyToCommand(client, "[SM] %t", "Map was not found", maps[mapCount]);
   129 			return Plugin_Handled;
   130 		}		
   131 
   132 		mapCount++;
   133 		
   134 		if (pos != -1)
   135 		{
   136 			len += pos;
   137 		}	
   138 	}
   139 
   140 	ShowActivity(client, "%t", "Initiated Vote Map");
   141 	
   142 	g_voteType = voteType:map;
   143 	
   144 	g_hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
   145 	
   146 	if (mapCount == 1)
   147 	{
   148 		strcopy(g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]), maps[0]);
   149 			
   150 		SetMenuTitle(g_hVoteMenu, "Change Map To");
   151 		AddMenuItem(g_hVoteMenu, maps[0], "Yes");
   152 		AddMenuItem(g_hVoteMenu, VOTE_NO, "No");
   153 	}
   154 	else
   155 	{
   156 		g_voteInfo[VOTE_NAME][0] = '\0';
   157 		
   158 		SetMenuTitle(g_hVoteMenu, "Map Vote");
   159 		for (new i = 0; i < mapCount; i++)
   160 		{
   161 			AddMenuItem(g_hVoteMenu, maps[i], maps[i]);
   162 		}	
   163 	}
   164 	
   165 	SetMenuExitButton(g_hVoteMenu, false);
   166 	VoteMenuToAll(g_hVoteMenu, 20);		
   167 	
   168 	return Plugin_Handled;	
   169 }
   170 
   171 public Action:Command_Vote(client, args)
   172 {
   173 	if (args < 1)
   174 	{
   175 		ReplyToCommand(client, "[SM] Usage: sm_vote <question> [Answer1] [Answer2] ... [Answer5]");
   176 		return Plugin_Handled;	
   177 	}
   178 	
   179 	if (IsVoteInProgress())
   180 	{
   181 		ReplyToCommand(client, "[SM] %t", "Vote in Progress");
   182 		return Plugin_Handled;
   183 	}
   184 		
   185 	if (!TestVoteDelay(client))
   186 	{
   187 		return Plugin_Handled;
   188 	}
   189 	
   190 	decl String:text[256];
   191 	GetCmdArgString(text, sizeof(text));
   192 
   193 	decl String:answers[5][64];
   194 	new answerCount;	
   195 	new len = BreakString(text, g_voteArg, sizeof(g_voteArg));
   196 	new pos = len;
   197 	
   198 	while (args > 1 && pos != -1 && answerCount < 5)
   199 	{	
   200 		pos = BreakString(text[len], answers[answerCount], sizeof(answers[]));
   201 		answerCount++;
   202 		
   203 		if (pos != -1)
   204 		{
   205 			len += pos;
   206 		}	
   207 	}
   208 
   209 	ShowActivity(client, "%t", "Initiate Vote", g_voteArg);
   210 	
   211 	g_voteType = voteType:question;
   212 	
   213 	g_hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
   214 	SetMenuTitle(g_hVoteMenu, "%s?", g_voteArg);
   215 	
   216 	if (answerCount < 2)
   217 	{
   218 		AddMenuItem(g_hVoteMenu, VOTE_YES, "Yes");
   219 		AddMenuItem(g_hVoteMenu, VOTE_NO, "No");
   220 	}
   221 	else
   222 	{
   223 		for (new i = 0; i < answerCount; i++)
   224 		{
   225 			AddMenuItem(g_hVoteMenu, answers[i], answers[i]);
   226 		}	
   227 	}
   228 	
   229 	SetMenuExitButton(g_hVoteMenu, false);
   230 	VoteMenuToAll(g_hVoteMenu, 20);		
   231 	
   232 	return Plugin_Handled;	
   233 }
   234 
   235 public Action:Command_Votekick(client, args)
   236 {
   237 	if (args < 1)
   238 	{
   239 		ReplyToCommand(client, "[SM] Usage: sm_votekick <player> [reason]");
   240 		return Plugin_Handled;	
   241 	}
   242 	
   243 	if (IsVoteInProgress())
   244 	{
   245 		ReplyToCommand(client, "[SM] %t", "Vote in Progress");
   246 		return Plugin_Handled;
   247 	}	
   248 	
   249 	if (!TestVoteDelay(client))
   250 	{
   251 		return Plugin_Handled;
   252 	}
   253 	
   254 	decl String:text[256], String:arg[64];
   255 	GetCmdArgString(text, sizeof(text));
   256 	
   257 	new len = BreakString(text, arg, sizeof(arg));
   258 	
   259 	new target = FindTarget(client, arg);
   260 	if (target == -1)
   261 	{
   262 		return Plugin_Handled;
   263 	}
   264 	
   265 	if (len != -1)
   266 	{
   267 		strcopy(g_voteArg, sizeof(g_voteArg), text[len]);
   268 	}
   269 	else
   270 	{
   271 		g_voteArg[0] = '\0';
   272 	}
   273 	
   274 	g_voteClient[VOTE_CLIENTID] = target;
   275 	g_voteClient[VOTE_USERID] = GetClientUserId(target);
   276 
   277 	GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]));
   278 
   279 	ShowActivity(client, "%t", "Initiated Vote Kick", g_voteInfo[VOTE_NAME]);
   280 	
   281 	g_voteType = voteType:kick;
   282 	
   283 	g_hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
   284 	SetMenuTitle(g_hVoteMenu, "Votekick Player");
   285 	AddMenuItem(g_hVoteMenu, VOTE_YES, "Yes");
   286 	AddMenuItem(g_hVoteMenu, VOTE_NO, "No");
   287 	SetMenuExitButton(g_hVoteMenu, false);
   288 	VoteMenuToAll(g_hVoteMenu, 20);
   289 	
   290 	return Plugin_Handled;
   291 }
   292 
   293 public Action:Command_Voteban(client, args)
   294 {
   295 	if (args < 1)
   296 	{
   297 		ReplyToCommand(client, "[SM] Usage: sm_voteban <player> [reason]");
   298 		return Plugin_Handled;	
   299 	}
   300 	
   301 	if (IsVoteInProgress())
   302 	{
   303 		ReplyToCommand(client, "[SM] %t", "Vote in Progress");
   304 		return Plugin_Handled;
   305 	}	
   306 	
   307 	if (!TestVoteDelay(client))
   308 	{
   309 		return Plugin_Handled;
   310 	}
   311 	
   312 	decl String:text[256], String:arg[64];
   313 	GetCmdArgString(text, sizeof(text));
   314 	
   315 	new len = BreakString(text, arg, sizeof(arg));
   316 	
   317 	new target = FindTarget(client, arg);
   318 	if (target == -1)
   319 	{
   320 		return Plugin_Handled;
   321 	}
   322 	
   323 	if (len != -1)
   324 	{
   325 		strcopy(g_voteArg, sizeof(g_voteArg), text[len]);
   326 	}
   327 	else
   328 	{
   329 		g_voteArg[0] = '\0';
   330 	}
   331 	
   332 	g_voteClient[VOTE_CLIENTID] = target;
   333 	g_voteClient[VOTE_USERID] = GetClientUserId(target);
   334 
   335 	GetClientName(target, g_voteInfo[VOTE_NAME], sizeof(g_voteInfo[]));
   336 	GetClientAuthString(target, g_voteInfo[VOTE_AUTHID], sizeof(g_voteInfo[]));
   337 	GetClientIP(target, g_voteInfo[VOTE_IP], sizeof(g_voteInfo[]));
   338 
   339 	ShowActivity(client, "%t", "Initiated Vote Ban", g_voteInfo[VOTE_NAME]);
   340 
   341 	g_voteType = voteType:ban;
   342 	
   343 	g_hVoteMenu = CreateMenu(Handler_VoteCallback, MenuAction:MENU_ACTIONS_ALL);
   344 	SetMenuTitle(g_hVoteMenu, "Voteban Player");
   345 	AddMenuItem(g_hVoteMenu, VOTE_YES, "Yes");
   346 	AddMenuItem(g_hVoteMenu, VOTE_NO, "No");
   347 	SetMenuExitButton(g_hVoteMenu, false);
   348 	VoteMenuToAll(g_hVoteMenu, 20);
   349 	
   350 	return Plugin_Handled;
   351 }
   352 
   353 public Handler_VoteCallback(Handle:menu, MenuAction:action, param1, param2)
   354 {
   355 	if (action == MenuAction_End)
   356 	{
   357 		VoteMenuClose();
   358 	}
   359 	else if (action == MenuAction_Display)
   360 	{
   361 	 	if (g_voteType != voteType:question)
   362 	 	{
   363 			decl String:title[64];
   364 			GetMenuTitle(g_hVoteMenu, title, sizeof(title));
   365 			
   366 	 		decl String:buffer[255];
   367 			Format(buffer, sizeof(buffer), "%T", title, param1, g_voteInfo[VOTE_NAME]);
   368 
   369 			SetMenuTitle(g_hVoteMenu, buffer);
   370 		}
   371 	}
   372 	else if (action == MenuAction_DisplayItem)
   373 	{
   374 		decl String:display[64];
   375 		GetMenuItem(menu, param2, "", 0, _, display, sizeof(display));
   376 	 
   377 	 	if (strcmp(display, "No") == 0 || strcmp(display, "Yes") == 0)
   378 	 	{
   379 			decl String:buffer[255];
   380 			Format(buffer, sizeof(buffer), "%T", display, param1);
   381 
   382 			return RedrawMenuItem(buffer);
   383 		}
   384 	}
   385 	/* else if (action == MenuAction_Select)
   386 	{
   387 		VoteSelect(menu, param1, param2);
   388 	}*/
   389 	else if (action == VoteCancel_NoVotes)
   390 	{
   391 		PrintToChatAll("[SM] %t", "No Votes Cast");
   392 	}	
   393 	else if (action == MenuAction_VoteEnd)
   394 	{
   395 		decl String:item[64], String:display[64];
   396 		new Float:percent, Float:limit, votes, totalVotes;
   397 
   398 		GetMenuVoteInfo(param2, votes, totalVotes);
   399 		GetMenuItem(menu, param1, item, sizeof(item), _, display, sizeof(display));
   400 		
   401 		if (strcmp(item, VOTE_NO) == 0 && param1 == 1)
   402 		{
   403 			votes = totalVotes - votes; // Reverse the votes to be in relation to the Yes option.
   404 		}
   405 		
   406 		percent = GetVotePercent(votes, totalVotes);
   407 		
   408 		if (g_voteType != voteType:question)
   409 		{
   410 			limit = GetConVarFloat(g_Cvar_Limits[g_voteType]);
   411 		}
   412 
   413 		// A multi-argument vote is "always successful", but have to check if its a Yes/No vote.
   414 		if ((strcmp(item, VOTE_YES) == 0 && FloatCompare(percent,limit) < 0 && param1 == 0) || (strcmp(item, VOTE_NO) == 0 && param1 == 1))
   415 		{
   416 			LogMessage("Vote failed.");
   417 			PrintToChatAll("[SM] %t", "Vote Failed", RoundToNearest(100.0*limit), RoundToNearest(100.0*percent), totalVotes);
   418 		}
   419 		else
   420 		{
   421 			PrintToChatAll("[SM] %t", "Vote Successful", RoundToNearest(100.0*percent), totalVotes);
   422 			
   423 			switch (g_voteType)
   424 			{
   425 				case (voteType:question):
   426 				{
   427 					if (strcmp(item, VOTE_NO) == 0 || strcmp(item, VOTE_YES) == 0)
   428 					{
   429 						strcopy(item, sizeof(item), display);
   430 					}
   431 					
   432 					PrintToChatAll("[SM] %t", "Vote End", g_voteArg, item);
   433 				}
   434 				
   435 				case (voteType:map):
   436 				{
   437 					LogMessage("Changing map to %s due to vote.", item);
   438 					PrintToChatAll("[SM] %t", "Changing map", item);
   439 					new Handle:dp;
   440 					CreateDataTimer(5.0, Timer_ChangeMap, dp);
   441 					WritePackString(dp, item);		
   442 				}
   443 					
   444 				case (voteType:kick):
   445 				{
   446 					if (g_voteArg[0] == '\0')
   447 					{
   448 						strcopy(g_voteArg, sizeof(g_voteArg), "Votekicked");
   449 					}
   450 					
   451 					PrintToChatAll("[SM] %t", "Kicked player", g_voteInfo[VOTE_NAME]);					
   452 					LogMessage("Vote kick successful, kicked \"%L\" (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg);
   453 					
   454 					ServerCommand("kickid %d \"%s\"", g_voteClient[VOTE_USERID], g_voteArg);					
   455 				}
   456 					
   457 				case (voteType:ban):
   458 				{
   459 					/* Fire the ban forward */
   460 					Call_StartForward(g_hBanForward);
   461 					Call_PushCell(0);
   462 					Call_PushCell(g_voteClient[VOTE_USERID]);
   463 					Call_PushCell(30);
   464 					Call_PushString(g_voteArg);
   465 					Call_Finish();
   466 				
   467 					if (g_voteArg[0] == '\0')
   468 					{
   469 						strcopy(g_voteArg, sizeof(g_voteArg), "Votebanned");
   470 					}
   471 					
   472 					PrintToChatAll("[SM] %t", "Banned player", g_voteInfo[VOTE_NAME], 30);
   473 					LogMessage("Vote ban successful, banned \"%L\" (minutes \"30\") (reason \"%s\")", g_voteClient[VOTE_CLIENTID], g_voteArg);
   474 					
   475 					ServerCommand("banid %d %s", 30, g_voteClient[VOTE_AUTHID]);
   476 					ServerCommand("kickid %d \"%s\"", g_voteClient[VOTE_USERID], g_voteArg);				
   477 				}
   478 			}
   479 		}
   480 	}
   481 	
   482 	return 0;
   483 }
   484 
   485 /*
   486 VoteSelect(Handle:menu, param1, param2 = 0)
   487 {
   488 	if (GetConVarInt(g_Cvar_VoteShow) == 1)
   489 	{
   490 		decl String:voter[64], String:junk[64], String:choice[64];
   491 		GetClientName(param1, voter, sizeof(voter));
   492 		GetMenuItem(menu, param2, junk, sizeof(junk), _, choice, sizeof(choice));
   493 		PrintToChatAll("[SM] %T", "Vote Select", LANG_SERVER, voter, choice);
   494 	}
   495 }
   496 */
   497 
   498 VoteMenuClose()
   499 {
   500 	CloseHandle(g_hVoteMenu);
   501 	g_hVoteMenu = INVALID_HANDLE;
   502 }
   503 
   504 Float:GetVotePercent(votes, totalVotes)
   505 {
   506 	return FloatDiv(float(votes),float(totalVotes));
   507 }
   508 
   509 bool:TestVoteDelay(client)
   510 {
   511  	new delay = CheckVoteDelay();
   512  	
   513  	if (delay > 0)
   514  	{
   515  		if (delay > 60)
   516  		{
   517  			ReplyToCommand(client, "[SM] %t", "Vote Delay Minutes", delay % 60);
   518  		}
   519  		else
   520  		{
   521  			ReplyToCommand(client, "[SM] %t", "Vote Delay Seconds", delay);
   522  		}
   523  		
   524  		return false;
   525  	}
   526  	
   527 	return true;
   528 }
   529 
   530 public Action:Timer_ChangeMap(Handle:timer, Handle:dp)
   531 {
   532 	decl String:mapname[65];
   533 	
   534 	ResetPack(dp);
   535 	ReadPackString(dp, mapname, sizeof(mapname));
   536 	
   537 	ServerCommand("changelevel \"%s\"", mapname);
   538 	
   539 	return Plugin_Stop;
   540 }