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