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