core/MenuVoting.cpp
author Kyle Sanderson <kyle.leet@gmail.com>
Mon Aug 12 00:45:29 2013 +0100 (2013-08-12)
changeset 3996 4a0daf289347
parent 3505 5fa96c27eab9
permissions -rw-r--r--
Dump handle information when a plugin is forcefully unloaded. (bug 5666, r=asherkin)
     1 /**
     2  * vim: set ts=4 sw=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2009 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <string.h>
    33 #include <stdlib.h>
    34 #include "MenuVoting.h"
    35 #include "PlayerManager.h"
    36 #include "sourcemm_api.h"
    37 #include <sourcemod.h>
    38 #include <Logger.h>
    39 #include <HalfLife2.h>
    40 #include <mathlib.h>
    41 #include <const.h>
    42 #include <ITranslator.h>
    43 #include "logic_bridge.h"
    44 
    45 float g_next_vote = 0.0f;
    46 
    47 #define VOTE_NOT_VOTING -2
    48 #define VOTE_PENDING -1
    49 
    50 ConVar sm_vote_hintbox("sm_vote_progress_hintbox", 
    51 					   "0", 
    52 					   0, 
    53 					   "Show current vote progress in a hint box",
    54 					   true,
    55 					   0.0,
    56 					   true,
    57 					   1.0);
    58 
    59 ConVar sm_vote_chat("sm_vote_progress_chat", 
    60 					   "0", 
    61 					   0, 
    62 					   "Show current vote progress as chat messages",
    63 					   true,
    64 					   0.0,
    65 					   true,
    66 					   1.0);
    67 
    68 ConVar sm_vote_console("sm_vote_progress_console", 
    69 					   "0", 
    70 					   0, 
    71 					   "Show current vote progress as console messages",
    72 					   true,
    73 					   0.0,
    74 					   true,
    75 					   1.0);
    76 
    77 ConVar sm_vote_client_console("sm_vote_progress_client_console", 
    78 					   "0", 
    79 					   0, 
    80 					   "Show current vote progress as console messages to clients",
    81 					   true,
    82 					   0.0,
    83 					   true,
    84 					   1.0);
    85 
    86 
    87 #if SOURCE_ENGINE >= SE_ORANGEBOX
    88 void OnVoteDelayChange(IConVar *cvar, const char *value, float flOldValue);
    89 #else
    90 void OnVoteDelayChange(ConVar *cvar, const char *value);
    91 #endif
    92 ConVar sm_vote_delay("sm_vote_delay",
    93 					 "30",
    94 					 0,
    95 					 "Sets the recommended time in between public votes",
    96 					 false,
    97 					 0.0,
    98 					 false,
    99 					 0.0,
   100 					 OnVoteDelayChange);
   101 
   102 #if SOURCE_ENGINE >= SE_ORANGEBOX
   103 void OnVoteDelayChange(IConVar *cvar, const char *value, float flOldValue)
   104 #else
   105 void OnVoteDelayChange(ConVar *cvar, const char *value)
   106 #endif
   107 {
   108 	/* See if the new vote delay isn't something we need to account for */
   109 	if (sm_vote_delay.GetFloat() < 1.0f)
   110 	{
   111 		g_next_vote = 0.0f;
   112 		return;
   113 	}
   114 
   115 	/* If there was never a last vote, ignore this change */
   116 	if (g_next_vote < 0.1f)
   117 	{
   118 		return;
   119 	}
   120 
   121 	/* Subtract the original value, then add the new one. */
   122 	g_next_vote -= (float)atof(value);
   123 	g_next_vote += sm_vote_delay.GetFloat();
   124 }
   125 
   126 unsigned int VoteMenuHandler::GetRemainingVoteDelay()
   127 {
   128 	if (g_next_vote <= gpGlobals->curtime)
   129 	{
   130 		return 0;
   131 	}
   132 
   133 	return (unsigned int)(g_next_vote - gpGlobals->curtime);
   134 }
   135 
   136 void VoteMenuHandler::OnSourceModAllInitialized()
   137 {
   138 	g_Players.AddClientListener(this);
   139 }
   140 
   141 void VoteMenuHandler::OnSourceModShutdown()
   142 {
   143 	g_Players.RemoveClientListener(this);
   144 }
   145 
   146 void VoteMenuHandler::OnSourceModLevelChange(const char *mapName)
   147 {
   148 	g_next_vote = 0.0f;
   149 }
   150 
   151 unsigned int VoteMenuHandler::GetMenuAPIVersion2()
   152 {
   153 	return m_pHandler->GetMenuAPIVersion2();
   154 }
   155 
   156 void VoteMenuHandler::OnClientDisconnected(int client)
   157 {
   158 	if (!IsVoteInProgress())
   159 	{
   160 		return;
   161 	}
   162 
   163 	/* Wipe out their vote if they had one.  We have to make sure the the the
   164 	 * newly connected client is not allowed to vote. 
   165 	 */
   166 	int item;
   167 	if ((item = m_ClientVotes[client]) >= VOTE_PENDING)
   168 	{
   169 		if (item >= 0)
   170 		{
   171 			assert((unsigned)item < m_Items);
   172 			assert(m_Votes[item] > 0);
   173 			m_Votes[item]--;
   174 		}
   175 		m_ClientVotes[client] = VOTE_NOT_VOTING;
   176 	}
   177 }
   178 
   179 bool VoteMenuHandler::IsVoteInProgress()
   180 {
   181 	return (m_pCurMenu != NULL);
   182 }
   183 
   184 bool VoteMenuHandler::StartVote(IBaseMenu *menu,
   185 								unsigned int num_clients,
   186 								int clients[],
   187 								unsigned int max_time,
   188 								unsigned int flags/* =0 */)
   189 {
   190 	if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags))
   191 	{
   192 		return false;
   193 	}
   194 
   195 	/* Note: we can use game time and not universal time because 
   196 	 * if we're voting then players are in-game.
   197 	 */
   198 
   199 	float fVoteDelay = sm_vote_delay.GetFloat();
   200 	if (fVoteDelay < 1.0)
   201 	{
   202 		g_next_vote = 0.0;
   203 	}
   204 	else
   205 	{
   206 		/* This little trick breaks for infinite votes!
   207 		 * However, we just ignore that since those 1) shouldn't exist and 
   208 		 * 2) people must be checking IsVoteInProgress() beforehand anyway.
   209 		 */
   210 		g_next_vote = gpGlobals->curtime + fVoteDelay + (float)max_time;
   211 	}
   212 
   213 	m_fStartTime = gpGlobals->curtime;
   214 	m_nMenuTime = max_time;
   215 
   216 	for (unsigned int i=0; i<num_clients; i++)
   217 	{
   218 		if (clients[i] < 1 || clients[i] > 256)
   219 		{
   220 			continue;
   221 		}
   222 		menu->Display(clients[i], max_time, this);
   223 	}
   224 
   225 	StartVoting();
   226 
   227 	return true;
   228 }
   229 
   230 bool VoteMenuHandler::IsClientInVotePool(int client)
   231 {
   232 	if (client < 1 
   233 		|| client > g_Players.MaxClients() 
   234 		|| m_pCurMenu == NULL)
   235 	{
   236 		return false;
   237 	}
   238 
   239 	return (m_ClientVotes[client] > VOTE_NOT_VOTING);
   240 }
   241 
   242 bool VoteMenuHandler::GetClientVoteChoice(int client, unsigned int *pItem)
   243 {
   244 	if (!IsClientInVotePool(client)
   245 		|| m_ClientVotes[client] == VOTE_PENDING)
   246 	{
   247 		return false;
   248 	}
   249 
   250 	*pItem = m_ClientVotes[client];
   251 
   252 	return true;
   253 }
   254 
   255 bool VoteMenuHandler::RedrawToClient(int client, bool revotes)
   256 {
   257 	unsigned int time_limit;
   258 
   259 	if (!IsClientInVotePool(client))
   260 	{
   261 		return false;
   262 	}
   263 
   264 	if (m_ClientVotes[client] >= 0)
   265 	{
   266 		if ((m_VoteFlags & VOTEFLAG_NO_REVOTES) == VOTEFLAG_NO_REVOTES || !revotes)
   267 		{
   268 			return false;
   269 		}
   270 		assert((unsigned)m_ClientVotes[client] < m_Items);
   271 		assert(m_Votes[m_ClientVotes[client]] > 0);
   272 		m_Votes[m_ClientVotes[client]]--;
   273 		m_ClientVotes[client] = VOTE_PENDING;
   274 		m_Revoting[client] = true;
   275 		m_NumVotes--;
   276 	}
   277 
   278 	if (m_nMenuTime == MENU_TIME_FOREVER)
   279 	{
   280 		time_limit = m_nMenuTime;
   281 	}
   282 	else
   283 	{
   284 		time_limit = (int)((float)m_nMenuTime - (gpGlobals->curtime - m_fStartTime));
   285 		
   286 		/* Make sure this doesn't round to zero */
   287 		if (time_limit == MENU_TIME_FOREVER)
   288 		{
   289 			time_limit = 1;
   290 		}
   291 	}
   292 
   293 	return m_pCurMenu->Display(client, time_limit, this);
   294 }
   295 
   296 bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu, 
   297 									   IMenuHandler *handler,
   298 									   unsigned int time,
   299 									   unsigned int flags)
   300 {
   301 	if (IsVoteInProgress())
   302 	{
   303 		return false;
   304 	}
   305 
   306 	InternalReset();
   307 
   308 	/* Mark all clients as not voting */
   309 	for (int i=1; i<=gpGlobals->maxClients; i++)
   310 	{
   311 		m_ClientVotes[i] = VOTE_NOT_VOTING;
   312 		m_Revoting[i] = false;
   313 	}
   314 
   315 	m_Items = menu->GetItemCount();
   316 
   317 	if (m_Votes.size() < (size_t)m_Items)
   318 	{
   319 		/* Only clear the items we need to... */
   320 		size_t size = m_Votes.size();
   321 		for (size_t i=0; i<size; i++)
   322 		{
   323 			m_Votes[i] = 0;
   324 		}
   325 		m_Votes.resize(m_Items, 0);
   326 	}
   327 	else
   328 	{
   329 		for (unsigned int i=0; i<m_Items; i++)
   330 		{
   331 			m_Votes[i] = 0;
   332 		}
   333 	}
   334 
   335 	m_pCurMenu = menu;
   336 	m_VoteTime = time;
   337 	m_VoteFlags = flags;
   338 	m_pHandler = handler;
   339 
   340 	m_pHandler->OnMenuStart(m_pCurMenu);
   341 
   342 	return true;
   343 }
   344 
   345 void VoteMenuHandler::StartVoting()
   346 {
   347 	if (!m_pCurMenu)
   348 	{
   349 		return;
   350 	}
   351 
   352 	m_bStarted = true;
   353 
   354 	m_pHandler->OnMenuVoteStart(m_pCurMenu);
   355 
   356 	m_displayTimer = g_Timers.CreateTimer(this, 1.0, NULL, TIMER_FLAG_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
   357 
   358 	/* By now we know how many clients were set.  
   359 	 * If there are none, we should end IMMEDIATELY.
   360 	 */
   361 	if (m_Clients == 0)
   362 	{
   363 		EndVoting();
   364 	}
   365 
   366 	m_TotalClients = m_Clients;
   367 }
   368 
   369 void VoteMenuHandler::DecrementPlayerCount()
   370 {
   371 	assert(m_Clients > 0);
   372 
   373 	m_Clients--;
   374 
   375 	if (m_bStarted && m_Clients == 0)
   376 	{
   377 		EndVoting();
   378 	}
   379 }
   380 
   381 int SortVoteItems(const void *item1, const void *item2)
   382 {
   383 	return ((menu_vote_result_t::menu_item_vote_t *)item2)->count 
   384 		- ((menu_vote_result_t::menu_item_vote_t *)item1)->count;
   385 }
   386 
   387 void VoteMenuHandler::EndVoting()
   388 {
   389 	/* Set when the next delay ends.  We ignore cancellation because a menu
   390 	 * was, at one point, displayed, which is all that counts.  However, we
   391 	 * do re-calculate the time just in case the menu had no time limit.
   392 	 */
   393 	float fVoteDelay = sm_vote_delay.GetFloat();
   394 	if (fVoteDelay < 1.0)
   395 	{
   396 		g_next_vote = 0.0;
   397 	}
   398 	else
   399 	{
   400 		g_next_vote = gpGlobals->curtime + fVoteDelay;
   401 	}
   402 
   403 	if (m_displayTimer)
   404 	{
   405 		g_Timers.KillTimer(m_displayTimer);
   406 	}
   407 
   408 	if (m_bCancelled)
   409 	{
   410 		/* If we were cancelled, don't bother tabulating anything.
   411 		 * Reset just in case someone tries to redraw, which means
   412 		 * we need to save our states.
   413 		 */
   414 		IBaseMenu *menu = m_pCurMenu;
   415 		IMenuHandler *handler = m_pHandler;
   416 		InternalReset();
   417 		handler->OnMenuVoteCancel(menu, VoteCancel_Generic);
   418 		handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
   419 		return;
   420 	}
   421 
   422 	menu_vote_result_t vote;
   423 	menu_vote_result_t::menu_client_vote_t client_vote[256];
   424 	menu_vote_result_t::menu_item_vote_t item_vote[256];
   425 
   426 	memset(&vote, 0, sizeof(vote));
   427 
   428 	/* Build the item list */
   429 	for (unsigned int i=0; i<m_Items; i++)
   430 	{
   431 		if (m_Votes[i] > 0)
   432 		{
   433 			item_vote[vote.num_items].count = m_Votes[i];
   434 			item_vote[vote.num_items].item = i;
   435 			vote.num_votes += m_Votes[i];
   436 			vote.num_items++;
   437 		}
   438 	}
   439 	vote.item_list = item_vote;
   440 
   441 	if (!vote.num_votes)
   442 	{
   443 		IBaseMenu *menu = m_pCurMenu;
   444 		IMenuHandler *handler = m_pHandler;
   445 		InternalReset();
   446 		handler->OnMenuVoteCancel(menu, VoteCancel_NoVotes);
   447 		handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
   448 		return;
   449 	}
   450 
   451 	/* Build the client list */
   452 	for (int i=1; i<=gpGlobals->maxClients; i++)
   453 	{
   454 		if (m_ClientVotes[i] >= VOTE_PENDING)
   455 		{
   456 			client_vote[vote.num_clients].client = i;
   457 			client_vote[vote.num_clients].item = m_ClientVotes[i];
   458 			vote.num_clients++;
   459 		}
   460 	}
   461 	vote.client_list = client_vote;
   462 
   463 	/* Sort the item list descending like we promised */
   464 	qsort(item_vote,
   465 		vote.num_items,
   466 		sizeof(menu_vote_result_t::menu_item_vote_t),
   467 		SortVoteItems);
   468 
   469 	/* Save states, then clear what we've saved.
   470 	 * This makes us re-entrant, which is always the safe way to go.
   471 	 */
   472 	IBaseMenu *menu = m_pCurMenu;
   473 	IMenuHandler *handler = m_pHandler;
   474 	InternalReset();
   475 
   476 	/* Send vote info */
   477 	handler->OnMenuVoteResults(menu, &vote);
   478 	handler->OnMenuEnd(menu, MenuEnd_VotingDone);
   479 }
   480 
   481 void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
   482 {
   483 	m_Clients++;
   484 }
   485 
   486 void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
   487 {
   488 	DecrementPlayerCount();
   489 }
   490 
   491 void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   492 {
   493 	m_pHandler->OnMenuCancel(menu, client, reason);
   494 }
   495 
   496 void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
   497 {
   498 	m_ClientVotes[client] = VOTE_PENDING;
   499 	m_pHandler->OnMenuDisplay(menu, client, display);
   500 }
   501 
   502 unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
   503 {
   504 	return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
   505 }
   506 
   507 void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
   508 {
   509 	m_pHandler->OnMenuDrawItem(menu, client, item, style);
   510 }
   511 
   512 void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   513 {
   514 	/* Check by our item count, NOT the vote array size */
   515 	if (item < m_Items)
   516 	{
   517 		m_ClientVotes[client] = item;
   518 		m_Votes[item]++;
   519 		m_NumVotes++;
   520 
   521 		if (sm_vote_chat.GetBool() || sm_vote_console.GetBool() || sm_vote_client_console.GetBool())
   522 		{
   523 			static char buffer[1024];
   524 			ItemDrawInfo dr;
   525 			menu->GetItemInfo(item, &dr);
   526 
   527 			if (sm_vote_console.GetBool())
   528 			{
   529 				int target = SOURCEMOD_SERVER_LANGUAGE;
   530 				logicore.CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Voted For",
   531 				                       &target, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
   532 				Engine_LogPrintWrapper(buffer);
   533 			}
   534 			
   535 			if (sm_vote_chat.GetBool() || sm_vote_client_console.GetBool())
   536 			{
   537 				int maxclients = g_Players.GetMaxClients();
   538 				for (int i=1; i<=maxclients; i++)
   539 				{	
   540 					CPlayer *pPlayer = g_Players.GetPlayerByIndex(i);
   541 					assert(pPlayer);
   542 
   543 					if (pPlayer->IsInGame() && !pPlayer->IsFakeClient())
   544 					{
   545 						if (m_Revoting[client])
   546 						{
   547 							logicore.CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Changed Vote",
   548 							                       &i, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
   549 						}
   550 						else
   551 						{
   552 							logicore.CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Voted For",
   553 							                       &i, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
   554 						}
   555 
   556 						if (sm_vote_chat.GetBool())
   557 						{
   558 							g_HL2.TextMsg(i, HUD_PRINTTALK, buffer);
   559 						}
   560 
   561 						if (sm_vote_client_console.GetBool())
   562 						{
   563 							ClientConsolePrint(pPlayer->GetEdict(), buffer);
   564 						}		
   565 					}
   566 				}
   567 			}
   568 		}
   569 
   570 		BuildVoteLeaders();
   571 		DrawHintProgress();
   572 	}
   573 
   574 	m_pHandler->OnMenuSelect(menu, client, item);
   575 }
   576 
   577 void VoteMenuHandler::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
   578 {
   579 	if (m_pHandler->GetMenuAPIVersion2() >= 13)
   580 	{
   581 		m_pHandler->OnMenuSelect2(menu, client, item, item_on_page);
   582 	}
   583 }
   584 
   585 void VoteMenuHandler::InternalReset()
   586 {
   587 	m_Clients = 0;
   588 	m_Items = 0;
   589 	m_bStarted = false;
   590 	m_pCurMenu = NULL;
   591 	m_NumVotes = 0;
   592 	m_bCancelled = false;
   593 	m_pHandler = NULL;
   594 	m_leaderList[0] = '\0';
   595 	m_displayTimer = NULL;
   596 	m_TotalClients = 0;
   597 }
   598 
   599 void VoteMenuHandler::CancelVoting()
   600 {
   601 	if (m_bCancelled || !m_pCurMenu)
   602 	{
   603 		return;
   604 	}
   605 	m_bCancelled = true;
   606 	m_pCurMenu->Cancel();
   607 }
   608 
   609 IBaseMenu *VoteMenuHandler::GetCurrentMenu()
   610 {
   611 	return m_pCurMenu;
   612 }
   613 
   614 bool VoteMenuHandler::IsCancelling()
   615 {
   616 	return m_bCancelled;
   617 }
   618 
   619 void VoteMenuHandler::DrawHintProgress()
   620 {
   621 	if (!sm_vote_hintbox.GetBool())
   622 	{
   623 		return;
   624 	}
   625 
   626 	static char buffer[1024];
   627 
   628 	float timeRemaining = (m_fStartTime + m_nMenuTime) - gpGlobals->curtime;
   629 	if (timeRemaining < 0)
   630 	{
   631 		timeRemaining = 0.0;
   632 	}
   633 
   634 	int iTimeRemaining = RoundFloatToInt(timeRemaining);
   635 	
   636 	int maxclients = g_Players.GetMaxClients();
   637 	for (int i=1; i<=maxclients; i++)
   638 	{
   639 		if (g_Players.GetPlayerByIndex(i)->IsInGame())
   640 		{
   641 			logicore.CoreTranslate(buffer, sizeof(buffer), "%T%s", 6, NULL, "Vote Count",
   642 			                       &i, &m_NumVotes, &m_TotalClients, &iTimeRemaining, &m_leaderList);
   643 			g_HL2.HintTextMsg(i, buffer);
   644 		}
   645 	}
   646 }
   647 
   648 void VoteMenuHandler::BuildVoteLeaders()
   649 {
   650 	if (m_NumVotes == 0 || !sm_vote_hintbox.GetBool())
   651 	{
   652 		return;
   653 	}
   654 
   655 	menu_vote_result_t vote;
   656 	menu_vote_result_t::menu_item_vote_t item_vote[256];
   657 
   658 	memset(&vote, 0, sizeof(vote));
   659 
   660 	/* Build the item list */
   661 	for (unsigned int i=0; i<m_Items; i++)
   662 	{
   663 		if (m_Votes[i] > 0)
   664 		{
   665 			item_vote[vote.num_items].count = m_Votes[i];
   666 			item_vote[vote.num_items].item = i;
   667 			vote.num_votes += m_Votes[i];
   668 			vote.num_items++;
   669 		}
   670 	}
   671 	vote.item_list = item_vote;
   672 	assert(vote.num_votes);
   673 
   674 	/* Sort the item list descending */
   675 	qsort(item_vote,
   676 		vote.num_items,
   677 		sizeof(menu_vote_result_t::menu_item_vote_t),
   678 		SortVoteItems);
   679 
   680 	/* Take the top 3 (if applicable) and draw them */
   681 	int len = 0;
   682 	for (unsigned int i=0; i<vote.num_items && i<3; i++)
   683 	{
   684 		int curItem = vote.item_list[i].item;
   685 		ItemDrawInfo dr;
   686 		m_pCurMenu->GetItemInfo(curItem, &dr);
   687 		len += g_SourceMod.Format(m_leaderList + len, sizeof(m_leaderList) - len, "\n%i. %s: (%i)", i+1, dr.display, vote.item_list[i].count);
   688 	}
   689 }
   690 
   691 SourceMod::ResultType VoteMenuHandler::OnTimer(ITimer *pTimer, void *pData)
   692 {
   693 	DrawHintProgress();
   694 
   695 	return Pl_Continue;
   696 }
   697 
   698 void VoteMenuHandler::OnTimerEnd(ITimer *pTimer, void *pData)
   699 {
   700 	m_displayTimer = NULL;
   701 }