3 * =============================================================================
5 * Copyright (C) 2004-2009 AlliedModders LLC. All rights reserved.
6 * =============================================================================
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.
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
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/>.
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>.
34 #include "MenuVoting.h"
35 #include "PlayerManager.h"
36 #include "sourcemm_api.h"
38 float g_next_vote = 0.0f;
40 #if SOURCE_ENGINE >= SE_ORANGEBOX
41 void OnVoteDelayChange(IConVar *cvar, const char *value, float flOldValue);
43 void OnVoteDelayChange(ConVar *cvar, const char *value);
45 ConVar sm_vote_delay("sm_vote_delay",
48 "Sets the recommended time in between public votes",
55 #if SOURCE_ENGINE >= SE_ORANGEBOX
56 void OnVoteDelayChange(IConVar *cvar, const char *value, float flOldValue)
58 void OnVoteDelayChange(ConVar *cvar, const char *value)
61 /* See if the new vote delay isn't something we need to account for */
62 if (sm_vote_delay.GetFloat() < 1.0f)
68 /* If there was never a last vote, ignore this change */
69 if (g_next_vote < 0.1f)
74 /* Subtract the original value, then add the new one. */
75 g_next_vote -= (float)atof(value);
76 g_next_vote += sm_vote_delay.GetFloat();
79 unsigned int VoteMenuHandler::GetRemainingVoteDelay()
81 if (g_next_vote <= gpGlobals->curtime)
86 return (unsigned int)(g_next_vote - gpGlobals->curtime);
89 void VoteMenuHandler::OnSourceModAllInitialized()
91 g_Players.AddClientListener(this);
94 void VoteMenuHandler::OnSourceModShutdown()
96 g_Players.RemoveClientListener(this);
99 void VoteMenuHandler::OnSourceModLevelChange(const char *mapName)
104 unsigned int VoteMenuHandler::GetMenuAPIVersion2()
106 return m_pHandler->GetMenuAPIVersion2();
109 void VoteMenuHandler::OnClientDisconnected(int client)
111 if (!IsVoteInProgress())
116 /* Wipe out their vote if they had one. We have to make sure the the the
117 * newly connected client is not allowed to vote.
120 if ((item = m_ClientVotes[client]) >= -1)
124 assert((unsigned)item < m_Items);
125 assert(m_Votes[item] > 0);
128 m_ClientVotes[client] = -2;
132 bool VoteMenuHandler::IsVoteInProgress()
134 return (m_pCurMenu != NULL);
137 bool VoteMenuHandler::StartVote(IBaseMenu *menu,
138 unsigned int num_clients,
140 unsigned int max_time,
141 unsigned int flags/* =0 */)
143 if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags))
148 /* Note: we can use game time and not universal time because
149 * if we're voting then players are in-game.
152 float fVoteDelay = sm_vote_delay.GetFloat();
153 if (fVoteDelay < 1.0)
159 /* This little trick breaks for infinite votes!
160 * However, we just ignore that since those 1) shouldn't exist and
161 * 2) people must be checking IsVoteInProgress() beforehand anyway.
163 g_next_vote = gpGlobals->curtime + fVoteDelay + (float)max_time;
166 m_fStartTime = gpGlobals->curtime;
167 m_nMenuTime = max_time;
169 for (unsigned int i=0; i<num_clients; i++)
171 if (clients[i] < 1 || clients[i] > 256)
175 menu->Display(clients[i], max_time, this);
183 bool VoteMenuHandler::IsClientInVotePool(int client)
186 || client > g_Players.MaxClients()
187 || m_pCurMenu == NULL)
192 return (m_ClientVotes[client] > -2);
195 bool VoteMenuHandler::GetClientVoteChoice(int client, unsigned int *pItem)
197 if (!IsClientInVotePool(client)
198 || m_ClientVotes[client] == -1)
203 *pItem = m_ClientVotes[client];
208 bool VoteMenuHandler::RedrawToClient(int client, bool revotes)
210 unsigned int time_limit;
212 if (!IsClientInVotePool(client))
217 if (m_ClientVotes[client] >= 0)
219 if ((m_VoteFlags & VOTEFLAG_NO_REVOTES) == VOTEFLAG_NO_REVOTES || !revotes)
223 assert((unsigned)m_ClientVotes[client] < m_Items);
224 assert(m_Votes[m_ClientVotes[client]] > 0);
225 m_Votes[m_ClientVotes[client]]--;
226 m_ClientVotes[client] = -1;
229 if (m_nMenuTime == MENU_TIME_FOREVER)
231 time_limit = m_nMenuTime;
235 time_limit = (int)((float)m_nMenuTime - (gpGlobals->curtime - m_fStartTime));
237 /* Make sure this doesn't round to zero */
238 if (time_limit == MENU_TIME_FOREVER)
244 return m_pCurMenu->Display(client, time_limit, this);
247 bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu,
248 IMenuHandler *handler,
252 if (IsVoteInProgress())
259 /* Mark all clients as not voting */
260 for (int i=1; i<=gpGlobals->maxClients; i++)
262 m_ClientVotes[i] = -2;
265 m_Items = menu->GetItemCount();
267 if (m_Votes.size() < (size_t)m_Items)
269 /* Only clear the items we need to... */
270 size_t size = m_Votes.size();
271 for (size_t i=0; i<size; i++)
275 m_Votes.resize(m_Items, 0);
279 for (unsigned int i=0; i<m_Items; i++)
288 m_pHandler = handler;
290 m_pHandler->OnMenuStart(m_pCurMenu);
295 void VoteMenuHandler::StartVoting()
304 m_pHandler->OnMenuVoteStart(m_pCurMenu);
306 /* By now we know how many clients were set.
307 * If there are none, we should end IMMEDIATELY.
315 void VoteMenuHandler::DecrementPlayerCount()
317 assert(m_Clients > 0);
321 if (m_bStarted && m_Clients == 0)
327 int SortVoteItems(const void *item1, const void *item2)
329 return ((menu_vote_result_t::menu_item_vote_t *)item2)->count
330 - ((menu_vote_result_t::menu_item_vote_t *)item1)->count;
333 void VoteMenuHandler::EndVoting()
335 /* Set when the next delay ends. We ignore cancellation because a menu
336 * was, at one point, displayed, which is all that counts. However, we
337 * do re-calculate the time just in case the menu had no time limit.
339 float fVoteDelay = sm_vote_delay.GetFloat();
340 if (fVoteDelay < 1.0)
346 g_next_vote = gpGlobals->curtime + fVoteDelay;
351 /* If we were cancelled, don't bother tabulating anything.
352 * Reset just in case someone tries to redraw, which means
353 * we need to save our states.
355 IBaseMenu *menu = m_pCurMenu;
356 IMenuHandler *handler = m_pHandler;
358 handler->OnMenuVoteCancel(menu, VoteCancel_Generic);
359 handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
363 menu_vote_result_t vote;
364 menu_vote_result_t::menu_client_vote_t client_vote[256];
365 menu_vote_result_t::menu_item_vote_t item_vote[256];
367 memset(&vote, 0, sizeof(vote));
369 /* Build the item list */
370 for (unsigned int i=0; i<m_Items; i++)
374 item_vote[vote.num_items].count = m_Votes[i];
375 item_vote[vote.num_items].item = i;
376 vote.num_votes += m_Votes[i];
380 vote.item_list = item_vote;
384 IBaseMenu *menu = m_pCurMenu;
385 IMenuHandler *handler = m_pHandler;
387 handler->OnMenuVoteCancel(menu, VoteCancel_NoVotes);
388 handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
392 /* Build the client list */
393 for (int i=1; i<=gpGlobals->maxClients; i++)
395 if (m_ClientVotes[i] >= -1)
397 client_vote[vote.num_clients].client = i;
398 client_vote[vote.num_clients].item = m_ClientVotes[i];
402 vote.client_list = client_vote;
404 /* Sort the item list descending like we promised */
407 sizeof(menu_vote_result_t::menu_item_vote_t),
410 /* Save states, then clear what we've saved.
411 * This makes us re-entrant, which is always the safe way to go.
413 IBaseMenu *menu = m_pCurMenu;
414 IMenuHandler *handler = m_pHandler;
418 handler->OnMenuVoteResults(menu, &vote);
419 handler->OnMenuEnd(menu, MenuEnd_VotingDone);
422 void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
427 void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
429 DecrementPlayerCount();
432 void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
434 m_pHandler->OnMenuCancel(menu, client, reason);
437 void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
439 m_ClientVotes[client] = -1;
440 m_pHandler->OnMenuDisplay(menu, client, display);
443 unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
445 return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
448 void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
450 m_pHandler->OnMenuDrawItem(menu, client, item, style);
453 void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
455 /* Check by our item count, NOT the vote array size */
458 m_ClientVotes[client] = item;
463 m_pHandler->OnMenuSelect(menu, client, item);
466 void VoteMenuHandler::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
468 if (m_pHandler->GetMenuAPIVersion2() >= 13)
470 m_pHandler->OnMenuSelect2(menu, client, item, item_on_page);
474 void VoteMenuHandler::InternalReset()
481 m_bCancelled = false;
485 void VoteMenuHandler::CancelVoting()
487 if (m_bCancelled || !m_pCurMenu)
492 m_pCurMenu->Cancel();
495 IBaseMenu *VoteMenuHandler::GetCurrentMenu()
500 bool VoteMenuHandler::IsCancelling()