core/MenuVoting.cpp
author David Anderson <dvander@alliedmods.net>
Thu Aug 09 19:05:34 2007 +0000 (2007-08-09)
changeset 1291 c6d26de53926
parent 1269 27397584df9c
child 1296 0197a1212cb8
permissions -rw-r--r--
added amb764, vote delay setting
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@1291
    39
float g_next_vote = 0.0f;
dvander@1291
    40
dvander@1291
    41
void OnVoteDelayChange(ConVar *cvar, const char *value);
dvander@1291
    42
ConVar sm_vote_delay("sm_vote_delay",
dvander@1291
    43
					 "30",
dvander@1291
    44
					 0,
dvander@1291
    45
					 "Sets the recommended time in between public votes",
dvander@1291
    46
					 false,
dvander@1291
    47
					 0.0,
dvander@1291
    48
					 false,
dvander@1291
    49
					 0.0,
dvander@1291
    50
					 OnVoteDelayChange);
dvander@1291
    51
dvander@1291
    52
void OnVoteDelayChange(ConVar *cvar, const char *value)
dvander@1291
    53
{
dvander@1291
    54
	/* See if the new vote delay isn't something we need to account for */
dvander@1291
    55
	if (sm_vote_delay.GetFloat() < 1.0f)
dvander@1291
    56
	{
dvander@1291
    57
		g_next_vote = 0.0f;
dvander@1291
    58
		return;
dvander@1291
    59
	}
dvander@1291
    60
dvander@1291
    61
	/* If there was never a last vote, ignore this change */
dvander@1291
    62
	if (g_next_vote < 0.1f)
dvander@1291
    63
	{
dvander@1291
    64
		return;
dvander@1291
    65
	}
dvander@1291
    66
dvander@1291
    67
	/* Subtract the original value, then add the new one. */
dvander@1291
    68
	g_next_vote -= (float)atof(value);
dvander@1291
    69
	g_next_vote += sm_vote_delay.GetFloat();
dvander@1291
    70
}
dvander@1291
    71
dvander@1291
    72
unsigned int VoteMenuHandler::GetRemainingVoteDelay()
dvander@1291
    73
{
dvander@1291
    74
	if (g_next_vote <= gpGlobals->curtime)
dvander@1291
    75
	{
dvander@1291
    76
		return 0;
dvander@1291
    77
	}
dvander@1291
    78
dvander@1291
    79
	return (unsigned int)(g_next_vote - gpGlobals->curtime);
dvander@1291
    80
}
dvander@1291
    81
dvander@1269
    82
void VoteMenuHandler::OnSourceModAllInitialized()
dvander@1269
    83
{
dvander@1269
    84
	g_Players.AddClientListener(this);
dvander@1269
    85
}
dvander@1269
    86
dvander@1269
    87
void VoteMenuHandler::OnSourceModShutdown()
dvander@1269
    88
{
dvander@1269
    89
	g_Players.RemoveClientListener(this);
dvander@1269
    90
}
dvander@1269
    91
dvander@1291
    92
void VoteMenuHandler::OnSourceModLevelChange(const char *mapName)
dvander@1291
    93
{
dvander@1291
    94
	g_next_vote = 0.0f;
dvander@1291
    95
}
dvander@1291
    96
dvander@1269
    97
unsigned int VoteMenuHandler::GetMenuAPIVersion2()
dvander@1269
    98
{
dvander@1269
    99
	return m_pHandler->GetMenuAPIVersion2();
dvander@1269
   100
}
dvander@1269
   101
dvander@1269
   102
void VoteMenuHandler::OnClientDisconnected(int client)
dvander@1269
   103
{
dvander@1269
   104
	if (!IsVoteInProgress())
dvander@1269
   105
	{
dvander@1269
   106
		return;
dvander@1269
   107
	}
dvander@1269
   108
dvander@1269
   109
	/* Wipe out their vote if they had one */
dvander@1269
   110
	int item;
dvander@1269
   111
	if ((item = m_ClientVotes[client]) >= 0)
dvander@1269
   112
	{
dvander@1269
   113
		assert((unsigned)item < m_Items);
dvander@1269
   114
		assert(m_Votes[item] > 0);
dvander@1269
   115
		m_Votes[item]--;
dvander@1269
   116
		m_ClientVotes[client] = -1;
dvander@1269
   117
	}
dvander@1269
   118
}
dvander@1269
   119
dvander@1269
   120
bool VoteMenuHandler::IsVoteInProgress()
dvander@1269
   121
{
dvander@1269
   122
	return (m_pCurMenu != NULL);
dvander@1269
   123
}
dvander@1269
   124
dvander@1269
   125
bool VoteMenuHandler::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags/* =0 */)
dvander@1269
   126
{
dvander@1269
   127
	if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags))
dvander@1269
   128
	{
dvander@1269
   129
		return false;
dvander@1269
   130
	}
dvander@1269
   131
dvander@1291
   132
	float fVoteDelay = sm_vote_delay.GetFloat();
dvander@1291
   133
	if (fVoteDelay < 1.0)
dvander@1291
   134
	{
dvander@1291
   135
		g_next_vote = 0.0;
dvander@1291
   136
	} else {
dvander@1291
   137
		/* This little trick breaks for infinite votes!
dvander@1291
   138
		 * However, we just ignore that since those 1) shouldn't exist and 
dvander@1291
   139
		 * 2) people must be checking IsVoteInProgress() beforehand anyway.
dvander@1291
   140
		 */
dvander@1291
   141
		g_next_vote = gpGlobals->curtime + fVoteDelay + (float)max_time;
dvander@1291
   142
	}
dvander@1291
   143
dvander@1269
   144
	for (unsigned int i=0; i<num_clients; i++)
dvander@1269
   145
	{
dvander@1269
   146
		menu->Display(clients[i], max_time, this);
dvander@1269
   147
	}
dvander@1269
   148
dvander@1269
   149
	StartVoting();
dvander@1269
   150
dvander@1269
   151
	return true;
dvander@1269
   152
}
dvander@1269
   153
dvander@1269
   154
bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu, 
dvander@1269
   155
									   IMenuHandler *handler,
dvander@1269
   156
									   unsigned int time,
dvander@1269
   157
									   unsigned int flags)
dvander@1269
   158
{
dvander@1269
   159
	if (IsVoteInProgress())
dvander@1269
   160
	{
dvander@1269
   161
		return false;
dvander@1269
   162
	}
dvander@1269
   163
dvander@1269
   164
	InternalReset();
dvander@1269
   165
dvander@1269
   166
	/* Mark all clients as not voting */
dvander@1269
   167
	for (int i=1; i<=gpGlobals->maxClients; i++)
dvander@1269
   168
	{
dvander@1269
   169
		m_ClientVotes[i] = -2;
dvander@1269
   170
	}
dvander@1269
   171
dvander@1269
   172
	m_Items = menu->GetItemCount();
dvander@1269
   173
dvander@1269
   174
	if (m_Votes.size() < (size_t)m_Items)
dvander@1269
   175
	{
dvander@1269
   176
		/* Only clear the items we need to... */
dvander@1269
   177
		size_t size = m_Votes.size();
dvander@1269
   178
		for (size_t i=0; i<size; i++)
dvander@1269
   179
		{
dvander@1269
   180
			m_Votes[i] = 0;
dvander@1269
   181
		}
dvander@1269
   182
		m_Votes.resize(m_Items, 0);
dvander@1269
   183
	} else {
dvander@1269
   184
		for (unsigned int i=0; i<m_Items; i++)
dvander@1269
   185
		{
dvander@1269
   186
			m_Votes[i] = 0;
dvander@1269
   187
		}
dvander@1269
   188
	}
dvander@1269
   189
dvander@1269
   190
	m_pCurMenu = menu;
dvander@1269
   191
	m_VoteTime = time;
dvander@1269
   192
	m_VoteFlags = flags;
dvander@1269
   193
	m_pHandler = handler;
dvander@1269
   194
dvander@1269
   195
	m_pHandler->OnMenuStart(m_pCurMenu);
dvander@1269
   196
dvander@1269
   197
	return true;
dvander@1269
   198
}
dvander@1269
   199
dvander@1269
   200
void VoteMenuHandler::StartVoting()
dvander@1269
   201
{
dvander@1269
   202
	if (!m_pCurMenu)
dvander@1269
   203
	{
dvander@1269
   204
		return;
dvander@1269
   205
	}
dvander@1269
   206
dvander@1269
   207
	m_bStarted = true;
dvander@1269
   208
dvander@1269
   209
	m_pHandler->OnMenuVoteStart(m_pCurMenu);
dvander@1269
   210
dvander@1269
   211
	/* By now we know how many clients were set.  
dvander@1269
   212
	 * If there are none, we should end IMMEDIATELY.
dvander@1269
   213
	 */
dvander@1269
   214
	if (m_Clients == 0)
dvander@1269
   215
	{
dvander@1269
   216
		EndVoting();
dvander@1269
   217
	}
dvander@1269
   218
}
dvander@1269
   219
dvander@1269
   220
void VoteMenuHandler::DecrementPlayerCount()
dvander@1269
   221
{
dvander@1269
   222
	assert(m_Clients > 0);
dvander@1269
   223
dvander@1269
   224
	m_Clients--;
dvander@1269
   225
dvander@1269
   226
	if (m_bStarted && m_Clients == 0)
dvander@1269
   227
	{
dvander@1269
   228
		EndVoting();
dvander@1269
   229
	}
dvander@1269
   230
}
dvander@1269
   231
dvander@1269
   232
int SortVoteItems(const void *item1, const void *item2)
dvander@1269
   233
{
dvander@1269
   234
	return ((menu_vote_result_t::menu_item_vote_t *)item2)->count 
dvander@1269
   235
		- ((menu_vote_result_t::menu_item_vote_t *)item1)->count;
dvander@1269
   236
}
dvander@1269
   237
dvander@1269
   238
void VoteMenuHandler::EndVoting()
dvander@1269
   239
{
dvander@1291
   240
	/* Set when the next delay ends.  We ignore cancellation because a menu
dvander@1291
   241
	 * was, at one point, displayed, which is all that counts.  However, we
dvander@1291
   242
	 * do re-calculate the time just in case the menu had no time limit.
dvander@1291
   243
	 */
dvander@1291
   244
	float fVoteDelay = sm_vote_delay.GetFloat();
dvander@1291
   245
	if (fVoteDelay < 1.0)
dvander@1291
   246
	{
dvander@1291
   247
		g_next_vote = 0.0;
dvander@1291
   248
	} else {
dvander@1291
   249
		g_next_vote = gpGlobals->curtime + fVoteDelay;
dvander@1291
   250
	}
dvander@1291
   251
dvander@1269
   252
	if (m_bCancelled)
dvander@1269
   253
	{
dvander@1269
   254
		/* If we were cancelled, don't bother tabulating anything.
dvander@1269
   255
		 * Reset just in case someone tries to redraw, which means
dvander@1269
   256
		 * we need to save our states.
dvander@1269
   257
		 */
dvander@1269
   258
		IBaseMenu *menu = m_pCurMenu;
dvander@1269
   259
		IMenuHandler *handler = m_pHandler;
dvander@1269
   260
		InternalReset();
dvander@1269
   261
		handler->OnMenuVoteCancel(menu);
dvander@1269
   262
		handler->OnMenuEnd(menu, MenuEnd_VotingCancelled);
dvander@1269
   263
		return;
dvander@1269
   264
	}
dvander@1269
   265
dvander@1269
   266
	menu_vote_result_t vote;
dvander@1269
   267
	menu_vote_result_t::menu_client_vote_t client_vote[256];
dvander@1269
   268
	menu_vote_result_t::menu_item_vote_t item_vote[256];
dvander@1269
   269
dvander@1269
   270
	memset(&vote, 0, sizeof(vote));
dvander@1269
   271
dvander@1269
   272
	/* Build the item list */
dvander@1269
   273
	for (unsigned int i=0; i<m_Items; i++)
dvander@1269
   274
	{
dvander@1269
   275
		if (m_Votes[i] > 0)
dvander@1269
   276
		{
dvander@1269
   277
			item_vote[vote.num_items].count = m_Votes[i];
dvander@1269
   278
			item_vote[vote.num_items].item = i;
dvander@1269
   279
			vote.num_votes += m_Votes[i];
dvander@1269
   280
			vote.num_items++;
dvander@1269
   281
		}
dvander@1269
   282
	}
dvander@1269
   283
	vote.item_list = item_vote;
dvander@1269
   284
dvander@1269
   285
	if (!vote.num_votes)
dvander@1269
   286
	{
dvander@1269
   287
		IBaseMenu *menu = m_pCurMenu;
dvander@1269
   288
		IMenuHandler *handler = m_pHandler;
dvander@1269
   289
		InternalReset();
dvander@1269
   290
		handler->OnMenuVoteCancel(menu);
dvander@1269
   291
		handler->OnMenuEnd(menu, MenuEnd_NoVotes);
dvander@1269
   292
		return;
dvander@1269
   293
	}
dvander@1269
   294
dvander@1269
   295
	/* Build the client list */
dvander@1269
   296
	for (int i=1; i<=gpGlobals->maxClients; i++)
dvander@1269
   297
	{
dvander@1269
   298
		if (m_ClientVotes[i] >= -1)
dvander@1269
   299
		{
dvander@1269
   300
			client_vote[vote.num_clients].client = i;
dvander@1269
   301
			client_vote[vote.num_clients].item = m_ClientVotes[i];
dvander@1269
   302
			vote.num_clients++;
dvander@1269
   303
		}
dvander@1269
   304
	}
dvander@1269
   305
	vote.client_list = client_vote;
dvander@1269
   306
dvander@1269
   307
	/* Sort the item list descending like we promised */
dvander@1269
   308
	qsort(item_vote,
dvander@1269
   309
		vote.num_items,
dvander@1269
   310
		sizeof(menu_vote_result_t::menu_item_vote_t),
dvander@1269
   311
		SortVoteItems);
dvander@1269
   312
dvander@1269
   313
	/* Save states, then clear what we've saved.
dvander@1269
   314
	 * This makes us re-entrant, which is always the safe way to go.
dvander@1269
   315
	 */
dvander@1269
   316
	IBaseMenu *menu = m_pCurMenu;
dvander@1269
   317
	IMenuHandler *handler = m_pHandler;
dvander@1269
   318
	InternalReset();
dvander@1269
   319
dvander@1269
   320
	/* Send vote info */
dvander@1269
   321
	handler->OnMenuVoteResults(menu, &vote);
dvander@1269
   322
	handler->OnMenuEnd(menu, MenuEnd_VotingDone);
dvander@1269
   323
}
dvander@1269
   324
dvander@1269
   325
void VoteMenuHandler::OnMenuStart(IBaseMenu *menu)
dvander@1269
   326
{
dvander@1269
   327
	m_Clients++;
dvander@1269
   328
}
dvander@1269
   329
dvander@1269
   330
void VoteMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
dvander@1269
   331
{
dvander@1269
   332
	DecrementPlayerCount();
dvander@1269
   333
}
dvander@1269
   334
dvander@1269
   335
void VoteMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
dvander@1269
   336
{
dvander@1269
   337
	m_pHandler->OnMenuCancel(menu, client, reason);
dvander@1269
   338
}
dvander@1269
   339
dvander@1269
   340
void VoteMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display)
dvander@1269
   341
{
dvander@1269
   342
	m_ClientVotes[client] = -1;
dvander@1269
   343
	m_pHandler->OnMenuDisplay(menu, client, display);
dvander@1269
   344
}
dvander@1269
   345
dvander@1269
   346
unsigned int VoteMenuHandler::OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr)
dvander@1269
   347
{
dvander@1269
   348
	return m_pHandler->OnMenuDisplayItem(menu, client, panel, item, dr);
dvander@1269
   349
}
dvander@1269
   350
dvander@1269
   351
void VoteMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
dvander@1269
   352
{
dvander@1269
   353
	m_pHandler->OnMenuDrawItem(menu, client, item, style);
dvander@1269
   354
}
dvander@1269
   355
dvander@1269
   356
void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
dvander@1269
   357
{
dvander@1269
   358
	/* Check by our item count, NOT the vote array size */
dvander@1269
   359
	if (item < m_Items)
dvander@1269
   360
	{
dvander@1269
   361
		m_ClientVotes[client] = item;
dvander@1269
   362
		m_Votes[item]++;
dvander@1269
   363
		m_NumVotes++;
dvander@1269
   364
	}
dvander@1269
   365
dvander@1269
   366
	m_pHandler->OnMenuSelect(menu, client, item);
dvander@1269
   367
}
dvander@1269
   368
dvander@1269
   369
void VoteMenuHandler::InternalReset()
dvander@1269
   370
{
dvander@1269
   371
	m_Clients = 0;
dvander@1269
   372
	m_Items = 0;
dvander@1269
   373
	m_bStarted = false;
dvander@1269
   374
	m_pCurMenu = NULL;
dvander@1269
   375
	m_NumVotes = 0;
dvander@1269
   376
	m_bCancelled = false;
dvander@1269
   377
	m_pHandler = NULL;
dvander@1269
   378
}
dvander@1269
   379
dvander@1269
   380
void VoteMenuHandler::CancelVoting()
dvander@1269
   381
{
dvander@1269
   382
	if (m_bCancelled || !m_pCurMenu)
dvander@1269
   383
	{
dvander@1269
   384
		return;
dvander@1269
   385
	}
dvander@1269
   386
	m_bCancelled = true;
dvander@1269
   387
	m_pCurMenu->Cancel();
dvander@1269
   388
}
dvander@1269
   389
dvander@1269
   390
IBaseMenu *VoteMenuHandler::GetCurrentMenu()
dvander@1269
   391
{
dvander@1269
   392
	return m_pCurMenu;
dvander@1269
   393
}
dvander@1269
   394
dvander@1269
   395
bool VoteMenuHandler::IsCancelling()
dvander@1269
   396
{
dvander@1269
   397
	return m_bCancelled;
dvander@1269
   398
}