core/MenuVoting.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1296 0197a1212cb8
child 1474 a3b73df84d88
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

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