core/MenuManager.cpp
author David Anderson <dvander@alliedmods.net>
Tue Dec 04 15:58:07 2007 +0000 (2007-12-04)
changeset 1764 e4f24e9789fd
parent 1653 ad81268fb2f2
child 1766 d30c252089be
permissions -rw-r--r--
- removed ancient bcompat shim for translations/core.cfg file
- fixed msvc8 whining about BfReadString
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <time.h>
    33 #include <stdarg.h>
    34 #include <stdlib.h>
    35 #include "MenuManager.h"
    36 #include "MenuVoting.h"
    37 #include "sm_stringutil.h"
    38 #include "sourcemm_api.h"
    39 #include "PlayerManager.h"
    40 #include "MenuStyle_Valve.h"
    41 #include "ShareSys.h"
    42 #include "HandleSys.h"
    43 #include "sourcemm_api.h"
    44 
    45 MenuManager g_Menus;
    46 VoteMenuHandler s_VoteHandler;
    47 
    48 ConVar sm_menu_sounds("sm_menu_sounds", "1", 0, "Sets whether SourceMod menus play trigger sounds");
    49 
    50 MenuManager::MenuManager()
    51 {
    52 	m_Styles.push_back(&g_ValveMenuStyle);
    53 	SetDefaultStyle(&g_ValveMenuStyle);
    54 }
    55 
    56 void MenuManager::OnSourceModAllInitialized()
    57 {
    58 	g_ShareSys.AddInterface(NULL, this);
    59 
    60 	HandleAccess access;
    61 	g_HandleSys.InitAccessDefaults(NULL, &access);
    62 
    63 	/* Deny cloning to menus */
    64 	access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
    65 	m_MenuType = g_HandleSys.CreateType("IBaseMenu", this, 0, NULL, &access, g_pCoreIdent, NULL);
    66 
    67 	/* Also deny deletion to styles */
    68 	access.access[HandleAccess_Delete] = HANDLE_RESTRICT_OWNER|HANDLE_RESTRICT_IDENTITY;
    69 	m_StyleType = g_HandleSys.CreateType("IMenuStyle", this, 0, NULL, &access, g_pCoreIdent, NULL);
    70 }
    71 
    72 void MenuManager::OnSourceModAllShutdown()
    73 {
    74 	g_HandleSys.RemoveType(m_MenuType, g_pCoreIdent);
    75 	g_HandleSys.RemoveType(m_StyleType, g_pCoreIdent);
    76 }
    77 
    78 void MenuManager::OnHandleDestroy(HandleType_t type, void *object)
    79 {
    80 	if (type == m_MenuType)
    81 	{
    82 		IBaseMenu *menu = (IBaseMenu *)object;
    83 		menu->Destroy(false);
    84 	} else if (type == m_StyleType) {
    85 		/* Do nothing */
    86 	}
    87 }
    88 
    89 Handle_t MenuManager::CreateMenuHandle(IBaseMenu *menu, IdentityToken_t *pOwner)
    90 {
    91 	if (m_MenuType == NO_HANDLE_TYPE)
    92 	{
    93 		return BAD_HANDLE;
    94 	}
    95 
    96 	return g_HandleSys.CreateHandle(m_MenuType, menu, pOwner, g_pCoreIdent, NULL);
    97 }
    98 
    99 Handle_t MenuManager::CreateStyleHandle(IMenuStyle *style)
   100 {
   101 	if (m_StyleType == NO_HANDLE_TYPE)
   102 	{
   103 		return BAD_HANDLE;
   104 	}
   105 
   106 	return g_HandleSys.CreateHandle(m_StyleType, style, g_pCoreIdent, g_pCoreIdent, NULL);
   107 }
   108 
   109 HandleError MenuManager::ReadMenuHandle(Handle_t handle, IBaseMenu **menu)
   110 {
   111 	HandleSecurity sec;
   112 
   113 	sec.pIdentity = g_pCoreIdent;
   114 	sec.pOwner = NULL;
   115 
   116 	return g_HandleSys.ReadHandle(handle, m_MenuType, &sec, (void **)menu);
   117 }
   118 
   119 HandleError MenuManager::ReadStyleHandle(Handle_t handle, IMenuStyle **style)
   120 {
   121 	HandleSecurity sec;
   122 
   123 	sec.pIdentity = g_pCoreIdent;
   124 	sec.pOwner = g_pCoreIdent;
   125 
   126 	return g_HandleSys.ReadHandle(handle, m_StyleType, &sec, (void **)style);
   127 }
   128 
   129 bool MenuManager::SetDefaultStyle(IMenuStyle *style)
   130 {
   131 	if (!style)
   132 	{
   133 		return false;
   134 	}
   135 
   136 	m_pDefaultStyle = style;
   137 
   138 	return true;
   139 }
   140 
   141 IMenuStyle *MenuManager::GetStyle(unsigned int index)
   142 {
   143 	if (index >= GetStyleCount())
   144 	{
   145 		return NULL;
   146 	}
   147 
   148 	return m_Styles[index];
   149 }
   150 
   151 void MenuManager::AddStyle(IMenuStyle *style)
   152 {
   153 	m_Styles.push_back(style);
   154 }
   155 
   156 unsigned int MenuManager::GetStyleCount()
   157 {
   158 	return (unsigned int)m_Styles.size();
   159 }
   160 
   161 IMenuStyle *MenuManager::FindStyleByName(const char *name)
   162 {
   163 	unsigned int count = GetStyleCount();
   164 	for (unsigned int i=0; i<count; i++)
   165 	{
   166 		IMenuStyle *ptr = GetStyle(i);
   167 		if (strcasecmp(ptr->GetStyleName(), name) == 0)
   168 		{
   169 			return ptr;
   170 		}
   171 	}
   172 
   173 	return NULL;
   174 }
   175 
   176 inline bool IsSlotItem(IMenuPanel *display,
   177 					   unsigned int style)
   178 {
   179 	if (!display->CanDrawItem(style))
   180 	{
   181 	return false;
   182 	}
   183 	if ((style & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE)
   184 	{
   185 		return false;
   186 	}
   187 	if (style & ITEMDRAW_RAWLINE)
   188 	{
   189 		return false;
   190 	}
   191 
   192 	return true;
   193 }
   194 
   195 IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder order)
   196 {
   197 	IBaseMenu *menu = md.menu;
   198 
   199 	if (!menu)
   200 	{
   201 		return NULL;
   202 	}
   203 
   204 	struct
   205 	{
   206 		unsigned int position;
   207 		ItemDrawInfo draw;
   208 	} drawItems[10];
   209 
   210 	/* Figure out how many items to draw */
   211 	IMenuStyle *style = menu->GetDrawStyle();
   212 	unsigned int pgn = menu->GetPagination();
   213 	unsigned int maxItems = style->GetMaxPageItems();
   214 	bool exitButton = (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT;
   215 
   216 	if (pgn != MENU_NO_PAGINATION)
   217 	{
   218 		maxItems = pgn;
   219 	}
   220 	else if (exitButton)
   221 	{
   222 		maxItems--;
   223 	}
   224 
   225 	/* This is very not allowed! */
   226 	if (maxItems < 2)
   227 	{
   228 		return NULL;
   229 	}
   230 
   231 	unsigned int totalItems = menu->GetItemCount();
   232 	unsigned int startItem = 0;
   233 
   234 	/* For pagination, find the starting point. */
   235 	if (pgn != MENU_NO_PAGINATION)
   236 	{
   237 		if (order == ItemOrder_Ascending)
   238 		{
   239 			startItem = md.lastItem;
   240 			/* This shouldn't happen with well-coded menus.
   241 			 * If the item is out of bounds, switch the order to
   242 			 * Items_Descending and make us start from the top.
   243 			 */
   244 			if (startItem >= totalItems)
   245 			{
   246 				startItem = totalItems - 1;
   247 				order = ItemOrder_Descending;
   248 			}
   249 		}
   250 		else if (order == ItemOrder_Descending)
   251 		{
   252 			startItem = md.firstItem;
   253 			/* This shouldn't happen with well-coded menus.
   254 			 * If searching backwards doesn't give us enough room,
   255 			 * start from the beginning and change to ascending.
   256 			 */
   257 			if (startItem <= maxItems)
   258 			{
   259 				startItem = 0;
   260 				order = ItemOrder_Ascending;
   261 			}
   262 		}
   263 	}
   264 
   265 	/* Get our Display pointer and initialize some crap */
   266 	IMenuPanel *panel = menu->CreatePanel();
   267 	IMenuHandler *mh = md.mh;
   268 	bool foundExtra = false;
   269 	unsigned int extraItem = 0;
   270 
   271 	if (panel == NULL)
   272 	{
   273 		return NULL;
   274 	}
   275 
   276 	/**
   277 	 * We keep searching until:
   278 	 * 1) There are no more items
   279 	 * 2) We reach one OVER the maximum number of slot items
   280 	 * 3) We have reached maxItems and pagination is MENU_NO_PAGINATION
   281 	 */
   282 	unsigned int i = startItem;
   283 	unsigned int foundItems = 0;
   284 	while (totalItems)
   285 	{
   286 		ItemDrawInfo &dr = drawItems[foundItems].draw;
   287 		/* Is the item valid? */
   288 		if (menu->GetItemInfo(i, &dr) != NULL)
   289 		{
   290 			/* Ask the user to change the style, if necessary */
   291 			mh->OnMenuDrawItem(menu, client, i, dr.style);
   292 			/* Check if it's renderable */
   293 			if (IsSlotItem(panel, dr.style))
   294 			{
   295 				/* If we've already found the max number of items,
   296 				 * This means we should just cancel out and log our
   297 				 * "last item."
   298 				 */
   299 				if (foundItems >= maxItems)
   300 				{
   301 					foundExtra = true;
   302 					extraItem = i;
   303 					break;
   304 				}
   305 				drawItems[foundItems++].position = i;
   306 			}
   307 		}
   308 		/* If there's no pagination, stop once the menu is full. */
   309 		if (pgn == MENU_NO_PAGINATION)
   310 		{
   311 			/* If we've filled up, then stop */
   312 			if (foundItems >= maxItems)
   313 			{
   314 				break;
   315 			}
   316 		}
   317 		/* If we're descending and this is the first item, stop */
   318 		if (order == ItemOrder_Descending)
   319 		{
   320 			if (i == 0)
   321 			{
   322 				break;
   323 			}
   324 			i--;
   325 		} 
   326 		/* If we're ascending and this is the last item, stop */
   327 		else if (order == ItemOrder_Ascending)
   328 		{
   329 			if (i >= totalItems - 1)
   330 			{
   331 				break;
   332 			}
   333 			i++;
   334 		}
   335 	}
   336 
   337 	/* There were no items to draw! */
   338 	if (!foundItems)
   339 	{
   340 		panel->DeleteThis();
   341 		return NULL;
   342 	}
   343 
   344 	bool displayPrev = false;
   345 	bool displayNext = false;
   346 
   347 	/* This is an annoying process.
   348 	 * Skip it for non-paginated menus, which get special treatment.
   349 	 */
   350 	if (pgn != MENU_NO_PAGINATION)
   351 	{
   352 		if (foundExtra)
   353 		{
   354 			if (order == ItemOrder_Descending)
   355 			{
   356 				displayPrev = true;
   357 				md.firstItem = extraItem;
   358 			}
   359 			else if (order == ItemOrder_Ascending)
   360 			{
   361 				displayNext = true;
   362 				md.lastItem = extraItem;
   363 			}
   364 		}
   365 
   366 		unsigned int lastItem = 0;
   367 		ItemDrawInfo dr;
   368 		/* Find the last feasible item to search from. */
   369 		if (order == ItemOrder_Descending)
   370 		{
   371 			lastItem = drawItems[0].position;
   372 			if (lastItem >= totalItems - 1)
   373 			{
   374 				goto skip_search;
   375 			}
   376 			while (++lastItem < totalItems)
   377 			{
   378 				if (menu->GetItemInfo(lastItem, &dr) != NULL)
   379 				{
   380 					mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
   381 					if (IsSlotItem(panel, dr.style))
   382 					{
   383 						displayNext = true;
   384 						md.lastItem = lastItem;
   385 						break;
   386 					}
   387 				}
   388 			}
   389 		}
   390 		else if (order == ItemOrder_Ascending)
   391 		{
   392 			lastItem = drawItems[0].position;
   393 			if (lastItem == 0)
   394 			{
   395 				goto skip_search;
   396 			}
   397 			lastItem--;
   398 			while (lastItem != 0)
   399 			{
   400 				if (menu->GetItemInfo(lastItem, &dr) != NULL)
   401 				{
   402 					mh->OnMenuDrawItem(menu, client, lastItem, dr.style);
   403 					if (IsSlotItem(panel, dr.style))
   404 					{
   405 						displayPrev = true;
   406 						md.firstItem = lastItem;
   407 						break;
   408 					}
   409 				}
   410 				lastItem--;
   411 			}
   412 		}
   413 	}
   414 
   415 skip_search:
   416 
   417 	/* Draw the item according to the order */
   418 	menu_slots_t *slots = md.slots;
   419 	unsigned int position = 0;			/* Keep track of the last position */
   420 	if (order == ItemOrder_Ascending)
   421 	{
   422 		md.item_on_page = drawItems[0].position;
   423 		for (unsigned int i = 0; i < foundItems; i++)
   424 		{
   425 			ItemDrawInfo &dr = drawItems[i].draw;
   426 			if ((position = mh->OnMenuDisplayItem(menu, client, panel, drawItems[i].position, dr)) == 0)
   427 			{
   428 				position = panel->DrawItem(dr);
   429 			}
   430 			if (position != 0)
   431 			{
   432 				slots[position].item = drawItems[i].position;
   433 				slots[position].type = ItemSel_Item;
   434 			}
   435 		}
   436 	}
   437 	else if (order == ItemOrder_Descending)
   438 	{
   439 		unsigned int i = foundItems;
   440 		/* NOTE: There will always be at least one item because
   441 		 * of the check earlier.
   442 		 */
   443 		md.item_on_page = drawItems[foundItems - 1].position;
   444 		while (i--)
   445 		{
   446 			ItemDrawInfo &dr = drawItems[i].draw;
   447 			if ((position = mh->OnMenuDisplayItem(menu, client, panel, drawItems[i].position, dr)) == 0)
   448 			{
   449 				position = panel->DrawItem(dr);
   450 			}
   451 			if (position != 0)
   452 			{
   453 				slots[position].item = drawItems[i].position;
   454 				slots[position].type = ItemSel_Item;
   455 			}
   456 		}
   457 	}
   458 
   459 	/* Now, we need to check if we need to add anything extra */
   460 	if (pgn != MENU_NO_PAGINATION || exitButton)
   461 	{
   462 		bool canDrawDisabled = panel->CanDrawItem(ITEMDRAW_DISABLED|ITEMDRAW_CONTROL);
   463 		bool exitBackButton = false;
   464 		char text[50];
   465 
   466 		if (pgn != MENU_NO_PAGINATION
   467 			&& (menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXITBACK)
   468 		{
   469 			exitBackButton = true;
   470 		}
   471 
   472 		/* Calculate how many items we are allowed for control stuff */
   473 		unsigned int padding = style->GetMaxPageItems() - maxItems;
   474 		
   475 		/* Add the number of available slots */
   476 		padding += (maxItems - foundItems);
   477 
   478 		/* Someday, if we are able to re-enable this, we will be very lucky men. */
   479 #if 0
   480 		if (!style->FeatureExists(MenuStyleFeature_ImplicitExit))
   481 		{
   482 #endif
   483 		/* Even if we don't draw an exit button, we invalidate the slot. */
   484 		padding--;
   485 #if 0
   486 		} else {
   487 			/* Otherwise, we don't draw anything and leave the slot available */
   488 			exitButton = false;
   489 		}
   490 #endif
   491 
   492 		if (pgn != MENU_NO_PAGINATION)
   493 		{
   494 			/* Subtract two slots for the displayNext/displayPrev padding */
   495 			padding -= 2;
   496 		}
   497 
   498 		/* If we have an "Exit Back" button and the space to draw it, do so. */
   499 		if (exitBackButton)
   500 		{
   501 			if (!displayPrev)
   502 			{
   503 				displayPrev = true;
   504 			}
   505 			else
   506 			{
   507 				exitBackButton = false;
   508 			}
   509 		}
   510 
   511 		/**
   512 		 * We allow next/prev to be undrawn if neither exists.
   513 		 * Thus, we only need padding if one of them will be drawn,
   514 		 * or the exit button will be drawn.
   515 		 */
   516 		ItemDrawInfo padItem(NULL, ITEMDRAW_SPACER);
   517 		if (exitButton || (displayNext || displayPrev))
   518 		{
   519 			/* If there are no control options,
   520 			 * Instead just pad with invisible slots.
   521 			 */
   522 			if (!displayPrev && !displayPrev)
   523 			{
   524 				padItem.style = ITEMDRAW_NOTEXT;
   525 			}
   526 			/* Add spacers so we can pad to the end */
   527 			for (unsigned int i=0; i<padding; i++)
   528 			{
   529 				position = panel->DrawItem(padItem);
   530 				slots[position].type = ItemSel_None;
   531 			}
   532 		}
   533 
   534 		/* Put a fake spacer before control stuff, if possible */
   535 		if ((displayPrev || displayNext) || exitButton)
   536 		{
   537 			ItemDrawInfo draw("", ITEMDRAW_RAWLINE|ITEMDRAW_SPACER);
   538 			panel->DrawItem(draw);
   539 		}
   540 
   541 		ItemDrawInfo dr(text, 0);
   542 
   543 		/**
   544 		 * If we have one or the other, we need to have spacers for both.
   545 		 */
   546 		if (pgn != MENU_NO_PAGINATION)
   547 		{
   548 			if (displayPrev || displayNext)
   549 			{
   550 				/* PREVIOUS */
   551 				ItemDrawInfo padCtrlItem(NULL, ITEMDRAW_SPACER|ITEMDRAW_CONTROL);
   552 				if (displayPrev || canDrawDisabled)
   553 				{
   554 					if (exitBackButton)
   555 					{
   556 						CorePlayerTranslate(client, text, sizeof(text), "Back", NULL);
   557 						dr.style = ITEMDRAW_CONTROL;
   558 						position = panel->DrawItem(dr);
   559 						slots[position].type = ItemSel_ExitBack;
   560 					}
   561 					else
   562 					{
   563 						CorePlayerTranslate(client, text, sizeof(text), "Previous", NULL);
   564 						dr.style = (displayPrev ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL;
   565 						position = panel->DrawItem(dr);
   566 						slots[position].type = ItemSel_Back;
   567 					}
   568 				}
   569 				else if (displayNext || exitButton)
   570 				{
   571 					/* If we can't display this, and there is an exit button,
   572 					 * we need to pad!
   573 					 */
   574 					position = panel->DrawItem(padCtrlItem);
   575 					slots[position].type = ItemSel_None;
   576 				}
   577 
   578 				/* NEXT */
   579 				if (displayNext || canDrawDisabled)
   580 				{
   581 					CorePlayerTranslate(client, text, sizeof(text), "Next", NULL);
   582 					dr.style = (displayNext ? 0 : ITEMDRAW_DISABLED)|ITEMDRAW_CONTROL;
   583 					position = panel->DrawItem(dr);
   584 					slots[position].type = ItemSel_Next;
   585 				}
   586 				else if (exitButton)
   587 				{
   588 					/* If we can't display this,
   589 					 * but there is an "exit" button, we need to pad!
   590 					 */
   591 					position = panel->DrawItem(padCtrlItem);
   592 					slots[position].type = ItemSel_None;
   593 				}
   594 			}
   595 			else
   596 			{
   597 				/* Otherwise, bump to two slots! */
   598 				ItemDrawInfo numBump(NULL, ITEMDRAW_NOTEXT);
   599 				position = panel->DrawItem(numBump);
   600 				slots[position].type = ItemSel_None;
   601 				position = panel->DrawItem(numBump);
   602 				slots[position].type = ItemSel_None;
   603 			}
   604 		}
   605 
   606 		/* EXIT */
   607 		if (exitButton)
   608 		{
   609 			CorePlayerTranslate(client, text, sizeof(text), "Exit", NULL);
   610 			dr.style = ITEMDRAW_CONTROL;
   611 			position = panel->DrawItem(dr);
   612 			slots[position].type = ItemSel_Exit;
   613 		}
   614 	}
   615 
   616 	/* Lastly, fill in any slots we could have missed */
   617 	for (unsigned int i = position + 1; i < 10; i++)
   618 	{
   619 		slots[i].type = ItemSel_None;
   620 	}
   621 
   622 	/* Do title stuff */
   623 	mh->OnMenuDisplay(menu, client, panel);
   624 	panel->DrawTitle(menu->GetDefaultTitle(), true);
   625 
   626 	return panel;
   627 }
   628 
   629 IMenuStyle *MenuManager::GetDefaultStyle()
   630 {
   631 	return m_pDefaultStyle;
   632 }
   633 
   634 bool MenuManager::MenuSoundsEnabled()
   635 {
   636 	return (sm_menu_sounds.GetInt() != 0);
   637 }
   638 
   639 ConfigResult MenuManager::OnSourceModConfigChanged(const char *key,
   640 												   const char *value,
   641 												   ConfigSource source,
   642 												   char *error,
   643 												   size_t maxlength)
   644 {
   645 	if (strcmp(key, "MenuItemSound") == 0)
   646 	{
   647 		m_SelectSound.assign(value);
   648 		return ConfigResult_Accept;
   649 	} else if (strcmp(key, "MenuExitBackSound") == 0) {
   650 		m_ExitBackSound.assign(value);
   651 		return ConfigResult_Accept;
   652 	} else if (strcmp(key, "MenuExitSound") == 0) {
   653 		m_ExitSound.assign(value);
   654 		return ConfigResult_Accept;
   655 	}
   656 
   657 	return ConfigResult_Ignore;
   658 }
   659 
   660 const char *MenuManager::GetMenuSound(ItemSelection sel)
   661 {
   662 	const char *sound = NULL;
   663 
   664 	switch (sel)
   665 	{
   666 	case ItemSel_Back:
   667 	case ItemSel_Next:
   668 	case ItemSel_Item:
   669 		{
   670 			if (m_SelectSound.size() > 0)
   671 			{
   672 				sound = m_SelectSound.c_str();
   673 			}
   674 			break;
   675 		}
   676 	case ItemSel_ExitBack:
   677 		{
   678 			if (m_ExitBackSound.size() > 0)
   679 			{
   680 				sound = m_ExitBackSound.c_str();
   681 			}
   682 			break;
   683 		}
   684 	case ItemSel_Exit:
   685 		{
   686 			if (m_ExitSound.size() > 0)
   687 			{
   688 				sound = m_ExitSound.c_str();
   689 			}
   690 			break;
   691 		}
   692 	default:
   693 		{
   694 			break;
   695 		}
   696 	}
   697 
   698 	return sound;
   699 }
   700 
   701 void MenuManager::OnSourceModLevelChange(const char *mapName)
   702 {
   703 	if (m_SelectSound.size() > 0)
   704 	{
   705 		enginesound->PrecacheSound(m_SelectSound.c_str(), true);
   706 	}
   707 	if (m_ExitBackSound.size() > 0)
   708 	{
   709 		enginesound->PrecacheSound(m_ExitBackSound.c_str(), true);
   710 	}
   711 	if (m_ExitSound.size() > 0)
   712 	{
   713 		enginesound->PrecacheSound(m_ExitSound.c_str(), true);
   714 	}
   715 }
   716 
   717 void MenuManager::CancelMenu(IBaseMenu *menu)
   718 {
   719 	if (s_VoteHandler.GetCurrentMenu() == menu
   720 		&& !s_VoteHandler.IsCancelling())
   721 	{
   722 		s_VoteHandler.CancelVoting();
   723 		return;
   724 	}
   725 
   726 	menu->Cancel();
   727 }
   728 
   729 bool MenuManager::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags)
   730 {
   731 	return s_VoteHandler.StartVote(menu, num_clients, clients, max_time, flags);
   732 }
   733 
   734 bool MenuManager::IsVoteInProgress()
   735 {
   736 	return s_VoteHandler.IsVoteInProgress();
   737 }
   738 
   739 void MenuManager::CancelVoting()
   740 {
   741 	s_VoteHandler.CancelVoting();
   742 }
   743 
   744 unsigned int MenuManager::GetRemainingVoteDelay()
   745 {
   746 	return s_VoteHandler.GetRemainingVoteDelay();
   747 }