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
dvander@1269
     1
/**
dvander@1269
     2
 * vim: set ts=4 :
dvander@1269
     3
 * ================================================================
dvander@1269
     4
 * SourceMod
dvander@1269
     5
 * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
dvander@1269
     6
 * ================================================================
dvander@1269
     7
 *
dvander@1269
     8
 * This program is free software; you can redistribute it and/or 
dvander@1269
     9
 * modify it under the terms of the GNU General Public License, 
dvander@1269
    10
 * version 3.0, as published by the Free Software Foundation.
dvander@1269
    11
 * 
dvander@1269
    12
 * This program is distributed in the hope that it will be useful,
dvander@1269
    13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
dvander@1269
    14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
dvander@1269
    15
 * GNU General Public License for more details.
dvander@1269
    16
 *
dvander@1269
    17
 * You should have received a copy of the GNU General Public License
dvander@1269
    18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@1269
    19
 *
dvander@1269
    20
 * As a special exception, AlliedModders LLC gives you permission to 
dvander@1269
    21
 * link the code of this program (as well as its derivative works) to 
dvander@1269
    22
 * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any 
dvander@1269
    23
 * Game MODs that run on software by the Valve Corporation.  You must 
dvander@1269
    24
 * obey the GNU General Public License in all respects for all other 
dvander@1269
    25
 * code used. Additionally, AlliedModders LLC grants this exception 
dvander@1269
    26
 * to all derivative works. AlliedModders LLC defines further 
dvander@1269
    27
 * exceptions, found in LICENSE.txt (as of this writing, version 
dvander@1269
    28
 * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
dvander@1269
    29
 *
dvander@1269
    30
 * Version: $Id$
dvander@1269
    31
 */
dvander@1269
    32
dvander@1269
    33
#include <string.h>
dvander@1269
    34
#include <stdlib.h>
dvander@1269
    35
#include "MenuVoting.h"
dvander@1269
    36
#include "PlayerManager.h"
dvander@1269
    37
#include "sourcemm_api.h"
dvander@1269
    38
dvander@1269
    39
void VoteMenuHandler::OnSourceModAllInitialized()
dvander@1269
    40
{
dvander@1269
    41
	g_Players.AddClientListener(this);
dvander@1269
    42
}
dvander@1269
    43
dvander@1269
    44
void VoteMenuHandler::OnSourceModShutdown()
dvander@1269
    45
{
dvander@1269
    46
	g_Players.RemoveClientListener(this);
dvander@1269
    47
}
dvander@1269
    48
dvander@1269
    49
unsigned int VoteMenuHandler::GetMenuAPIVersion2()
dvander@1269
    50
{
dvander@1269
    51
	return m_pHandler->GetMenuAPIVersion2();
dvander@1269
    52
}
dvander@1269
    53
dvander@1269
    54
void VoteMenuHandler::OnClientDisconnected(int client)
dvander@1269
    55
{
dvander@1269
    56
	if (!IsVoteInProgress())
dvander@1269
    57
	{
dvander@1269
    58
		return;
dvander@1269
    59
	}
dvander@1269
    60
dvander@1269
    61
	/* Wipe out their vote if they had one */
dvander@1269
    62
	int item;
dvander@1269
    63
	if ((item = m_ClientVotes[client]) >= 0)
dvander@1269
    64
	{
dvander@1269
    65
		assert((unsigned)item < m_Items);
dvander@1269
    66
		assert(m_Votes[item] > 0);
dvander@1269
    67
		m_Votes[item]--;
dvander@1269
    68
		m_ClientVotes[client] = -1;
dvander@1269
    69
	}
dvander@1269
    70
}
dvander@1269
    71
dvander@1269
    72
bool VoteMenuHandler::IsVoteInProgress()
dvander@1269
    73
{
dvander@1269
    74
	return (m_pCurMenu != NULL);
dvander@1269
    75
}
dvander@1269
    76
dvander@1269
    77
bool VoteMenuHandler::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags/* =0 */)
dvander@1269
    78
{
dvander@1269
    79
	if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags))
dvander@1269
    80
	{
dvander@1269
    81
		return false;
dvander@1269
    82
	}
dvander@1269
    83
dvander@1269
    84
	for (unsigned int i=0; i<num_clients; i++)
dvander@1269
    85
	{
dvander@1269
    86
		menu->Display(clients[i], max_time, this);
dvander@1269
    87
	}
dvander@1269
    88
dvander@1269
    89
	StartVoting();
dvander@1269
    90
dvander@1269
    91
	return true;
dvander@1269
    92
}
dvander@1269
    93
dvander@1269
    94
bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu, 
dvander@1269
    95
									   IMenuHandler *handler,
dvander@1269
    96
									   unsigned int time,
dvander@1269
    97
									   unsigned int flags)
dvander@1269
    98
{
dvander@1269
    99
	if (IsVoteInProgress())
dvander@1269
   100
	{
dvander@1269
   101
		return false;
dvander@1269
   102
	}
dvander@1269
   103
dvander@1269
   104
	InternalReset();
dvander@1269
   105
dvander@1269
   106
	/* Mark all clients as not voting */
dvander@1269
   107
	for (int i=1; i<=gpGlobals->maxClients; i++)
dvander@1269
   108
	{
dvander@1269
   109
		m_ClientVotes[i] = -2;
dvander@1269
   110
	}
dvander@1269
   111
dvander@1269
   112
	m_Items = menu->GetItemCount();
dvander@1269
   113
dvander@1269
   114
	if (m_Votes.size() < (size_t)m_Items)
dvander@1269
   115
	{
dvander@1269
   116
		/* Only clear the items we need to... */
dvander@1269
   117
		size_t size = m_Votes.size();
dvander@1269
   118
		for (size_t i=0; i<size; i++)
dvander@1269
   119
		{
dvander@1269
   120
			m_Votes[i] = 0;
dvander@1269
   121
		}
dvander@1269
   122
		m_Votes.resize(m_Items, 0);
dvander@1269
   123
	} else {
dvander@1269
   124
		for (unsigned int i=0; i<m_Items; i++)
dvander@1269
   125
		{
dvander@1269
   126
			m_Votes[i] = 0;
dvander@1269
   127
		}
dvander@1269
   128
	}
dvander@1269
   129
dvander@1269
   130
	m_pCurMenu = menu;
dvander@1269
   131
	m_VoteTime = time;
dvander@1269
   132
	m_VoteFlags = flags;
dvander@1269
   133
	m_pHandler = handler;
dvander@1269
   134
dvander@1269
   135
	m_pHandler->OnMenuStart(m_pCurMenu);
dvander@1269
   136
dvander@1269
   137
	return true;
dvander@1269
   138
}
dvander@1269
   139
dvander@1269
   140
void VoteMenuHandler::StartVoting()
dvander@1269
   141
{
dvander@1269
   142
	if (!m_pCurMenu)
dvander@1269
   143
	{
dvander@1269
   144
		return;
dvander@1269
   145
	}
dvander@1269
   146
dvander@1269
   147
	m_bStarted = true;
dvander@1269
   148
dvander@1269
   149
	m_pHandler->OnMenuVoteStart(m_pCurMenu);
dvander@1269
   150
dvander@1269
   151
	/* By now we know how many clients were set.  
dvander@1269
   152
	 * If there are none, we should end IMMEDIATELY.
dvander@1269
   153
	 */
dvander@1269
   154
	if (m_Clients == 0)
dvander@1269
   155
	{
dvander@1269
   156
		EndVoting();
dvander@1269
   157
	}
dvander@1269
   158
}
dvander@1269
   159
dvander@1269
   160
void VoteMenuHandler::DecrementPlayerCount()
dvander@1269
   161
{
dvander@1269
   162
	assert(m_Clients > 0);
dvander@1269
   163
dvander@1269
   164
	m_Clients--;
dvander@1269
   165
dvander@1269
   166
	if (m_bStarted && m_Clients == 0)
dvander@1269
   167
	{
dvander@1269
   168
		EndVoting();
dvander@1269
   169
	}
dvander@1269
   170
}
dvander@1269
   171
dvander@1269
   172
int SortVoteItems(const void *item1, const void *item2)
dvander@1269
   173
{
dvander@1269
   174
	return ((menu_vote_result_t::menu_item_vote_t *)item2)->count 
dvander@1269
   175
		- ((menu_vote_result_t::menu_item_vote_t *)item1)->count;
dvander@1269
   176
}
dvander@1269
   177
dvander@1269
   178
void VoteMenuHandler::EndVoting()
dvander@1269
   179
{
dvander@1269
   180
	if (m_bCancelled)
dvander@1269
   181
	{
dvander@1269
   182
		/* If we were cancelled, don't bother tabulating anything.
dvander@1269
   183
		 * Reset just in case someone tries to redraw, which means
dvander@1269
   184
		 * we need to save our states.
dvander@1269
   185
		 */
dvander@1269
   186
		IBaseMenu *menu = m_pCurMenu;
dvander@1269
   187
		IMenuHandler *handler = m_pHandler;
dvander@1269
   188
		InternalReset();
dvander@1269
   189
		handler->OnMenuVoteCancel(menu);
dvander@1269
   190
		handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
dvander@1269
   191
		return;
dvander@1269
   192
	}
dvander@1269
   193
dvander@1269
   194
	menu_vote_result_t vote;
dvander@1269
   195
	menu_vote_result_t::menu_client_vote_t client_vote[256];
dvander@1269
   196
	menu_vote_result_t::menu_item_vote_t item_vote[256];
dvander@1269
   197
dvander@1269
   198
	memset(&vote, 0, sizeof(vote));
dvander@1269
   199
dvander@1269
   200
	/* Build the item list */
dvander@1269
   201
	for (unsigned int i=0; i<m_Items; i++)
dvander@1269
   202
	{
dvander@1269
   203
		if (m_Votes[i] > 0)
dvander@1269
   204
		{
dvander@1269
   205
			item_vote[vote.num_items].count = m_Votes[i];
dvander@1269
   206
			item_vote[vote.num_items].item = i;
dvander@1269
   207
			vote.num_votes += m_Votes[i];
dvander@1269
   208
			vote.num_items++;
dvander@1269
   209
		}
dvander@1269
   210
	}
dvander@1269
   211
	vote.item_list = item_vote;
dvander@1269
   212
dvander@1269
   213
	if (!vote.num_votes)
dvander@1269
   214
	{
dvander@1269
   215
		IBaseMenu *menu = m_pCurMenu;
dvander@1269
   216
		IMenuHandler *handler = m_pHandler;
dvander@1269
   217
		InternalReset();
dvander@1269
   218
		handler->OnMenuVoteCancel(menu);
dvander@1269
   219
		handler->OnMenuEnd(menu, MenuEnd_NoVotes);
dvander@1269
   220
		return;
dvander@1269
   221
	}
dvander@1269
   222
dvander@1269
   223
	/* Build the client list */
dvander@1269
   224
	for (int i=1; i<=gpGlobals->maxClients; i++)
dvander@1269
   225
	{
dvander@1269
   226
		if (m_ClientVotes[i] >= -1)
dvander@1269
   227
		{
dvander@1269
   228
			client_vote[vote.num_clients].client = i;
dvander@1269
   229
			client_vote[vote.num_clients].item = m_ClientVotes[i];
dvander@1269
   230
			vote.num_clients++;
dvander@1269
   231
		}
dvander@1269
   232
	}
dvander@1269
   233
	vote.client_list = client_vote;
dvander@1269
   234
dvander@1269
   235
	/* Sort the item list descending like we promised */
dvander@1269
   236
	qsort(item_vote,
dvander@1269
   237
		vote.num_items,
dvander@1269
   238
		sizeof(menu_vote_result_t::menu_item_vote_t),
dvander@1269
   239
		SortVoteItems);
dvander@1269
   240
dvander@1269
   241
	/* Save states, then clear what we've saved.
dvander@1269
   242
	 * This makes us re-entrant, which is always the safe way to go.
dvander@1269
   243
	 */
dvander@1269
   244
	IBaseMenu *menu = m_pCurMenu;
dvander@1269
   245
	IMenuHandler *handler = m_pHandler;
dvander@1269
   246
	InternalReset();
dvander@1269
   247
dvander@1269
   248
	/* Send vote info */
dvander@1269
   249
	handler->OnMenuVoteResults(menu, &vote);
dvander@1269
   250
	handler->OnMenuEnd(menu, MenuEnd_VotingDone);
dvander@1269
   251
}
dvander@1269
   252
dvander@1269
   253
void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
dvander@1269
   254
{
dvander@1269
   255
	m_Clients++;
dvander@1269
   256
}
dvander@1269
   257
dvander@1269
   258
void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
dvander@1269
   259
{
dvander@1269
   260
	DecrementPlayerCount();
dvander@1269
   261
}
dvander@1269
   262
dvander@1269
   263
void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
dvander@1269
   264
{
dvander@1269
   265
	m_pHandler->OnMenuCancel(menu, client, reason);
dvander@1269
   266
}
dvander@1269
   267
dvander@1269
   268
void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
dvander@1269
   269
{
dvander@1269
   270
	m_ClientVotes[client] = -1;
dvander@1269
   271
	m_pHandler->OnMenuDisplay(menu, client, display);
dvander@1269
   272
}
dvander@1269
   273
dvander@1269
   274
unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
dvander@1269
   275
{
dvander@1269
   276
	return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
dvander@1269
   277
}
dvander@1269
   278
dvander@1269
   279
void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
dvander@1269
   280
{
dvander@1269
   281
	m_pHandler->OnMenuDrawItem(menu, client, item, style);
dvander@1269
   282
}
dvander@1269
   283
dvander@1269
   284
void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
dvander@1269
   285
{
dvander@1269
   286
	/* Check by our item count, NOT the vote array size */
dvander@1269
   287
	if (item < m_Items)
dvander@1269
   288
	{
dvander@1269
   289
		m_ClientVotes[client] = item;
dvander@1269
   290
		m_Votes[item]++;
dvander@1269
   291
		m_NumVotes++;
dvander@1269
   292
	}
dvander@1269
   293
dvander@1269
   294
	m_pHandler->OnMenuSelect(menu, client, item);
dvander@1269
   295
}
dvander@1269
   296
dvander@1269
   297
void VoteMenuHandler::InternalReset()
dvander@1269
   298
{
dvander@1269
   299
	m_Clients = 0;
dvander@1269
   300
	m_Items = 0;
dvander@1269
   301
	m_bStarted = false;
dvander@1269
   302
	m_pCurMenu = NULL;
dvander@1269
   303
	m_NumVotes = 0;
dvander@1269
   304
	m_bCancelled = false;
dvander@1269
   305
	m_pHandler = NULL;
dvander@1269
   306
}
dvander@1269
   307
dvander@1269
   308
void VoteMenuHandler::CancelVoting()
dvander@1269
   309
{
dvander@1269
   310
	if (m_bCancelled || !m_pCurMenu)
dvander@1269
   311
	{
dvander@1269
   312
		return;
dvander@1269
   313
	}
dvander@1269
   314
	m_bCancelled = true;
dvander@1269
   315
	m_pCurMenu->Cancel();
dvander@1269
   316
}
dvander@1269
   317
dvander@1269
   318
IBaseMenu *VoteMenuHandler::GetCurrentMenu()
dvander@1269
   319
{
dvander@1269
   320
	return m_pCurMenu;
dvander@1269
   321
}
dvander@1269
   322
dvander@1269
   323
bool VoteMenuHandler::IsCancelling()
dvander@1269
   324
{
dvander@1269
   325
	return m_bCancelled;
dvander@1269
   326
}