core/MenuStyle_Valve.cpp
author David Anderson <dvander@alliedmods.net>
Sat May 12 23:45:52 2007 +0000 (2007-05-12)
changeset 769 d7dc79f1f93e
child 772 ff6662122b97
permissions -rw-r--r--
behold with your eyes and view upon what are MENUS
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include "sm_stringutil.h"
    16 #include "PlayerManager.h"
    17 #include "MenuStyle_Valve.h"
    18 #include "Translator.h"
    19 #include "PlayerManager.h"
    20 #include "ConCmdManager.h"
    21 
    22 SH_DECL_HOOK4_void(IServerPluginHelpers, CreateMessage, SH_NOATTRIB, false, edict_t *, DIALOG_TYPE, KeyValues *, IServerPluginCallbacks *);
    23 
    24 ValveMenuStyle g_ValveMenuStyle;
    25 const char *g_OptionNumTable[];
    26 const char *g_OptionCmdTable[];
    27 IServerPluginCallbacks *g_pVSPHandle = NULL;
    28 CallClass<IServerPluginHelpers> *g_pSPHCC = NULL;
    29 
    30 class TestHandler : public IMenuHandler
    31 {
    32 public:
    33 	virtual void OnMenuEnd(IBaseMenu *menu)
    34 	{
    35 		menu->Destroy();
    36 	}
    37 };
    38 
    39 ValveMenuStyle::ValveMenuStyle() : m_players(new CValveMenuPlayer[256+1]), m_WatchList(256)
    40 {
    41 }
    42 
    43 bool ValveMenuStyle::OnClientCommand(int client)
    44 {
    45 	const char *cmd = engine->Cmd_Argv(0);
    46 
    47 	if (strcmp(cmd, "sm_vmenuselect") == 0)
    48 	{
    49 		int key_press = atoi(engine->Cmd_Argv(1));
    50 		g_ValveMenuStyle.ClientPressedKey(client, key_press);
    51 		return true;
    52 	}
    53 
    54 	if (strcmp(cmd, "sm_test") == 0)
    55 	{
    56 		IBaseMenu *menu = g_ValveMenuStyle.CreateMenu();
    57 		menu->AppendItem("test1", ItemDrawInfo("Test #1", 0));
    58 		menu->AppendItem("test2", ItemDrawInfo("Test #2", 0));
    59 		menu->AppendItem("test3", ItemDrawInfo("Test #3", 0));
    60 		menu->AppendItem("test4", ItemDrawInfo("Test #4", 0));
    61 		menu->AppendItem("test5", ItemDrawInfo("Test #5", 0));
    62 		menu->AppendItem("test6", ItemDrawInfo("Test #6", 0));
    63 		menu->AppendItem("test7", ItemDrawInfo("Test #7", 0));
    64 		menu->AppendItem("test8", ItemDrawInfo("Test #8", 0));
    65 		menu->AppendItem("test9", ItemDrawInfo("Test #9", 0));
    66 		menu->AppendItem("test10", ItemDrawInfo("Test #10", 0));
    67 		menu->AppendItem("test11", ItemDrawInfo("Test #11", 0));
    68 		menu->AppendItem("test12", ItemDrawInfo("Test #12", 0));
    69 		menu->AppendItem("test13", ItemDrawInfo("Test #13", 0));
    70 		menu->AppendItem("test14", ItemDrawInfo("Test #14", 0));
    71 		menu->AppendItem("test15", ItemDrawInfo("Test #15", 0));
    72 		menu->AppendItem("test16", ItemDrawInfo("Test #16", 0));
    73 		menu->AppendItem("test17", ItemDrawInfo("Test #17", 0));
    74 		menu->AppendItem("test18", ItemDrawInfo("Test #18", 0));
    75 		menu->AppendItem("test19", ItemDrawInfo("Test #19", 0));
    76 		menu->AppendItem("test20", ItemDrawInfo("Test #20", 0));
    77 
    78 		menu->Display(client, new TestHandler, 20);
    79 		return true;
    80 	} else if (strcmp(cmd, "gaben") == 0) {
    81 		KeyValues *kv = new KeyValues("menu");
    82 		kv->SetString("msg", "hi");
    83 		serverpluginhelpers->CreateMessage(engine->PEntityOfEntIndex(client), DIALOG_MENU, kv, g_pVSPHandle);
    84 		kv->deleteThis();
    85 	}
    86 
    87 	return false;
    88 }
    89 
    90 void ValveMenuStyle::OnSourceModAllInitialized()
    91 {
    92 	g_Players.AddClientListener(this);
    93 	SH_ADD_HOOK_MEMFUNC(IServerPluginHelpers, CreateMessage, serverpluginhelpers, this, &ValveMenuStyle::HookCreateMessage, false);
    94 	g_pSPHCC = SH_GET_CALLCLASS(serverpluginhelpers);
    95 }
    96 
    97 void ValveMenuStyle::OnSourceModShutdown()
    98 {
    99 	SH_RELEASE_CALLCLASS(g_pSPHCC);
   100 	SH_REMOVE_HOOK_MEMFUNC(IServerPluginHelpers, CreateMessage, serverpluginhelpers, this, &ValveMenuStyle::HookCreateMessage, false);
   101 	g_Players.RemoveClientListener(this);
   102 }
   103 
   104 void ValveMenuStyle::OnClientDisconnected(int client)
   105 {
   106 	CValveMenuPlayer *player = &m_players[client];
   107 	if (!player->bInMenu)
   108 	{
   109 		return;
   110 	}
   111 
   112 	menu_states_t &states = player->states;
   113 	states.mh->OnMenuCancel(states.menu, client, MenuCancel_Disconnect);
   114 	states.mh->OnMenuEnd(states.menu);
   115 
   116 	if (player->menuHoldTime)
   117 	{
   118 		m_WatchList.remove(client);
   119 	}
   120 
   121 	player->bInMenu = false;
   122 }
   123 
   124 void ValveMenuStyle::HookCreateMessage(edict_t *pEdict,
   125 									   DIALOG_TYPE type,
   126 									   KeyValues *kv,
   127 									   IServerPluginCallbacks *plugin)
   128 {
   129 	if (type != DIALOG_MENU)
   130 	{
   131 		return;
   132 	}
   133 
   134 	int client = engine->IndexOfEdict(pEdict);
   135 	if (client < 1 || client > 256)
   136 	{
   137 		return;
   138 	}
   139 
   140 	CValveMenuPlayer *player = &m_players[client];
   141 	
   142 	/* We don't care if the player is in a menu because, for all intents and purposes,
   143 	 * the menu is completely private.  Instead, we just figure out the level we'll need
   144 	 * in order to override it.
   145 	 */
   146 	player->curPrioLevel = kv->GetInt("level", player->curPrioLevel);
   147 
   148 	/* Oh no! What happens if we got a menu that overwrites ours?! */
   149 	if (player->bInMenu)
   150 	{
   151 		/* Okay, let the external menu survive for now.  It may live another 
   152 		 * day to avenge its grandfather, killed in the great Menu Interruption
   153 		 * battle.
   154 		 */
   155 		_CancelMenu(client, true);
   156 	}
   157 }
   158 
   159 void ValveMenuStyle::OnSourceModVSPReceived(IServerPluginCallbacks *iface)
   160 {
   161 	g_pVSPHandle = iface;
   162 }
   163 
   164 IMenuDisplay *ValveMenuStyle::CreateDisplay()
   165 {
   166 	return new CValveMenuDisplay();
   167 }
   168 
   169 IBaseMenu *ValveMenuStyle::CreateMenu()
   170 {
   171 	return new CValveMenu();
   172 }
   173 
   174 const char *ValveMenuStyle::GetStyleName()
   175 {
   176 	return "valve";
   177 }
   178 
   179 unsigned int ValveMenuStyle::GetMaxPageItems()
   180 {
   181 	return 8;
   182 }
   183 
   184 static int do_lookup[256];
   185 void ValveMenuStyle::ProcessWatchList()
   186 {
   187 	if (!m_WatchList.size())
   188 	{
   189 		return;
   190 	}
   191 
   192 	FastLink<int>::iterator iter;
   193 
   194 	unsigned int total = 0;
   195 	for (iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter)
   196 	{
   197 		do_lookup[total++] = (*iter);
   198 	}
   199 
   200 	int client;
   201 	CValveMenuPlayer *player;
   202 	float curTime = gpGlobals->curtime;
   203 	for (unsigned int i=0; i<total; i++)
   204 	{
   205 		client = do_lookup[i];
   206 		player = &m_players[client];
   207 		if (!player->bInMenu || !player->menuHoldTime)
   208 		{
   209 			m_WatchList.remove(i);
   210 			continue;
   211 		}
   212 		if (curTime > player->menuStartTime + player->menuHoldTime)
   213 		{
   214 			_CancelMenu(i, false);
   215 		}
   216 	}
   217 }
   218 
   219 void ValveMenuStyle::_CancelMenu(int client, bool bAutoIgnore)
   220 {
   221 	CValveMenuPlayer *player = &m_players[client];
   222 	menu_states_t &states = player->states;
   223 
   224 	bool bOldIgnore = player->bAutoIgnore;
   225 	if (bAutoIgnore)
   226 	{
   227 		player->bAutoIgnore = true;
   228 	}
   229 
   230 	/* Save states */
   231 	IMenuHandler *mh = states.mh;
   232 	IBaseMenu *menu = states.menu;
   233 
   234 	/* Clear menu */
   235 	player->bInMenu = false;
   236 	if (player->menuHoldTime)
   237 	{
   238 		m_WatchList.remove(client);
   239 	}
   240 
   241 	/* Fire callbacks */
   242 	mh->OnMenuCancel(menu, client, MenuCancel_Interrupt);
   243 	mh->OnMenuEnd(menu);
   244 
   245 	if (bAutoIgnore)
   246 	{
   247 		player->bAutoIgnore = bOldIgnore;
   248 	}
   249 }
   250 
   251 void ValveMenuStyle::CancelMenu(CValveMenu *menu)
   252 {
   253 	int maxClients = g_Players.GetMaxClients();
   254 	for (int i=1; i<=maxClients; i++)
   255 	{
   256 		if (m_players[i].bInMenu)
   257 		{
   258 			menu_states_t &states = m_players[i].states;
   259 			if (states.menu == menu)
   260 			{
   261 				_CancelMenu(i);
   262 			}
   263 		}
   264 	}
   265 }
   266 
   267 bool ValveMenuStyle::CancelClientMenu(int client, bool autoIgnore)
   268 {
   269 	if (client < 1 || client > 256 || !m_players[client].bInMenu)
   270 	{
   271 		return false;
   272 	}
   273 
   274 	_CancelMenu(client, autoIgnore);
   275 
   276 	return true;
   277 }
   278 
   279 
   280 void ValveMenuStyle::ClientPressedKey(int client, unsigned int key_press)
   281 {
   282 	CValveMenuPlayer *player = &m_players[client];
   283 
   284 	/* First question: Are we in a menu? */
   285 	if (!player->bInMenu)
   286 	{
   287 		return;
   288 	}
   289 
   290 	bool cancel = false;
   291 	unsigned int item = 0;
   292 	MenuCancelReason reason = MenuCancel_Exit;
   293 	menu_states_t &states = player->states;
   294 
   295 	assert(states.mh != NULL);
   296 
   297 	if (states.menu == NULL)
   298 	{
   299 		item = key_press;
   300 	} else if (key_press < 1 ||  key_press > 8) {
   301 		cancel = true;
   302 	} else {
   303 		ItemSelection type = states.slots[key_press].type;
   304 
   305 		/* For navigational items, we're going to redisplay */
   306 		if (type == ItemSel_Back)
   307 		{
   308 			if (!RedoClientMenu(client, ItemOrder_Descending))
   309 			{
   310 				cancel = true;
   311 				reason = MenuCancel_NoDisplay;
   312 			} else {
   313 				return;
   314 			}
   315 		} else if (type == ItemSel_Next) {
   316 			if (!RedoClientMenu(client, ItemOrder_Ascending))
   317 			{
   318 				cancel = true;						/* I like Saltines. */
   319 				reason = MenuCancel_NoDisplay;
   320 			} else {
   321 				return;
   322 			}
   323 		} else if (type == ItemSel_Exit || type == ItemSel_None) {
   324 			cancel = true;
   325 		} else {
   326 			item = states.slots[key_press].item;
   327 		}
   328 	}
   329 
   330 	/* Save variables */
   331 	IMenuHandler *mh = states.mh;
   332 	IBaseMenu *menu = states.menu;
   333 
   334 	/* Clear states */
   335 	player->bInMenu = false;
   336 	if (player->menuHoldTime)
   337 	{
   338 		m_WatchList.remove(client);
   339 	}
   340 
   341 	if (cancel)
   342 	{
   343 		mh->OnMenuCancel(menu, client, reason);
   344 	} else {
   345 		mh->OnMenuSelect(menu, client, item);
   346 	}
   347 
   348 	mh->OnMenuEnd(menu);
   349 }
   350 
   351 bool ValveMenuStyle::DoClientMenu(int client, CValveMenuDisplay *menu, IMenuHandler *mh, unsigned int time)
   352 {
   353 	if (!g_pVSPHandle || !mh)
   354 	{
   355 		return false;
   356 	}
   357 
   358 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   359 	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
   360 	{
   361 		return false;
   362 	}
   363 
   364 	CValveMenuPlayer *player = &m_players[client];
   365 	if (player->bAutoIgnore)
   366 	{
   367 		return false;
   368 	}
   369 
   370 	/* For the duration of this, we are going to totally ignore whether
   371 	 * the player is already in a menu or not (except to cancel the old one).
   372 	 * Instead, we are simply going to ignore any further menu displays, so
   373 	 * this display can't be interrupted.
   374 	 */
   375 	player->bAutoIgnore = true;
   376 
   377 	/* Cancel any old menus */
   378 	menu_states_t &states = player->states;
   379 	if (player->bInMenu)
   380 	{
   381 		/* We need to cancel the old menu */
   382 		if (player->menuHoldTime)
   383 		{
   384 			m_WatchList.remove(client);
   385 		}
   386 		states.mh->OnMenuCancel(states.menu, client, MenuCancel_Interrupt);
   387 		states.mh->OnMenuEnd(states.menu);
   388 	}
   389 
   390 	states.firstItem = 0;
   391 	states.lastItem = 0;
   392 	states.menu = NULL;
   393 	states.mh = mh;
   394 	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
   395 	player->curPrioLevel--;
   396 	player->bInMenu = true;
   397 	player->menuStartTime = gpGlobals->curtime;
   398 	player->menuHoldTime = time;
   399 
   400 	if (time)
   401 	{
   402 		m_WatchList.push_back(client);
   403 	}
   404 
   405 	/* Draw the display */
   406 	menu->SendRawDisplay(client, player->curPrioLevel, time);
   407 
   408 	/* We can be interrupted again! */
   409 	player->bAutoIgnore = false;
   410 
   411 	return true;
   412 }
   413 
   414 bool ValveMenuStyle::DoClientMenu(int client, CValveMenu *menu, IMenuHandler *mh, unsigned int time)
   415 {
   416 	mh->OnMenuStart(menu);
   417 
   418 	if (!g_pVSPHandle || !mh)
   419 	{
   420 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   421 		mh->OnMenuEnd(menu);
   422 		return false;
   423 	}
   424 
   425 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
   426 	if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame())
   427 	{
   428 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   429 		mh->OnMenuEnd(menu);
   430 		return false;
   431 	}
   432 
   433 	CValveMenuPlayer *player = &m_players[client];
   434 	if (player->bAutoIgnore)
   435 	{
   436 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   437 		mh->OnMenuEnd(menu);
   438 		return false;
   439 	}
   440 
   441 	/* For the duration of this, we are going to totally ignore whether
   442 	 * the player is already in a menu or not (except to cancel the old one).
   443 	 * Instead, we are simply going to ignore any further menu displays, so
   444 	 * this display can't be interrupted.
   445 	 */
   446 	player->bAutoIgnore = true;
   447 
   448 	/* Cancel any old menus */
   449 	menu_states_t &states = player->states;
   450 	if (player->bInMenu)
   451 	{
   452 		/* We need to cancel the old menu */
   453 		if (player->menuHoldTime)
   454 		{
   455 			m_WatchList.remove(client);
   456 		}
   457 		states.mh->OnMenuCancel(states.menu, client, MenuCancel_Interrupt);
   458 		states.mh->OnMenuEnd(states.menu);
   459 	}
   460 
   461 	states.firstItem = 0;
   462 	states.lastItem = 0;
   463 	states.menu = menu;
   464 	states.mh = mh;
   465 	states.apiVers = SMINTERFACE_MENUMANAGER_VERSION;
   466 
   467 	IMenuDisplay *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending);
   468 	if (!display)
   469 	{
   470 		player->bAutoIgnore = false;
   471 		player->bInMenu = false;
   472 		mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay);
   473 		mh->OnMenuEnd(menu);
   474 		return false;
   475 	}
   476 
   477 	/* Finally, set our states */
   478 	player->curPrioLevel--;
   479 	player->bInMenu = true;
   480 	player->menuStartTime = gpGlobals->curtime;
   481 	player->menuHoldTime = time;
   482 
   483 	if (time)
   484 	{
   485 		m_WatchList.push_back(client);
   486 	}
   487 
   488 	/* Draw the display */
   489 	CValveMenuDisplay *vDisplay = (CValveMenuDisplay *)display;
   490 	vDisplay->SendRawDisplay(client, player->curPrioLevel, time);
   491 
   492 	/* Free the display pointer */
   493 	delete display;
   494 
   495 	/* We can be interrupted again! */
   496 	player->bAutoIgnore = false;
   497 
   498 	return true;
   499 }
   500 
   501 bool ValveMenuStyle::RedoClientMenu(int client, ItemOrder order)
   502 {
   503 	CValveMenuPlayer *player = &m_players[client];
   504 	menu_states_t &states = player->states;
   505 
   506 	player->bAutoIgnore = true;
   507 	IMenuDisplay *display = g_Menus.RenderMenu(client, states, order);
   508 	if (!display)
   509 	{
   510 		if (player->menuHoldTime)
   511 		{
   512 			m_WatchList.remove(client);
   513 		}
   514 		player->bAutoIgnore = false;
   515 		return false;
   516 	}
   517 
   518 	CValveMenuDisplay *vDisplay = (CValveMenuDisplay *)display;
   519 	vDisplay->SendRawDisplay(client, --player->curPrioLevel, player->menuHoldTime);
   520 
   521 	delete display;
   522 
   523 	player->bAutoIgnore = false;
   524 
   525 	return true;
   526 }
   527 
   528 MenuSource ValveMenuStyle::GetClientMenu(int client, void **object)
   529 {
   530 	if (client < 1 || client > 256 || !m_players[client].bInMenu)
   531 	{
   532 		return MenuSource_None;
   533 	}
   534 
   535 	IBaseMenu *menu;
   536 	if ((menu=m_players[client].states.menu) != NULL)
   537 	{
   538 		if (object)
   539 		{
   540 			*object = menu;
   541 		}
   542 		return MenuSource_BaseMenu;
   543 	}
   544 
   545 	return MenuSource_Display;
   546 }
   547 
   548 CValveMenuDisplay::CValveMenuDisplay()
   549 {
   550 	m_pKv = NULL;
   551 	Reset();
   552 }
   553 
   554 CValveMenuDisplay::CValveMenuDisplay(CValveMenu *pMenu)
   555 {
   556 	m_pKv = NULL;
   557 	Reset();
   558 	m_pKv->SetColor("color", pMenu->m_IntroColor);
   559 	m_pKv->SetString("title", pMenu->m_IntroMsg);
   560 }
   561 
   562 CValveMenuDisplay::~CValveMenuDisplay()
   563 {
   564 	m_pKv->deleteThis();
   565 }
   566 
   567 IMenuStyle *CValveMenuDisplay::GetParentStyle()
   568 {
   569 	return &g_ValveMenuStyle;
   570 }
   571 
   572 void CValveMenuDisplay::Reset()
   573 {
   574 	if (m_pKv)
   575 	{
   576 		m_pKv->deleteThis();
   577 	}
   578 	m_pKv = new KeyValues("menu");
   579 	m_NextPos = 1;
   580 	m_TitleDrawn = false;
   581 }
   582 
   583 bool CValveMenuDisplay::SetExtOption(MenuOption option, const void *valuePtr)
   584 {
   585 	if (option == MenuOption_IntroMessage)
   586 	{
   587 		m_pKv->SetString("title", (const char *)valuePtr);
   588 		return true;
   589 	} else if (option == MenuOption_IntroColor) {
   590 		int *array = (int *)valuePtr;
   591 		m_pKv->SetColor("color", Color(array[0], array[1], array[2], array[3]));
   592 		return true;
   593 	}
   594 
   595 	return false;
   596 }
   597 
   598 bool CValveMenuDisplay::CanDrawItem(unsigned int drawFlags)
   599 {
   600 	/**
   601 	 * ITEMDRAW_RAWLINE - We can't draw random text, and this doesn't add a slot,
   602 	 *  so it's completely safe to ignore it.
   603 	 * -----------------------------------------
   604 	 */
   605 	if (drawFlags & ITEMDRAW_RAWLINE)
   606 	{
   607 		return false;
   608 	}
   609 
   610 	/**
   611 	 * Special cases, explained in DrawItem()
   612 	 */
   613 	if ((drawFlags & ITEMDRAW_NOTEXT)
   614 		|| (drawFlags & ITEMDRAW_SPACER))
   615 	{
   616 		return true;
   617 	}
   618 
   619 	/**
   620 	 * We can't draw disabled text.  We could bump the position, but we
   621 	 * want DirectDraw() to find some actual items to display.
   622 	 */
   623 	if (drawFlags & ITEMDRAW_DISABLED)
   624 	{
   625 		return false;
   626 	}
   627 
   628 	return true;
   629 }
   630 
   631 unsigned int CValveMenuDisplay::DrawItem(const ItemDrawInfo &item)
   632 {
   633 	if (m_NextPos > 9 || !CanDrawItem(item.style))
   634 	{
   635 		return 0;
   636 	}
   637 
   638 	/**
   639 	 * For these cases we can't draw anything at all, but
   640 	 * we can at least bump the position since we were explicitly asked to.
   641 	 */
   642 	if ((item.style & ITEMDRAW_NOTEXT)
   643 		|| (item.style & ITEMDRAW_SPACER))
   644 	{
   645 		return m_NextPos++;
   646 	}
   647 
   648 	char buffer[255];
   649 	UTIL_Format(buffer, sizeof(buffer), "%d. %s", m_NextPos, item.display);
   650 
   651 	KeyValues *ki = m_pKv->FindKey(g_OptionNumTable[m_NextPos], true);
   652 	ki->SetString("command", g_OptionCmdTable[m_NextPos]);
   653 	ki->SetString("msg", buffer);
   654 
   655 	return m_NextPos++;
   656 }
   657 
   658 void CValveMenuDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */)
   659 {
   660 	if (onlyIfEmpty && m_TitleDrawn)
   661 	{
   662 		return;
   663 	}
   664 
   665 	m_pKv->SetString("msg", text);
   666 	m_TitleDrawn = true;
   667 }
   668 
   669 bool CValveMenuDisplay::DrawRawLine(const char *rawline)
   670 {
   671 	return false;
   672 }
   673 
   674 void CValveMenuDisplay::SendRawDisplay(int client, int priority, unsigned int time)
   675 {
   676 	m_pKv->SetInt("level", priority);
   677 	m_pKv->SetInt("time", time);
   678 
   679 	SH_CALL(g_pSPHCC, &IServerPluginHelpers::CreateMessage)(
   680 		engine->PEntityOfEntIndex(client),
   681 		DIALOG_MENU,
   682 		m_pKv,
   683 		g_pVSPHandle);
   684 }
   685 
   686 bool CValveMenuDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned int time)
   687 {
   688 	return g_ValveMenuStyle.DoClientMenu(client, this, handler, time);
   689 }
   690 
   691 CValveMenu::CValveMenu() : CBaseMenu(&g_ValveMenuStyle), 
   692 	m_IntroColor(255, 0, 0, 255), m_bShouldDelete(false), m_bCancelling(false)
   693 {
   694 	strcpy(m_IntroMsg, "You have a menu, press ESC");
   695 	m_Pagination = 5;
   696 }
   697 
   698 void CValveMenu::Cancel()
   699 {
   700 	if (m_bCancelling)
   701 	{
   702 		return;
   703 	}
   704 
   705 	m_bCancelling = true;
   706 	g_ValveMenuStyle.CancelMenu(this);
   707 	m_bCancelling = false;
   708 
   709 	if (m_bShouldDelete)
   710 	{
   711 		delete this;
   712 	}
   713 }
   714 
   715 void CValveMenu::Destroy()
   716 {
   717 	if (!m_bCancelling || m_bShouldDelete)
   718 	{
   719 		Cancel();
   720 		delete this;
   721 	} else {
   722 		m_bShouldDelete = true;
   723 	}
   724 }
   725 
   726 bool CValveMenu::SetPagination(unsigned int itemsPerPage)
   727 {
   728 	if (itemsPerPage < 1 || itemsPerPage > 5)
   729 	{
   730 		return false;
   731 	}
   732 
   733 	CBaseMenu::SetPagination(itemsPerPage);
   734 
   735 	return true;
   736 }
   737 
   738 bool CValveMenu::SetExtOption(MenuOption option, const void *valuePtr)
   739 {
   740 	if (option == MenuOption_IntroMessage)
   741 	{
   742 		strncopy(m_IntroMsg, (const char *)valuePtr, sizeof(m_IntroMsg));
   743 		return true;
   744 	} else if (option == MenuOption_IntroColor) {
   745 		unsigned int *array = (unsigned int *)valuePtr;
   746 		m_IntroColor = Color(array[0], array[1], array[2], array[3]);
   747 		return true;
   748 	}
   749 
   750 	return false;
   751 }
   752 
   753 bool CValveMenu::Display(int client, IMenuHandler *handler, unsigned int time)
   754 {
   755 	if (m_bCancelling)
   756 	{
   757 		return false;
   758 	}
   759 
   760 	return g_ValveMenuStyle.DoClientMenu(client, this, handler, time);
   761 }
   762 
   763 IMenuDisplay *CValveMenu::CreateDisplay()
   764 {
   765 	return new CValveMenuDisplay(this);
   766 }
   767 
   768 bool CValveMenu::GetExitButton()
   769 {
   770 	return true;
   771 }
   772 
   773 bool CValveMenu::SetExitButton(bool set)
   774 {
   775 	return false;
   776 }
   777 
   778 static const char *g_OptionNumTable[11] = 
   779 {
   780 	"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"
   781 };
   782 
   783 static const char *g_OptionCmdTable[11] = 
   784 {
   785 	"sm_vmenuselect 0", /* INVALID! */
   786 	"sm_vmenuselect 1",
   787 	"sm_vmenuselect 2",
   788 	"sm_vmenuselect 3",
   789 	"sm_vmenuselect 4",
   790 	"sm_vmenuselect 5",
   791 	"sm_vmenuselect 6",
   792 	"sm_vmenuselect 7",
   793 	"sm_vmenuselect 8",
   794 	"sm_vmenuselect 9",
   795 	"sm_vmenuselect 10"
   796 };