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