core/MenuStyle_Base.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Aug 24 04:35:48 2008 +0000 (2008-08-24)
changeset 2237 4403a982ec91
parent 1952 b7aa2b626807
child 2289 44045ead61cd
permissions -rw-r--r--
Improved bot detection slightly. This was meant to go along with the fix for amb1801.
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 <stdarg.h>
ds@1952
    33
#include "sm_stringutil.h"
ds@1952
    34
#include "MenuStyle_Base.h"
ds@1952
    35
#include "PlayerManager.h"
ds@1952
    36
#include "MenuManager.h"
ds@1952
    37
#include "HandleSys.h"
ds@1952
    38
#include "CellRecipientFilter.h"
ds@1952
    39
#if defined MENU_DEBUG
ds@1952
    40
#include "Logger.h"
ds@1952
    41
#endif
ds@1952
    42
ds@1952
    43
BaseMenuStyle::BaseMenuStyle() : m_WatchList(256), m_hHandle(BAD_HANDLE)
ds@1952
    44
{
ds@1952
    45
}
ds@1952
    46
ds@1952
    47
Handle_t BaseMenuStyle::GetHandle()
ds@1952
    48
{
ds@1952
    49
	/* Don't create the handle until we need it */
ds@1952
    50
	if (m_hHandle == BAD_HANDLE)
ds@1952
    51
	{
ds@1952
    52
		m_hHandle = g_Menus.CreateStyleHandle(this);
ds@1952
    53
	}
ds@1952
    54
ds@1952
    55
	return m_hHandle;
ds@1952
    56
}
ds@1952
    57
ds@1952
    58
void BaseMenuStyle::AddClientToWatch(int client)
ds@1952
    59
{
ds@1952
    60
#if defined MENU_DEBUG
ds@1952
    61
	g_Logger.LogMessage("[SM_MENU] AddClientToWatch(%d)", client);
ds@1952
    62
#endif
ds@1952
    63
	m_WatchList.push_back(client);
ds@1952
    64
}
ds@1952
    65
ds@1952
    66
void BaseMenuStyle::RemoveClientFromWatch(int client)
ds@1952
    67
{
ds@1952
    68
#if defined MENU_DEBUG
ds@1952
    69
	g_Logger.LogMessage("[SM_MENU] RemoveClientFromWatch(%d)", client);
ds@1952
    70
#endif
ds@1952
    71
	m_WatchList.remove(client);
ds@1952
    72
}
ds@1952
    73
ds@1952
    74
void BaseMenuStyle::_CancelClientMenu(int client, MenuCancelReason reason, bool bAutoIgnore/* =false */)
ds@1952
    75
{
ds@1952
    76
#if defined MENU_DEBUG
ds@1952
    77
	g_Logger.LogMessage("[SM_MENU] _CancelClientMenu() (client %d) (bAutoIgnore %d) (reason %d)", client, bAutoIgnore, reason);
ds@1952
    78
#endif
ds@1952
    79
	CBaseMenuPlayer *player = GetMenuPlayer(client);
ds@1952
    80
	menu_states_t &states = player->states;
ds@1952
    81
ds@1952
    82
	bool bOldIgnore = player->bAutoIgnore;
ds@1952
    83
	if (bAutoIgnore)
ds@1952
    84
	{
ds@1952
    85
		player->bAutoIgnore = true;
ds@1952
    86
	}
ds@1952
    87
ds@1952
    88
	/* Save states */
ds@1952
    89
	IMenuHandler *mh = states.mh;
ds@1952
    90
	IBaseMenu *menu = states.menu;
ds@1952
    91
ds@1952
    92
	/* Clear menu */
ds@1952
    93
	player->bInMenu = false;
ds@1952
    94
	if (player->menuHoldTime)
ds@1952
    95
	{
ds@1952
    96
		RemoveClientFromWatch(client);
ds@1952
    97
	}
ds@1952
    98
ds@1952
    99
	/* Fire callbacks */
ds@1952
   100
	mh->OnMenuCancel(menu, client, reason);
ds@1952
   101
	
ds@1952
   102
	/* Only fire end if there's a valid menu */
ds@1952
   103
	if (menu)
ds@1952
   104
	{
ds@1952
   105
		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
ds@1952
   106
	}
ds@1952
   107
ds@1952
   108
	if (bAutoIgnore)
ds@1952
   109
	{
ds@1952
   110
		player->bAutoIgnore = bOldIgnore;
ds@1952
   111
	}
ds@1952
   112
}
ds@1952
   113
ds@1952
   114
void BaseMenuStyle::CancelMenu(CBaseMenu *menu)
ds@1952
   115
{
ds@1952
   116
#if defined MENU_DEBUG
ds@1952
   117
	g_Logger.LogMessage("[SM_MENU] CancelMenu() (menu %p)", menu);
ds@1952
   118
#endif
ds@1952
   119
	int maxClients = g_Players.GetMaxClients();
ds@1952
   120
	for (int i=1; i<=maxClients; i++)
ds@1952
   121
	{
ds@1952
   122
		CBaseMenuPlayer *player = GetMenuPlayer(i);
ds@1952
   123
		if (player->bInMenu)
ds@1952
   124
		{
ds@1952
   125
			menu_states_t &states = player->states;
ds@1952
   126
			if (states.menu == menu)
ds@1952
   127
			{
ds@1952
   128
				_CancelClientMenu(i, MenuCancel_Interrupted);
ds@1952
   129
			}
ds@1952
   130
		}
ds@1952
   131
	}
ds@1952
   132
}
ds@1952
   133
ds@1952
   134
bool BaseMenuStyle::CancelClientMenu(int client, bool autoIgnore)
ds@1952
   135
{
ds@1952
   136
#if defined MENU_DEBUG
ds@1952
   137
	g_Logger.LogMessage("[SM_MENU] CancelClientMenu() (client %d) (bAutoIgnore %d)", client, autoIgnore);
ds@1952
   138
#endif
ds@1952
   139
	if (client < 1 || client > g_Players.MaxClients())
ds@1952
   140
	{
ds@1952
   141
		return false;
ds@1952
   142
	}
ds@1952
   143
ds@1952
   144
	CBaseMenuPlayer *player = GetMenuPlayer(client);
ds@1952
   145
	if (!player->bInMenu)
ds@1952
   146
	{
ds@1952
   147
		return false;
ds@1952
   148
	}
ds@1952
   149
ds@1952
   150
	_CancelClientMenu(client, MenuCancel_Interrupted, autoIgnore);
ds@1952
   151
ds@1952
   152
	return true;
ds@1952
   153
}
ds@1952
   154
ds@1952
   155
MenuSource BaseMenuStyle::GetClientMenu(int client, void **object)
ds@1952
   156
{
ds@1952
   157
	if (client < 1 || client > g_Players.GetMaxClients())
ds@1952
   158
	{
ds@1952
   159
		return MenuSource_None;
ds@1952
   160
	}
ds@1952
   161
ds@1952
   162
	CBaseMenuPlayer *player = GetMenuPlayer(client);
ds@1952
   163
ds@1952
   164
	if (player->bInMenu)
ds@1952
   165
	{
ds@1952
   166
		IBaseMenu *menu;
ds@1952
   167
		if ((menu=player->states.menu) != NULL)
ds@1952
   168
		{
ds@1952
   169
			if (object)
ds@1952
   170
			{
ds@1952
   171
				*object = menu;
ds@1952
   172
			}
ds@1952
   173
			return MenuSource_BaseMenu;
ds@1952
   174
		}
ds@1952
   175
ds@1952
   176
		return MenuSource_Display;
ds@1952
   177
	} else if (player->bInExternMenu) {
ds@1952
   178
		if (player->menuHoldTime != 0
ds@1952
   179
			&& (gpGlobals->curtime > player->menuStartTime + player->menuHoldTime))
ds@1952
   180
		{
ds@1952
   181
			player->bInExternMenu = false;
ds@1952
   182
			return MenuSource_None;
ds@1952
   183
		}
ds@1952
   184
		return MenuSource_External;
ds@1952
   185
	}
ds@1952
   186
ds@1952
   187
	return MenuSource_None;
ds@1952
   188
}
ds@1952
   189
ds@1952
   190
void BaseMenuStyle::OnClientDisconnected(int client)
ds@1952
   191
{
ds@1952
   192
#if defined MENU_DEBUG
ds@1952
   193
	g_Logger.LogMessage("[SM_MENU] OnClientDisconnected(%d)", client);
ds@1952
   194
#endif
ds@1952
   195
	CBaseMenuPlayer *player = GetMenuPlayer(client);
ds@1952
   196
	if (!player->bInMenu)
ds@1952
   197
	{
ds@1952
   198
		return;
ds@1952
   199
	}
ds@1952
   200
ds@1952
   201
	_CancelClientMenu(client, MenuCancel_Disconnected, true);
ds@1952
   202
ds@1952
   203
	player->bInMenu = false;
ds@1952
   204
	player->bInExternMenu = false;
ds@1952
   205
}
ds@1952
   206
ds@1952
   207
static int do_lookup[256];
ds@1952
   208
void BaseMenuStyle::ProcessWatchList()
ds@1952
   209
{
ds@1952
   210
	if (!m_WatchList.size())
ds@1952
   211
	{
ds@1952
   212
		return;
ds@1952
   213
	}
ds@1952
   214
ds@1952
   215
#if defined MENU_DEBUG
ds@1952
   216
	g_Logger.LogMessage("BaseMenuStyle::ProcessWatchList(%d,%d,%d,%d,%d,%p)", 
ds@1952
   217
		m_WatchList.m_Size,
ds@1952
   218
		m_WatchList.m_FirstLink,
ds@1952
   219
		m_WatchList.m_FreeNodes,
ds@1952
   220
		m_WatchList.m_LastLink,
ds@1952
   221
		m_WatchList.m_MaxSize,
ds@1952
   222
		m_WatchList.m_Nodes);
ds@1952
   223
#endif
ds@1952
   224
ds@1952
   225
	unsigned int total = 0;
ds@1952
   226
	for (FastLink<int>::iterator iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter)
ds@1952
   227
	{
ds@1952
   228
		do_lookup[total++] = (*iter);
ds@1952
   229
	}
ds@1952
   230
ds@1952
   231
#if defined MENU_DEBUG
ds@1952
   232
	if (total)
ds@1952
   233
	{
ds@1952
   234
		g_Logger.LogMessage("[SM_MENU] ProcessWatchList() found %d clients", total);
ds@1952
   235
	}
ds@1952
   236
#endif
ds@1952
   237
ds@1952
   238
	int client;
ds@1952
   239
	CBaseMenuPlayer *player;
ds@1952
   240
	float curTime = gpGlobals->curtime;
ds@1952
   241
	for (unsigned int i=0; i<total; i++)
ds@1952
   242
	{
ds@1952
   243
		client = do_lookup[i];
ds@1952
   244
		player = GetMenuPlayer(client);
ds@1952
   245
#if defined MENU_DEBUG
ds@1952
   246
		g_Logger.LogMessage("[SM_MENU] ProcessWatchList() (client %d) (bInMenu %d) (menuHoldTime %d) (curTime %f) (menuStartTime %f)",
ds@1952
   247
			client,
ds@1952
   248
			player->bInMenu,
ds@1952
   249
			player->menuHoldTime,
ds@1952
   250
			curTime,
ds@1952
   251
			player->menuStartTime);
ds@1952
   252
#endif
ds@1952
   253
		if (!player->bInMenu || !player->menuHoldTime)
ds@1952
   254
		{
ds@1952
   255
#if defined MENU_DEBUG
ds@1952
   256
			g_Logger.LogMessage("[SM_MENU] ProcessWatchList() removing client %d", client);
ds@1952
   257
#endif
ds@1952
   258
			m_WatchList.remove(client);
ds@1952
   259
			continue;
ds@1952
   260
		}
ds@1952
   261
		if (curTime > player->menuStartTime + player->menuHoldTime)
ds@1952
   262
		{
ds@1952
   263
			_CancelClientMenu(client, MenuCancel_Timeout, false);
ds@1952
   264
		}
ds@1952
   265
	}
ds@1952
   266
}
ds@1952
   267
ds@1952
   268
void BaseMenuStyle::ClientPressedKey(int client, unsigned int key_press)
ds@1952
   269
{
ds@1952
   270
#if defined MENU_DEBUG
ds@1952
   271
	g_Logger.LogMessage("[SM_MENU] ClientPressedKey() (client %d) (key_press %d)", client, key_press);
ds@1952
   272
#endif
ds@1952
   273
	CBaseMenuPlayer *player = GetMenuPlayer(client);
ds@1952
   274
ds@1952
   275
	/* First question: Are we in a menu? */
ds@1952
   276
	if (!player->bInMenu)
ds@1952
   277
	{
ds@1952
   278
		return;
ds@1952
   279
	}
ds@1952
   280
ds@1952
   281
	bool cancel = false;
ds@1952
   282
	unsigned int item = 0;
ds@1952
   283
	MenuCancelReason reason = MenuCancel_Exit;
ds@1952
   284
	MenuEndReason end_reason = MenuEnd_Selected;
ds@1952
   285
	menu_states_t &states = player->states;
ds@1952
   286
ds@1952
   287
	/* Save variables */
ds@1952
   288
	IMenuHandler *mh = states.mh;
ds@1952
   289
	IBaseMenu *menu = states.menu;
ds@1952
   290
ds@1952
   291
	unsigned int item_on_page = states.item_on_page;
ds@1952
   292
ds@1952
   293
	assert(mh != NULL);
ds@1952
   294
ds@1952
   295
	if (menu == NULL)
ds@1952
   296
	{
ds@1952
   297
		item = key_press;
ds@1952
   298
	} else if (key_press < 1 || key_press > GetMaxPageItems()) {
ds@1952
   299
		cancel = true;
ds@1952
   300
	} else {
ds@1952
   301
		ItemSelection type = states.slots[key_press].type;
ds@1952
   302
ds@1952
   303
		/* Check if we should play a sound about the type */
ds@1952
   304
		if (g_Menus.MenuSoundsEnabled() && 
ds@1952
   305
			(!menu || (menu->GetMenuOptionFlags() & MENUFLAG_NO_SOUND) != MENUFLAG_NO_SOUND))
ds@1952
   306
		{
ds@1952
   307
			CellRecipientFilter filter;
ds@1952
   308
			cell_t clients[1];
ds@1952
   309
ds@1952
   310
			clients[0] = client;
ds@1952
   311
			filter.Initialize(clients, 1);
ds@1952
   312
ds@1952
   313
			const char *sound = g_Menus.GetMenuSound(type);
ds@1952
   314
ds@1952
   315
			if (sound != NULL)
ds@1952
   316
			{
ds@1952
   317
				edict_t *pEdict = engine->PEntityOfEntIndex(client);
ds@1952
   318
				if (pEdict)
ds@1952
   319
				{
ds@1952
   320
					ICollideable *pCollideable = pEdict->GetCollideable();
ds@1952
   321
ds@1952
   322
					if (pCollideable)
ds@1952
   323
					{
ds@1952
   324
						const Vector & pos = pCollideable->GetCollisionOrigin();
ds@1952
   325
		
ds@1952
   326
						enginesound->EmitSound(filter, 
ds@1952
   327
							client, 
ds@1952
   328
							CHAN_AUTO, 
ds@1952
   329
							sound, 
ds@1952
   330
							VOL_NORM, 
ds@1952
   331
							ATTN_NORM, 
ds@1952
   332
							0, 
ds@1952
   333
							PITCH_NORM, 
ds@1952
   334
							&pos);
ds@1952
   335
					}
ds@1952
   336
				}
ds@1952
   337
			}
ds@1952
   338
		}
ds@1952
   339
ds@1952
   340
		/* For navigational items, we're going to redisplay */
ds@1952
   341
		if (type == ItemSel_Back)
ds@1952
   342
		{
ds@1952
   343
			if (!RedoClientMenu(client, ItemOrder_Descending))
ds@1952
   344
			{
ds@1952
   345
				cancel = true;
ds@1952
   346
				reason = MenuCancel_NoDisplay;
ds@1952
   347
				end_reason = MenuEnd_Cancelled;
ds@1952
   348
			} else {
ds@1952
   349
				return;
ds@1952
   350
			}
ds@1952
   351
		} else if (type == ItemSel_Next) {
ds@1952
   352
			if (!RedoClientMenu(client, ItemOrder_Ascending))
ds@1952
   353
			{
ds@1952
   354
				cancel = true;						/* I like Saltines. */
ds@1952
   355
				reason = MenuCancel_NoDisplay;
ds@1952
   356
				end_reason = MenuEnd_Cancelled;
ds@1952
   357
			} else {
ds@1952
   358
				return;
ds@1952
   359
			}
ds@1952
   360
		} else if (type == ItemSel_Exit || type == ItemSel_None) {
ds@1952
   361
			cancel = true;
ds@1952
   362
			reason = MenuCancel_Exit;
ds@1952
   363
			end_reason = MenuEnd_Exit;
ds@1952
   364
		} else if (type == ItemSel_ExitBack) {
ds@1952
   365
			cancel = true;
ds@1952
   366
			reason = MenuCancel_ExitBack;
ds@1952
   367
			end_reason = MenuEnd_ExitBack;
ds@1952
   368
		} else {
ds@1952
   369
			item = states.slots[key_press].item;
ds@1952
   370
		}
ds@1952
   371
	}
ds@1952
   372
ds@1952
   373
	/* Clear states */
ds@1952
   374
	player->bInMenu = false;
ds@1952
   375
	if (player->menuHoldTime)
ds@1952
   376
	{
ds@1952
   377
		RemoveClientFromWatch(client);
ds@1952
   378
	}
ds@1952
   379
ds@1952
   380
	if (cancel)
ds@1952
   381
	{
ds@1952
   382
		mh->OnMenuCancel(menu, client, reason);
ds@1952
   383
	} else {
ds@1952
   384
		mh->OnMenuSelect(menu, client, item);
ds@1952
   385
		if (mh->GetMenuAPIVersion2() >= 13)
ds@1952
   386
		{
ds@1952
   387
			mh->OnMenuSelect2(menu, client, item, item_on_page);
ds@1952
   388
		}
ds@1952
   389
	}
ds@1952
   390
ds@1952
   391
	/* Only fire end for valid menus */
ds@1952
   392
	if (menu)
ds@1952
   393
	{
ds@1952
   394
		mh->OnMenuEnd(menu, end_reason);
ds@1952
   395
	}
ds@1952
   396
}
ds@1952
   397
ds@1952
   398
bool BaseMenuStyle::DoClientMenu(int client, IMenuPanel *menu, IMenuHandler *mh, unsigned int time)
ds@1952
   399
{
ds@1952
   400
#if defined MENU_DEBUG
ds@1952
   401
	g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (panel %p) (mh %p) (time %d)",
ds@1952
   402
		client,
ds@1952
   403
		menu,
ds@1952
   404
		mh,
ds@1952
   405
		time);
ds@1952
   406
#endif
ds@1952
   407
	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
ds@1952
   408
	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
ds@1952
   409
	{
ds@1952
   410
		return false;
ds@1952
   411
	}
ds@1952
   412
ds@1952
   413
	CBaseMenuPlayer *player = GetMenuPlayer(client);
ds@1952
   414
	if (player->bAutoIgnore)
ds@1952
   415
	{
ds@1952
   416
		return false;
ds@1952
   417
	}
ds@1952
   418
ds@1952
   419
	/* For the duration of this, we are going to totally ignore whether
ds@1952
   420
	* the player is already in a menu or not (except to cancel the old one).
ds@1952
   421
	* Instead, we are simply going to ignore any further menu displays, so
ds@1952
   422
	* this display can't be interrupted.
ds@1952
   423
	*/
ds@1952
   424
	player->bAutoIgnore = true;
ds@1952
   425
ds@1952
   426
	/* Cancel any old menus */
ds@1952
   427
	menu_states_t &states = player->states;
ds@1952
   428
	if (player->bInMenu)
ds@1952
   429
	{
ds@1952
   430
		_CancelClientMenu(client, MenuCancel_Interrupted, true);
ds@1952
   431
	}
ds@1952
   432
ds@1952
   433
	states.firstItem = 0;
ds@1952
   434
	states.lastItem = 0;
ds@1952
   435
	states.menu = NULL;
ds@1952
   436
	states.mh = mh;
ds@1952
   437
	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
ds@1952
   438
	player->bInMenu = true;
ds@1952
   439
	player->bInExternMenu = false;
ds@1952
   440
	player->menuStartTime = gpGlobals->curtime;
ds@1952
   441
	player->menuHoldTime = time;
ds@1952
   442
ds@1952
   443
	if (time)
ds@1952
   444
	{
ds@1952
   445
		AddClientToWatch(client);
ds@1952
   446
	}
ds@1952
   447
ds@1952
   448
	/* Draw the display */
ds@1952
   449
	SendDisplay(client, menu);
ds@1952
   450
ds@1952
   451
	/* We can be interrupted again! */
ds@1952
   452
	player->bAutoIgnore = false;
ds@1952
   453
ds@1952
   454
	return true;
ds@1952
   455
}
ds@1952
   456
ds@1952
   457
bool BaseMenuStyle::DoClientMenu(int client,
ds@1952
   458
								 CBaseMenu *menu,
ds@1952
   459
								 unsigned int first_item,
ds@1952
   460
								 IMenuHandler *mh,
ds@1952
   461
								 unsigned int time)
ds@1952
   462
{
ds@1952
   463
#if defined MENU_DEBUG
ds@1952
   464
	g_Logger.LogMessage("[SM_MENU] DoClientMenu() (client %d) (menu %p) (mh %p) (time %d)",
ds@1952
   465
		client,
ds@1952
   466
		menu,
ds@1952
   467
		mh,
ds@1952
   468
		time);
ds@1952
   469
#endif
ds@1952
   470
	mh->OnMenuStart(menu);
ds@1952
   471
ds@1952
   472
	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
ds@1952
   473
	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
ds@1952
   474
	{
ds@1952
   475
#if defined MENU_DEBUG
ds@1952
   476
		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to display to client %d", client);
ds@1952
   477
#endif
ds@1952
   478
		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
ds@1952
   479
		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
ds@1952
   480
		return false;
ds@1952
   481
	}
ds@1952
   482
ds@1952
   483
	CBaseMenuPlayer *player = GetMenuPlayer(client);
ds@1952
   484
	if (player->bAutoIgnore)
ds@1952
   485
	{
ds@1952
   486
#if defined MENU_DEBUG
ds@1952
   487
		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Client %d is autoIgnoring", client);
ds@1952
   488
#endif
ds@1952
   489
		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
ds@1952
   490
		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
ds@1952
   491
		return false;
ds@1952
   492
	}
ds@1952
   493
ds@1952
   494
	/* For the duration of this, we are going to totally ignore whether
ds@1952
   495
	 * the player is already in a menu or not (except to cancel the old one).
ds@1952
   496
	 * Instead, we are simply going to ignore any further menu displays, so
ds@1952
   497
	 * this display can't be interrupted.
ds@1952
   498
	 */
ds@1952
   499
	player->bAutoIgnore = true;
ds@1952
   500
ds@1952
   501
	/* Cancel any old menus */
ds@1952
   502
	menu_states_t &states = player->states;
ds@1952
   503
	if (player->bInMenu)
ds@1952
   504
	{
ds@1952
   505
#if defined MENU_DEBUG
ds@1952
   506
		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Cancelling old menu to client %d", client);
ds@1952
   507
#endif
ds@1952
   508
		_CancelClientMenu(client, MenuCancel_Interrupted, true);
ds@1952
   509
	}
ds@1952
   510
ds@1952
   511
	states.firstItem = 0;
ds@1952
   512
	states.lastItem = first_item;
ds@1952
   513
	states.menu = menu;
ds@1952
   514
	states.mh = mh;
ds@1952
   515
	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
ds@1952
   516
ds@1952
   517
	IMenuPanel *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
ds@1952
   518
	if (!display)
ds@1952
   519
	{
ds@1952
   520
#if defined MENU_DEBUG
ds@1952
   521
		g_Logger.LogMessage("[SM_MENU] DoClientMenu(): Failed to render to client %d", client);
ds@1952
   522
#endif
ds@1952
   523
		player->bAutoIgnore = false;
ds@1952
   524
		player->bInMenu = false;
ds@1952
   525
		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
ds@1952
   526
		mh->OnMenuEnd(menu, MenuEnd_Cancelled);
ds@1952
   527
		return false;
ds@1952
   528
	}
ds@1952
   529
ds@1952
   530
	/* Finally, set our states */
ds@1952
   531
	player->bInMenu = true;
ds@1952
   532
	player->bInExternMenu = false;
ds@1952
   533
	player->menuStartTime = gpGlobals->curtime;
ds@1952
   534
	player->menuHoldTime = time;
ds@1952
   535
ds@1952
   536
	if (time)
ds@1952
   537
	{
ds@1952
   538
		AddClientToWatch(client);
ds@1952
   539
	}
ds@1952
   540
ds@1952
   541
	/* Draw the display */
ds@1952
   542
	SendDisplay(client, display);
ds@1952
   543
ds@1952
   544
	/* Free the display pointer */
ds@1952
   545
	display->DeleteThis();
ds@1952
   546
ds@1952
   547
	/* We can be interrupted again! */
ds@1952
   548
	player->bAutoIgnore = false;
ds@1952
   549
ds@1952
   550
#if defined MENU_DEBUG
ds@1952
   551
	g_Logger.LogMessage("[SM_MENU] DoClientMenu() finished successfully (client %d)", client);
ds@1952
   552
#endif
ds@1952
   553
ds@1952
   554
	return true;
ds@1952
   555
}
ds@1952
   556
ds@1952
   557
bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order)
ds@1952
   558
{
ds@1952
   559
#if defined MENU_DEBUG
ds@1952
   560
	g_Logger.LogMessage("[SM_MENU] RedoClientMenu() (client %d) (order %d)", client, order);
ds@1952
   561
#endif
ds@1952
   562
	CBaseMenuPlayer *player = GetMenuPlayer(client);
ds@1952
   563
	menu_states_t &states = player->states;
ds@1952
   564
ds@1952
   565
	player->bAutoIgnore = true;
ds@1952
   566
	IMenuPanel *display = g_Menus.RenderMenu(client, states, order);
ds@1952
   567
	if (!display)
ds@1952
   568
	{
ds@1952
   569
#if defined MENU_DEBUG
ds@1952
   570
		g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Failed to render menu");
ds@1952
   571
#endif
ds@1952
   572
		if (player->menuHoldTime)
ds@1952
   573
		{
ds@1952
   574
			RemoveClientFromWatch(client);
ds@1952
   575
		}
ds@1952
   576
		player->bAutoIgnore = false;
ds@1952
   577
		return false;
ds@1952
   578
	}
ds@1952
   579
ds@1952
   580
	SendDisplay(client, display);
ds@1952
   581
ds@1952
   582
	display->DeleteThis();
ds@1952
   583
ds@1952
   584
	player->bAutoIgnore = false;
ds@1952
   585
ds@1952
   586
#if defined MENU_DEBUG
ds@1952
   587
	g_Logger.LogMessage("[SM_MENU] RedoClientMenu(): Succeeded to client %d", client);
ds@1952
   588
#endif
ds@1952
   589
ds@1952
   590
	return true;
ds@1952
   591
}
ds@1952
   592
ds@1952
   593
CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) : 
ds@1952
   594
m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false), 
ds@1952
   595
m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false), 
ds@1952
   596
m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT)
ds@1952
   597
{
ds@1952
   598
}
ds@1952
   599
ds@1952
   600
CBaseMenu::~CBaseMenu()
ds@1952
   601
{
ds@1952
   602
}
ds@1952
   603
ds@1952
   604
Handle_t CBaseMenu::GetHandle()
ds@1952
   605
{
ds@1952
   606
	if (!m_hHandle)
ds@1952
   607
	{
ds@1952
   608
		m_hHandle = g_Menus.CreateMenuHandle(this, m_pOwner);
ds@1952
   609
	}
ds@1952
   610
ds@1952
   611
	return m_hHandle;
ds@1952
   612
}
ds@1952
   613
ds@1952
   614
bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw)
ds@1952
   615
{
ds@1952
   616
	if (m_Pagination == (unsigned)MENU_NO_PAGINATION
ds@1952
   617
		&& m_items.size() >= m_pStyle->GetMaxPageItems())
ds@1952
   618
	{
ds@1952
   619
		return false;
ds@1952
   620
	}
ds@1952
   621
ds@1952
   622
	CItem item;
ds@1952
   623
ds@1952
   624
	item.infoString = m_Strings.AddString(info);
ds@1952
   625
	if (draw.display)
ds@1952
   626
	{
ds@1952
   627
		item.displayString = m_Strings.AddString(draw.display);
ds@1952
   628
	}
ds@1952
   629
	item.style = draw.style;
ds@1952
   630
ds@1952
   631
	m_items.push_back(item);
ds@1952
   632
ds@1952
   633
	return true;
ds@1952
   634
}
ds@1952
   635
ds@1952
   636
bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw)
ds@1952
   637
{
ds@1952
   638
	if (m_Pagination == (unsigned)MENU_NO_PAGINATION
ds@1952
   639
		&& m_items.size() >= m_pStyle->GetMaxPageItems())
ds@1952
   640
	{
ds@1952
   641
		return false;
ds@1952
   642
	}
ds@1952
   643
ds@1952
   644
	if (position >= m_items.size())
ds@1952
   645
	{
ds@1952
   646
		return false;
ds@1952
   647
	}
ds@1952
   648
ds@1952
   649
	CItem item;
ds@1952
   650
	item.infoString = m_Strings.AddString(info);
ds@1952
   651
	if (draw.display)
ds@1952
   652
	{
ds@1952
   653
		item.displayString = m_Strings.AddString(draw.display);
ds@1952
   654
	}
ds@1952
   655
	item.style = draw.style;
ds@1952
   656
ds@1952
   657
	CVector<CItem>::iterator iter = m_items.iterAt(position);
ds@1952
   658
	m_items.insert(iter, item);
ds@1952
   659
ds@1952
   660
	return true;
ds@1952
   661
}
ds@1952
   662
ds@1952
   663
bool CBaseMenu::RemoveItem(unsigned int position)
ds@1952
   664
{
ds@1952
   665
	if (position >= m_items.size())
ds@1952
   666
	{
ds@1952
   667
		return false;
ds@1952
   668
	}
ds@1952
   669
ds@1952
   670
	m_items.erase(m_items.iterAt(position));
ds@1952
   671
ds@1952
   672
	if (m_items.size() == 0)
ds@1952
   673
	{
ds@1952
   674
		m_Strings.Reset();
ds@1952
   675
	}
ds@1952
   676
ds@1952
   677
	return true;
ds@1952
   678
}
ds@1952
   679
ds@1952
   680
void CBaseMenu::RemoveAllItems()
ds@1952
   681
{
ds@1952
   682
	m_items.clear();
ds@1952
   683
	m_Strings.Reset();
ds@1952
   684
}
ds@1952
   685
ds@1952
   686
const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */)
ds@1952
   687
{
ds@1952
   688
	if (position >= m_items.size())
ds@1952
   689
	{
ds@1952
   690
		return NULL;
ds@1952
   691
	}
ds@1952
   692
ds@1952
   693
	if (draw)
ds@1952
   694
	{
ds@1952
   695
		draw->display = m_Strings.GetString(m_items[position].displayString);
ds@1952
   696
		draw->style = m_items[position].style;
ds@1952
   697
	}
ds@1952
   698
ds@1952
   699
	return m_Strings.GetString(m_items[position].infoString);
ds@1952
   700
}
ds@1952
   701
ds@1952
   702
unsigned int CBaseMenu::GetItemCount()
ds@1952
   703
{
ds@1952
   704
	return m_items.size();
ds@1952
   705
}
ds@1952
   706
ds@1952
   707
bool CBaseMenu::SetPagination(unsigned int itemsPerPage)
ds@1952
   708
{
ds@1952
   709
	if (itemsPerPage > 7 || itemsPerPage == 1)
ds@1952
   710
	{
ds@1952
   711
		return false;
ds@1952
   712
	}
ds@1952
   713
ds@1952
   714
	if (itemsPerPage == MENU_NO_PAGINATION
ds@1952
   715
		&& m_Pagination != MENU_NO_PAGINATION)
ds@1952
   716
	{
ds@1952
   717
		m_nFlags &= ~MENUFLAG_BUTTON_EXIT;
ds@1952
   718
	}
ds@1952
   719
ds@1952
   720
	m_Pagination = itemsPerPage;
ds@1952
   721
ds@1952
   722
	return true;
ds@1952
   723
}
ds@1952
   724
ds@1952
   725
unsigned int CBaseMenu::GetPagination()
ds@1952
   726
{
ds@1952
   727
	return m_Pagination;
ds@1952
   728
}
ds@1952
   729
ds@1952
   730
IMenuStyle *CBaseMenu::GetDrawStyle()
ds@1952
   731
{
ds@1952
   732
	return m_pStyle;
ds@1952
   733
}
ds@1952
   734
ds@1952
   735
void CBaseMenu::SetDefaultTitle(const char *message)
ds@1952
   736
{
ds@1952
   737
	m_Title.assign(message);
ds@1952
   738
}
ds@1952
   739
ds@1952
   740
const char *CBaseMenu::GetDefaultTitle()
ds@1952
   741
{
ds@1952
   742
	return m_Title.c_str();
ds@1952
   743
}
ds@1952
   744
ds@1952
   745
void CBaseMenu::Cancel()
ds@1952
   746
{
ds@1952
   747
	if (m_bCancelling)
ds@1952
   748
	{
ds@1952
   749
		return;
ds@1952
   750
	}
ds@1952
   751
ds@1952
   752
#if defined MENU_DEBUG
ds@1952
   753
	g_Logger.LogMessage("[SM_MENU] CBaseMenu::Cancel(%p) (m_bShouldDelete %d)",
ds@1952
   754
		this,
ds@1952
   755
		m_bShouldDelete);
ds@1952
   756
#endif
ds@1952
   757
ds@1952
   758
	m_bCancelling = true;
ds@1952
   759
	Cancel_Finally();
ds@1952
   760
	m_bCancelling = false;
ds@1952
   761
ds@1952
   762
	if (m_bShouldDelete)
ds@1952
   763
	{
ds@1952
   764
		InternalDelete();
ds@1952
   765
	}
ds@1952
   766
}
ds@1952
   767
ds@1952
   768
void CBaseMenu::Destroy(bool releaseHandle)
ds@1952
   769
{
ds@1952
   770
	/* Check if we shouldn't be here */
ds@1952
   771
	if (m_bDeleting)
ds@1952
   772
	{
ds@1952
   773
		return;
ds@1952
   774
	}
ds@1952
   775
ds@1952
   776
#if defined MENU_DEBUG
ds@1952
   777
	g_Logger.LogMessage("[SM_MENU] CBaseMenu::Destroy(%p) (release %d) (m_bCancelling %d) (m_bShouldDelete %d)",
ds@1952
   778
		this,
ds@1952
   779
		releaseHandle,
ds@1952
   780
		m_bCancelling,
ds@1952
   781
		m_bShouldDelete);
ds@1952
   782
#endif
ds@1952
   783
ds@1952
   784
	/* Save the destruction hint about our handle */
ds@1952
   785
	m_bWillFreeHandle = releaseHandle;
ds@1952
   786
ds@1952
   787
	/* Now actually do stuff */
ds@1952
   788
	if (!m_bCancelling || m_bShouldDelete)
ds@1952
   789
	{
ds@1952
   790
		Cancel();
ds@1952
   791
		InternalDelete();
ds@1952
   792
	} else {
ds@1952
   793
		m_bShouldDelete = true;
ds@1952
   794
	}
ds@1952
   795
}
ds@1952
   796
ds@1952
   797
void CBaseMenu::InternalDelete()
ds@1952
   798
{
ds@1952
   799
	if (m_bWillFreeHandle && m_hHandle != BAD_HANDLE)
ds@1952
   800
	{
ds@1952
   801
		Handle_t hndl = m_hHandle;
ds@1952
   802
		HandleSecurity sec;
ds@1952
   803
ds@1952
   804
		sec.pOwner = m_pOwner;
ds@1952
   805
		sec.pIdentity = g_pCoreIdent;
ds@1952
   806
ds@1952
   807
		m_hHandle = BAD_HANDLE;
ds@1952
   808
		m_bDeleting = true;
ds@1952
   809
		g_HandleSys.FreeHandle(hndl, &sec);
ds@1952
   810
	}
ds@1952
   811
ds@1952
   812
	m_pHandler->OnMenuDestroy(this);
ds@1952
   813
ds@1952
   814
	delete this;
ds@1952
   815
}
ds@1952
   816
ds@1952
   817
unsigned int CBaseMenu::GetMenuOptionFlags()
ds@1952
   818
{
ds@1952
   819
	return m_nFlags;
ds@1952
   820
}
ds@1952
   821
ds@1952
   822
void CBaseMenu::SetMenuOptionFlags(unsigned int flags)
ds@1952
   823
{
ds@1952
   824
	m_nFlags = flags;
ds@1952
   825
}
ds@1952
   826
ds@1952
   827
IMenuHandler *CBaseMenu::GetHandler()
ds@1952
   828
{
ds@1952
   829
	return m_pHandler;
ds@1952
   830
}
ds@1952
   831
ds@1952
   832
unsigned int CBaseMenu::GetBaseMemUsage()
ds@1952
   833
{
ds@1952
   834
	return m_Title.size() 
ds@1952
   835
		+ m_Strings.GetMemTable()->GetMemUsage()
ds@1952
   836
		+ (m_items.size() * sizeof(CItem));
ds@1952
   837
}