3 * ================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * ================================================================
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License,
10 * version 3.0, as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to
21 * link the code of this program (as well as its derivative works) to
22 * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any
23 * Game MODs that run on software by the Valve Corporation. You must
24 * obey the GNU General Public License in all respects for all other
25 * code used. Additionally, AlliedModders LLC grants this exception
26 * to all derivative works. AlliedModders LLC defines further
27 * exceptions, found in LICENSE.txt (as of this writing, version
28 * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
35 #include "MenuVoting.h"
36 #include "PlayerManager.h"
37 #include "sourcemm_api.h"
39 float g_next_vote = 0.0f;
41 void OnVoteDelayChange(ConVar *cvar, const char *value);
42 ConVar sm_vote_delay("sm_vote_delay",
45 "Sets the recommended time in between public votes",
52 void OnVoteDelayChange(ConVar *cvar, const char *value)
54 /* See if the new vote delay isn't something we need to account for */
55 if (sm_vote_delay.GetFloat() < 1.0f)
61 /* If there was never a last vote, ignore this change */
62 if (g_next_vote < 0.1f)
67 /* Subtract the original value, then add the new one. */
68 g_next_vote -= (float)atof(value);
69 g_next_vote += sm_vote_delay.GetFloat();
72 unsigned int VoteMenuHandler::GetRemainingVoteDelay()
74 if (g_next_vote <= gpGlobals->curtime)
79 return (unsigned int)(g_next_vote - gpGlobals->curtime);
82 void VoteMenuHandler::OnSourceModAllInitialized()
84 g_Players.AddClientListener(this);
87 void VoteMenuHandler::OnSourceModShutdown()
89 g_Players.RemoveClientListener(this);
92 void VoteMenuHandler::OnSourceModLevelChange(const char *mapName)
97 unsigned int VoteMenuHandler::GetMenuAPIVersion2()
99 return m_pHandler->GetMenuAPIVersion2();
102 void VoteMenuHandler::OnClientDisconnected(int client)
104 if (!IsVoteInProgress())
109 /* Wipe out their vote if they had one */
111 if ((item = m_ClientVotes[client]) >= 0)
113 assert((unsigned)item < m_Items);
114 assert(m_Votes[item] > 0);
116 m_ClientVotes[client] = -1;
120 bool VoteMenuHandler::IsVoteInProgress()
122 return (m_pCurMenu != NULL);
125 bool VoteMenuHandler::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags/* =0 */)
127 if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags))
132 float fVoteDelay = sm_vote_delay.GetFloat();
133 if (fVoteDelay < 1.0)
137 /* This little trick breaks for infinite votes!
138 * However, we just ignore that since those 1) shouldn't exist and
139 * 2) people must be checking IsVoteInProgress() beforehand anyway.
141 g_next_vote = gpGlobals->curtime + fVoteDelay + (float)max_time;
144 for (unsigned int i=0; i<num_clients; i++)
146 menu->Display(clients[i], max_time, this);
154 bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu,
155 IMenuHandler *handler,
159 if (IsVoteInProgress())
166 /* Mark all clients as not voting */
167 for (int i=1; i<=gpGlobals->maxClients; i++)
169 m_ClientVotes[i] = -2;
172 m_Items = menu->GetItemCount();
174 if (m_Votes.size() < (size_t)m_Items)
176 /* Only clear the items we need to... */
177 size_t size = m_Votes.size();
178 for (size_t i=0; i<size; i++)
182 m_Votes.resize(m_Items, 0);
184 for (unsigned int i=0; i<m_Items; i++)
193 m_pHandler = handler;
195 m_pHandler->OnMenuStart(m_pCurMenu);
200 void VoteMenuHandler::StartVoting()
209 m_pHandler->OnMenuVoteStart(m_pCurMenu);
211 /* By now we know how many clients were set.
212 * If there are none, we should end IMMEDIATELY.
220 void VoteMenuHandler::DecrementPlayerCount()
222 assert(m_Clients > 0);
226 if (m_bStarted && m_Clients == 0)
232 int SortVoteItems(const void *item1, const void *item2)
234 return ((menu_vote_result_t::menu_item_vote_t *)item2)->count
235 - ((menu_vote_result_t::menu_item_vote_t *)item1)->count;
238 void VoteMenuHandler::EndVoting()
240 /* Set when the next delay ends. We ignore cancellation because a menu
241 * was, at one point, displayed, which is all that counts. However, we
242 * do re-calculate the time just in case the menu had no time limit.
244 float fVoteDelay = sm_vote_delay.GetFloat();
245 if (fVoteDelay < 1.0)
249 g_next_vote = gpGlobals->curtime + fVoteDelay;
254 /* If we were cancelled, don't bother tabulating anything.
255 * Reset just in case someone tries to redraw, which means
256 * we need to save our states.
258 IBaseMenu *menu = m_pCurMenu;
259 IMenuHandler *handler = m_pHandler;
261 handler->OnMenuVoteCancel(menu);
262 handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
266 menu_vote_result_t vote;
267 menu_vote_result_t::menu_client_vote_t client_vote[256];
268 menu_vote_result_t::menu_item_vote_t item_vote[256];
270 memset(&vote, 0, sizeof(vote));
272 /* Build the item list */
273 for (unsigned int i=0; i<m_Items; i++)
277 item_vote[vote.num_items].count = m_Votes[i];
278 item_vote[vote.num_items].item = i;
279 vote.num_votes += m_Votes[i];
283 vote.item_list = item_vote;
287 IBaseMenu *menu = m_pCurMenu;
288 IMenuHandler *handler = m_pHandler;
290 handler->OnMenuVoteCancel(menu);
291 handler->OnMenuEnd(menu, MenuEnd_NoVotes);
295 /* Build the client list */
296 for (int i=1; i<=gpGlobals->maxClients; i++)
298 if (m_ClientVotes[i] >= -1)
300 client_vote[vote.num_clients].client = i;
301 client_vote[vote.num_clients].item = m_ClientVotes[i];
305 vote.client_list = client_vote;
307 /* Sort the item list descending like we promised */
310 sizeof(menu_vote_result_t::menu_item_vote_t),
313 /* Save states, then clear what we've saved.
314 * This makes us re-entrant, which is always the safe way to go.
316 IBaseMenu *menu = m_pCurMenu;
317 IMenuHandler *handler = m_pHandler;
321 handler->OnMenuVoteResults(menu, &vote);
322 handler->OnMenuEnd(menu, MenuEnd_VotingDone);
325 void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
330 void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
332 DecrementPlayerCount();
335 void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
337 m_pHandler->OnMenuCancel(menu, client, reason);
340 void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
342 m_ClientVotes[client] = -1;
343 m_pHandler->OnMenuDisplay(menu, client, display);
346 unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
348 return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
351 void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
353 m_pHandler->OnMenuDrawItem(menu, client, item, style);
356 void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
358 /* Check by our item count, NOT the vote array size */
361 m_ClientVotes[client] = item;
366 m_pHandler->OnMenuSelect(menu, client, item);
369 void VoteMenuHandler::InternalReset()
376 m_bCancelled = false;
380 void VoteMenuHandler::CancelVoting()
382 if (m_bCancelled || !m_pCurMenu)
387 m_pCurMenu->Cancel();
390 IBaseMenu *VoteMenuHandler::GetCurrentMenu()
395 bool VoteMenuHandler::IsCancelling()