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