Add -Wno-narrowing to support building on GCC-4.7 (no bug, r=me).
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"
37 #include <sourcemod.h>
39 #include <HalfLife2.h>
42 #include <ITranslator.h>
43 #include "logic_bridge.h"
45 float g_next_vote = 0.0f;
47 #define VOTE_NOT_VOTING -2
48 #define VOTE_PENDING -1
50 ConVar sm_vote_hintbox("sm_vote_progress_hintbox",
53 "Show current vote progress in a hint box",
59 ConVar sm_vote_chat("sm_vote_progress_chat",
62 "Show current vote progress as chat messages",
68 ConVar sm_vote_console("sm_vote_progress_console",
71 "Show current vote progress as console messages",
77 ConVar sm_vote_client_console("sm_vote_progress_client_console",
80 "Show current vote progress as console messages to clients",
87 #if SOURCE_ENGINE >= SE_ORANGEBOX
88 void OnVoteDelayChange(IConVar *cvar, const char *value, float flOldValue);
90 void OnVoteDelayChange(ConVar *cvar, const char *value);
92 ConVar sm_vote_delay("sm_vote_delay",
95 "Sets the recommended time in between public votes",
102 #if SOURCE_ENGINE >= SE_ORANGEBOX
103 void OnVoteDelayChange(IConVar *cvar, const char *value, float flOldValue)
105 void OnVoteDelayChange(ConVar *cvar, const char *value)
108 /* See if the new vote delay isn't something we need to account for */
109 if (sm_vote_delay.GetFloat() < 1.0f)
115 /* If there was never a last vote, ignore this change */
116 if (g_next_vote < 0.1f)
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();
126 unsigned int VoteMenuHandler::GetRemainingVoteDelay()
128 if (g_next_vote <= gpGlobals->curtime)
133 return (unsigned int)(g_next_vote - gpGlobals->curtime);
136 void VoteMenuHandler::OnSourceModAllInitialized()
138 g_Players.AddClientListener(this);
141 void VoteMenuHandler::OnSourceModShutdown()
143 g_Players.RemoveClientListener(this);
146 void VoteMenuHandler::OnSourceModLevelChange(const char *mapName)
151 unsigned int VoteMenuHandler::GetMenuAPIVersion2()
153 return m_pHandler->GetMenuAPIVersion2();
156 void VoteMenuHandler::OnClientDisconnected(int client)
158 if (!IsVoteInProgress())
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.
167 if ((item = m_ClientVotes[client]) >= VOTE_PENDING)
171 assert((unsigned)item < m_Items);
172 assert(m_Votes[item] > 0);
175 m_ClientVotes[client] = VOTE_NOT_VOTING;
179 bool VoteMenuHandler::IsVoteInProgress()
181 return (m_pCurMenu != NULL);
184 bool VoteMenuHandler::StartVote(IBaseMenu *menu,
185 unsigned int num_clients,
187 unsigned int max_time,
188 unsigned int flags/* =0 */)
190 if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags))
195 /* Note: we can use game time and not universal time because
196 * if we're voting then players are in-game.
199 float fVoteDelay = sm_vote_delay.GetFloat();
200 if (fVoteDelay < 1.0)
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.
210 g_next_vote = gpGlobals->curtime + fVoteDelay + (float)max_time;
213 m_fStartTime = gpGlobals->curtime;
214 m_nMenuTime = max_time;
216 for (unsigned int i=0; i<num_clients; i++)
218 if (clients[i] < 1 || clients[i] > 256)
222 menu->Display(clients[i], max_time, this);
230 bool VoteMenuHandler::IsClientInVotePool(int client)
233 || client > g_Players.MaxClients()
234 || m_pCurMenu == NULL)
239 return (m_ClientVotes[client] > VOTE_NOT_VOTING);
242 bool VoteMenuHandler::GetClientVoteChoice(int client, unsigned int *pItem)
244 if (!IsClientInVotePool(client)
245 || m_ClientVotes[client] == VOTE_PENDING)
250 *pItem = m_ClientVotes[client];
255 bool VoteMenuHandler::RedrawToClient(int client, bool revotes)
257 unsigned int time_limit;
259 if (!IsClientInVotePool(client))
264 if (m_ClientVotes[client] >= 0)
266 if ((m_VoteFlags & VOTEFLAG_NO_REVOTES) == VOTEFLAG_NO_REVOTES || !revotes)
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;
278 if (m_nMenuTime == MENU_TIME_FOREVER)
280 time_limit = m_nMenuTime;
284 time_limit = (int)((float)m_nMenuTime - (gpGlobals->curtime - m_fStartTime));
286 /* Make sure this doesn't round to zero */
287 if (time_limit == MENU_TIME_FOREVER)
293 return m_pCurMenu->Display(client, time_limit, this);
296 bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu,
297 IMenuHandler *handler,
301 if (IsVoteInProgress())
308 /* Mark all clients as not voting */
309 for (int i=1; i<=gpGlobals->maxClients; i++)
311 m_ClientVotes[i] = VOTE_NOT_VOTING;
312 m_Revoting[i] = false;
315 m_Items = menu->GetItemCount();
317 if (m_Votes.size() < (size_t)m_Items)
319 /* Only clear the items we need to... */
320 size_t size = m_Votes.size();
321 for (size_t i=0; i<size; i++)
325 m_Votes.resize(m_Items, 0);
329 for (unsigned int i=0; i<m_Items; i++)
338 m_pHandler = handler;
340 m_pHandler->OnMenuStart(m_pCurMenu);
345 void VoteMenuHandler::StartVoting()
354 m_pHandler->OnMenuVoteStart(m_pCurMenu);
356 m_displayTimer = g_Timers.CreateTimer(this, 1.0, NULL, TIMER_FLAG_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
358 /* By now we know how many clients were set.
359 * If there are none, we should end IMMEDIATELY.
366 m_TotalClients = m_Clients;
369 void VoteMenuHandler::DecrementPlayerCount()
371 assert(m_Clients > 0);
375 if (m_bStarted && m_Clients == 0)
381 int SortVoteItems(const void *item1, const void *item2)
383 return ((menu_vote_result_t::menu_item_vote_t *)item2)->count
384 - ((menu_vote_result_t::menu_item_vote_t *)item1)->count;
387 void VoteMenuHandler::EndVoting()
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.
393 float fVoteDelay = sm_vote_delay.GetFloat();
394 if (fVoteDelay < 1.0)
400 g_next_vote = gpGlobals->curtime + fVoteDelay;
405 g_Timers.KillTimer(m_displayTimer);
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.
414 IBaseMenu *menu = m_pCurMenu;
415 IMenuHandler *handler = m_pHandler;
417 handler->OnMenuVoteCancel(menu, VoteCancel_Generic);
418 handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
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];
426 memset(&vote, 0, sizeof(vote));
428 /* Build the item list */
429 for (unsigned int i=0; i<m_Items; i++)
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];
439 vote.item_list = item_vote;
443 IBaseMenu *menu = m_pCurMenu;
444 IMenuHandler *handler = m_pHandler;
446 handler->OnMenuVoteCancel(menu, VoteCancel_NoVotes);
447 handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
451 /* Build the client list */
452 for (int i=1; i<=gpGlobals->maxClients; i++)
454 if (m_ClientVotes[i] >= VOTE_PENDING)
456 client_vote[vote.num_clients].client = i;
457 client_vote[vote.num_clients].item = m_ClientVotes[i];
461 vote.client_list = client_vote;
463 /* Sort the item list descending like we promised */
466 sizeof(menu_vote_result_t::menu_item_vote_t),
469 /* Save states, then clear what we've saved.
470 * This makes us re-entrant, which is always the safe way to go.
472 IBaseMenu *menu = m_pCurMenu;
473 IMenuHandler *handler = m_pHandler;
477 handler->OnMenuVoteResults(menu, &vote);
478 handler->OnMenuEnd(menu, MenuEnd_VotingDone);
481 void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
486 void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
488 DecrementPlayerCount();
491 void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
493 m_pHandler->OnMenuCancel(menu, client, reason);
496 void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
498 m_ClientVotes[client] = VOTE_PENDING;
499 m_pHandler->OnMenuDisplay(menu, client, display);
502 unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
504 return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
507 void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
509 m_pHandler->OnMenuDrawItem(menu, client, item, style);
512 void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
514 /* Check by our item count, NOT the vote array size */
517 m_ClientVotes[client] = item;
521 if (sm_vote_chat.GetBool() || sm_vote_console.GetBool() || sm_vote_client_console.GetBool())
523 static char buffer[1024];
525 menu->GetItemInfo(item, &dr);
527 if (sm_vote_console.GetBool())
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);
535 if (sm_vote_chat.GetBool() || sm_vote_client_console.GetBool())
537 int maxclients = g_Players.GetMaxClients();
538 for (int i=1; i<=maxclients; i++)
540 CPlayer *pPlayer = g_Players.GetPlayerByIndex(i);
543 if (pPlayer->IsInGame() && !pPlayer->IsFakeClient())
545 if (m_Revoting[client])
547 logicore.CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Changed Vote",
548 &i, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
552 logicore.CoreTranslate(buffer, sizeof(buffer), "[SM] %T", 4, NULL, "Voted For",
553 &i, g_Players.GetPlayerByIndex(client)->GetName(), dr.display);
556 if (sm_vote_chat.GetBool())
558 g_HL2.TextMsg(i, HUD_PRINTTALK, buffer);
561 if (sm_vote_client_console.GetBool())
563 ClientConsolePrint(pPlayer->GetEdict(), buffer);
574 m_pHandler->OnMenuSelect(menu, client, item);
577 void VoteMenuHandler::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
579 if (m_pHandler->GetMenuAPIVersion2() >= 13)
581 m_pHandler->OnMenuSelect2(menu, client, item, item_on_page);
585 void VoteMenuHandler::InternalReset()
592 m_bCancelled = false;
594 m_leaderList[0] = '\0';
595 m_displayTimer = NULL;
599 void VoteMenuHandler::CancelVoting()
601 if (m_bCancelled || !m_pCurMenu)
606 m_pCurMenu->Cancel();
609 IBaseMenu *VoteMenuHandler::GetCurrentMenu()
614 bool VoteMenuHandler::IsCancelling()
619 void VoteMenuHandler::DrawHintProgress()
621 if (!sm_vote_hintbox.GetBool())
626 static char buffer[1024];
628 float timeRemaining = (m_fStartTime + m_nMenuTime) - gpGlobals->curtime;
629 if (timeRemaining < 0)
634 int iTimeRemaining = RoundFloatToInt(timeRemaining);
636 int maxclients = g_Players.GetMaxClients();
637 for (int i=1; i<=maxclients; i++)
639 if (g_Players.GetPlayerByIndex(i)->IsInGame())
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);
648 void VoteMenuHandler::BuildVoteLeaders()
650 if (m_NumVotes == 0 || !sm_vote_hintbox.GetBool())
655 menu_vote_result_t vote;
656 menu_vote_result_t::menu_item_vote_t item_vote[256];
658 memset(&vote, 0, sizeof(vote));
660 /* Build the item list */
661 for (unsigned int i=0; i<m_Items; i++)
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];
671 vote.item_list = item_vote;
672 assert(vote.num_votes);
674 /* Sort the item list descending */
677 sizeof(menu_vote_result_t::menu_item_vote_t),
680 /* Take the top 3 (if applicable) and draw them */
682 for (unsigned int i=0; i<vote.num_items && i<3; i++)
684 int curItem = vote.item_list[i].item;
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);
691 SourceMod::ResultType VoteMenuHandler::OnTimer(ITimer *pTimer, void *pData)
698 void VoteMenuHandler::OnTimerEnd(ITimer *pTimer, void *pData)
700 m_displayTimer = NULL;