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