core/MenuVoting.cpp
author David Anderson <dvander@alliedmods.net>
Wed Aug 08 02:27:10 2007 +0000 (2007-08-08)
changeset 1269 27397584df9c
child 1291 c6d26de53926
permissions -rw-r--r--
- massive overhaul of the voting subsystem
- NOTE: menu API compatibility was COMPLETELY broken for C++!
- cleaned up various aspects of the menu API
- implemented new extended vote results callback
- extended the debug reporter a tiny bit
     1 /**
     2  * vim: set ts=4 :
     3  * ================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * ================================================================
     7  *
     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.
    11  * 
    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.
    16  *
    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/>.
    19  *
    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>.
    29  *
    30  * Version: $Id$
    31  */
    32 
    33 #include <string.h>
    34 #include <stdlib.h>
    35 #include "MenuVoting.h"
    36 #include "PlayerManager.h"
    37 #include "sourcemm_api.h"
    38 
    39 void VoteMenuHandler::OnSourceModAllInitialized()
    40 {
    41 	g_Players.AddClientListener(this);
    42 }
    43 
    44 void VoteMenuHandler::OnSourceModShutdown()
    45 {
    46 	g_Players.RemoveClientListener(this);
    47 }
    48 
    49 unsigned int VoteMenuHandler::GetMenuAPIVersion2()
    50 {
    51 	return m_pHandler->GetMenuAPIVersion2();
    52 }
    53 
    54 void VoteMenuHandler::OnClientDisconnected(int client)
    55 {
    56 	if (!IsVoteInProgress())
    57 	{
    58 		return;
    59 	}
    60 
    61 	/* Wipe out their vote if they had one */
    62 	int item;
    63 	if ((item = m_ClientVotes[client]) >= 0)
    64 	{
    65 		assert((unsigned)item < m_Items);
    66 		assert(m_Votes[item] > 0);
    67 		m_Votes[item]--;
    68 		m_ClientVotes[client] = -1;
    69 	}
    70 }
    71 
    72 bool VoteMenuHandler::IsVoteInProgress()
    73 {
    74 	return (m_pCurMenu != NULL);
    75 }
    76 
    77 bool VoteMenuHandler::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags/* =0 */)
    78 {
    79 	if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags))
    80 	{
    81 		return false;
    82 	}
    83 
    84 	for (unsigned int i=0; i<num_clients; i++)
    85 	{
    86 		menu->Display(clients[i], max_time, this);
    87 	}
    88 
    89 	StartVoting();
    90 
    91 	return true;
    92 }
    93 
    94 bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu, 
    95 									   IMenuHandler *handler,
    96 									   unsigned int time,
    97 									   unsigned int flags)
    98 {
    99 	if (IsVoteInProgress())
   100 	{
   101 		return false;
   102 	}
   103 
   104 	InternalReset();
   105 
   106 	/* Mark all clients as not voting */
   107 	for (int i=1; i<=gpGlobals->maxClients; i++)
   108 	{
   109 		m_ClientVotes[i] = -2;
   110 	}
   111 
   112 	m_Items = menu->GetItemCount();
   113 
   114 	if (m_Votes.size() < (size_t)m_Items)
   115 	{
   116 		/* Only clear the items we need to... */
   117 		size_t size = m_Votes.size();
   118 		for (size_t i=0; i<size; i++)
   119 		{
   120 			m_Votes[i] = 0;
   121 		}
   122 		m_Votes.resize(m_Items, 0);
   123 	} else {
   124 		for (unsigned int i=0; i<m_Items; i++)
   125 		{
   126 			m_Votes[i] = 0;
   127 		}
   128 	}
   129 
   130 	m_pCurMenu = menu;
   131 	m_VoteTime = time;
   132 	m_VoteFlags = flags;
   133 	m_pHandler = handler;
   134 
   135 	m_pHandler->OnMenuStart(m_pCurMenu);
   136 
   137 	return true;
   138 }
   139 
   140 void VoteMenuHandler::StartVoting()
   141 {
   142 	if (!m_pCurMenu)
   143 	{
   144 		return;
   145 	}
   146 
   147 	m_bStarted = true;
   148 
   149 	m_pHandler->OnMenuVoteStart(m_pCurMenu);
   150 
   151 	/* By now we know how many clients were set.  
   152 	 * If there are none, we should end IMMEDIATELY.
   153 	 */
   154 	if (m_Clients == 0)
   155 	{
   156 		EndVoting();
   157 	}
   158 }
   159 
   160 void VoteMenuHandler::DecrementPlayerCount()
   161 {
   162 	assert(m_Clients > 0);
   163 
   164 	m_Clients--;
   165 
   166 	if (m_bStarted && m_Clients == 0)
   167 	{
   168 		EndVoting();
   169 	}
   170 }
   171 
   172 int SortVoteItems(const void *item1, const void *item2)
   173 {
   174 	return ((menu_vote_result_t::menu_item_vote_t *)item2)->count 
   175 		- ((menu_vote_result_t::menu_item_vote_t *)item1)->count;
   176 }
   177 
   178 void VoteMenuHandler::EndVoting()
   179 {
   180 	if (m_bCancelled)
   181 	{
   182 		/* If we were cancelled, don't bother tabulating anything.
   183 		 * Reset just in case someone tries to redraw, which means
   184 		 * we need to save our states.
   185 		 */
   186 		IBaseMenu *menu = m_pCurMenu;
   187 		IMenuHandler *handler = m_pHandler;
   188 		InternalReset();
   189 		handler->OnMenuVoteCancel(menu);
   190 		handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
   191 		return;
   192 	}
   193 
   194 	menu_vote_result_t vote;
   195 	menu_vote_result_t::menu_client_vote_t client_vote[256];
   196 	menu_vote_result_t::menu_item_vote_t item_vote[256];
   197 
   198 	memset(&vote, 0, sizeof(vote));
   199 
   200 	/* Build the item list */
   201 	for (unsigned int i=0; i<m_Items; i++)
   202 	{
   203 		if (m_Votes[i] > 0)
   204 		{
   205 			item_vote[vote.num_items].count = m_Votes[i];
   206 			item_vote[vote.num_items].item = i;
   207 			vote.num_votes += m_Votes[i];
   208 			vote.num_items++;
   209 		}
   210 	}
   211 	vote.item_list = item_vote;
   212 
   213 	if (!vote.num_votes)
   214 	{
   215 		IBaseMenu *menu = m_pCurMenu;
   216 		IMenuHandler *handler = m_pHandler;
   217 		InternalReset();
   218 		handler->OnMenuVoteCancel(menu);
   219 		handler->OnMenuEnd(menu, MenuEnd_NoVotes);
   220 		return;
   221 	}
   222 
   223 	/* Build the client list */
   224 	for (int i=1; i<=gpGlobals->maxClients; i++)
   225 	{
   226 		if (m_ClientVotes[i] >= -1)
   227 		{
   228 			client_vote[vote.num_clients].client = i;
   229 			client_vote[vote.num_clients].item = m_ClientVotes[i];
   230 			vote.num_clients++;
   231 		}
   232 	}
   233 	vote.client_list = client_vote;
   234 
   235 	/* Sort the item list descending like we promised */
   236 	qsort(item_vote,
   237 		vote.num_items,
   238 		sizeof(menu_vote_result_t::menu_item_vote_t),
   239 		SortVoteItems);
   240 
   241 	/* Save states, then clear what we've saved.
   242 	 * This makes us re-entrant, which is always the safe way to go.
   243 	 */
   244 	IBaseMenu *menu = m_pCurMenu;
   245 	IMenuHandler *handler = m_pHandler;
   246 	InternalReset();
   247 
   248 	/* Send vote info */
   249 	handler->OnMenuVoteResults(menu, &vote);
   250 	handler->OnMenuEnd(menu, MenuEnd_VotingDone);
   251 }
   252 
   253 void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
   254 {
   255 	m_Clients++;
   256 }
   257 
   258 void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
   259 {
   260 	DecrementPlayerCount();
   261 }
   262 
   263 void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   264 {
   265 	m_pHandler->OnMenuCancel(menu, client, reason);
   266 }
   267 
   268 void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
   269 {
   270 	m_ClientVotes[client] = -1;
   271 	m_pHandler->OnMenuDisplay(menu, client, display);
   272 }
   273 
   274 unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
   275 {
   276 	return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
   277 }
   278 
   279 void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
   280 {
   281 	m_pHandler->OnMenuDrawItem(menu, client, item, style);
   282 }
   283 
   284 void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   285 {
   286 	/* Check by our item count, NOT the vote array size */
   287 	if (item < m_Items)
   288 	{
   289 		m_ClientVotes[client] = item;
   290 		m_Votes[item]++;
   291 		m_NumVotes++;
   292 	}
   293 
   294 	m_pHandler->OnMenuSelect(menu, client, item);
   295 }
   296 
   297 void VoteMenuHandler::InternalReset()
   298 {
   299 	m_Clients = 0;
   300 	m_Items = 0;
   301 	m_bStarted = false;
   302 	m_pCurMenu = NULL;
   303 	m_NumVotes = 0;
   304 	m_bCancelled = false;
   305 	m_pHandler = NULL;
   306 }
   307 
   308 void VoteMenuHandler::CancelVoting()
   309 {
   310 	if (m_bCancelled || !m_pCurMenu)
   311 	{
   312 		return;
   313 	}
   314 	m_bCancelled = true;
   315 	m_pCurMenu->Cancel();
   316 }
   317 
   318 IBaseMenu *VoteMenuHandler::GetCurrentMenu()
   319 {
   320 	return m_pCurMenu;
   321 }
   322 
   323 bool VoteMenuHandler::IsCancelling()
   324 {
   325 	return m_bCancelled;
   326 }