core/smn_menus.cpp
author Ruben Gonzalez <drifter01620@gmail.com>
Thu May 22 09:49:27 2014 -0400 (2014-05-22)
changeset 4378 568f4497ef4a
parent 4060 b4484bd36c22
permissions -rw-r--r--
Update CS:GO team score gamedata.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 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 "sm_globals.h"
    33 #include <sh_stack.h>
    34 #include "MenuManager.h"
    35 #include "MenuStyle_Valve.h"
    36 #include "MenuStyle_Radio.h"
    37 #include "PlayerManager.h"
    38 #include "sm_stringutil.h"
    39 #include "sourcemm_api.h"
    40 #if defined MENU_DEBUG
    41 #include "Logger.h"
    42 #endif
    43 #include "ChatTriggers.h"
    44 #include "logic_bridge.h"
    45 #include "sourcemod.h"
    46 
    47 #if defined CreateMenu
    48 #undef CreateMenu
    49 #endif
    50 
    51 /**
    52  * And God said, "let there be menus," and behold, there were menus.  
    53  * God saw the menus and they were good.  And the evening and the morning
    54  * were the third day.
    55  */
    56 
    57 enum MenuAction
    58 {
    59 	MenuAction_Start = (1<<0),		/**< A menu has been started (nothing passed) */
    60 	MenuAction_Display = (1<<1),	/**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */
    61 	MenuAction_Select = (1<<2),		/**< An item was selected (param1=client, param2=item) */
    62 	MenuAction_Cancel = (1<<3),		/**< The menu was cancelled (param1=client, param2=item) */
    63 	MenuAction_End = (1<<4),		/**< A menu's display/selection cycle is complete (nothing passed). */
    64 	MenuAction_VoteEnd = (1<<5),	/**< (VOTE ONLY): A vote sequence has ended (param1=chosen item) */
    65 	MenuAction_VoteStart = (1<<6), 	/**< (VOTE ONLY): A vote sequence has started */
    66 	MenuAction_VoteCancel = (1<<7),	/**< (VOTE ONLY): A vote sequence has been cancelled (nothing passed) */
    67 	MenuAction_DrawItem = (1<<8),	/**< A style is being drawn; return the new style (param1=client, param2=item) */
    68 	MenuAction_DisplayItem = (1<<9),	/**< the odd duck */
    69 };
    70 
    71 class CPanelHandler : public IMenuHandler
    72 {
    73 	friend class MenuNativeHelpers;
    74 public:
    75 	CPanelHandler()
    76 	{
    77 	}
    78 	void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
    79 	void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
    80 private:
    81 	IPluginFunction *m_pFunc;
    82 	IPlugin *m_pPlugin;
    83 };
    84 
    85 class CMenuHandler : public IMenuHandler
    86 {
    87 	friend class MenuNativeHelpers;
    88 public:
    89 	CMenuHandler(IPluginFunction *pBasic, int flags);
    90 public:
    91 	void OnMenuStart(IBaseMenu *menu);
    92 	void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
    93 	void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page);
    94 	void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
    95 	void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
    96 	void OnMenuDestroy(IBaseMenu *menu);
    97 	void OnMenuVoteStart(IBaseMenu *menu);
    98 	void OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results);
    99 	void OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason);
   100 	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
   101 	unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
   102 	bool OnSetHandlerOption(const char *option, const void *data);
   103 #if 0
   104 	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
   105 	void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
   106 #endif
   107 private:
   108 	cell_t DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res=0);
   109 private:
   110 	IPluginFunction *m_pBasic;
   111 	int m_Flags;
   112 	IPluginFunction *m_pVoteResults;
   113 	cell_t m_fnVoteResult;
   114 };
   115 
   116 /**
   117  * GLOBAL CLASS FOR HELPERS
   118  */
   119 
   120 class MenuNativeHelpers : 
   121 	public SMGlobalClass,
   122 	public IHandleTypeDispatch,
   123 	public IPluginsListener
   124 {
   125 public:
   126 	virtual void OnSourceModAllInitialized()
   127 	{
   128 		m_PanelType = handlesys->CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
   129 		m_TempPanelType = handlesys->CreateType("TempIMenuPanel", this, m_PanelType, NULL, NULL, g_pCoreIdent, NULL);
   130 		scripts->AddPluginsListener(this);
   131 	}
   132 
   133 	virtual void OnSourceModShutdown()
   134 	{
   135 		scripts->RemovePluginsListener(this);
   136 		handlesys->RemoveType(m_TempPanelType, g_pCoreIdent);
   137 		handlesys->RemoveType(m_PanelType, g_pCoreIdent);
   138 
   139 		while (!m_FreePanelHandlers.empty())
   140 		{
   141 			delete m_FreePanelHandlers.front();
   142 			m_FreePanelHandlers.pop();
   143 		}
   144 
   145 		while (!m_FreeMenuHandlers.empty())
   146 		{
   147 			delete m_FreeMenuHandlers.front();
   148 			m_FreeMenuHandlers.pop();
   149 		}
   150 	}
   151 
   152 	virtual void OnHandleDestroy(HandleType_t type, void *object)
   153 	{
   154 		if (type == m_TempPanelType)
   155 		{
   156 			return;
   157 		}
   158 
   159 		IMenuPanel *panel = (IMenuPanel *)object;
   160 		panel->DeleteThis();
   161 	}
   162 
   163 	virtual bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
   164 	{
   165 		*pSize = ((IMenuPanel *)object)->GetApproxMemUsage();
   166 		return true;
   167 	}
   168 
   169 	/**
   170 	 * It is extremely important that unloaded plugins don't crash.
   171 	 * Thus, if a plugin unloads, we run through every handler we have.
   172 	 * This means we do almost no runtime work for keeping track of
   173 	 * our panel handlers (we don't have to store a list of the running 
   174 	 * ones), but when push comes to shove, we have to scan them all
   175 	 * in case any of them are active.
   176 	 */
   177 	virtual void OnPluginUnloaded(IPlugin *plugin)
   178 	{
   179 		for (size_t i = 0; i < m_PanelHandlers.size(); i++)
   180 		{
   181 			if (m_PanelHandlers[i]->m_pPlugin == plugin)
   182 			{
   183 				m_PanelHandlers[i]->m_pPlugin = NULL;
   184 				m_PanelHandlers[i]->m_pFunc = NULL;
   185 			}
   186 		}
   187 	}
   188 
   189 	inline HandleType_t GetPanelType()
   190 	{
   191 		return m_PanelType;
   192 	}
   193 
   194 	inline HandleType_t GetTempPanelType()
   195 	{
   196 		return m_TempPanelType;
   197 	}
   198 
   199 	CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
   200 	{
   201 		CPanelHandler *handler;
   202 		if (m_FreePanelHandlers.empty())
   203 		{
   204 			handler = new CPanelHandler;
   205 			m_PanelHandlers.push_back(handler);
   206 		} else {
   207 			handler = m_FreePanelHandlers.front();
   208 			m_FreePanelHandlers.pop();
   209 		}
   210 		handler->m_pFunc = pFunction;
   211 		handler->m_pPlugin = scripts->FindPluginByContext(pFunction->GetParentContext()->GetContext());
   212 		return handler;
   213 	}
   214 
   215 	void FreePanelHandler(CPanelHandler *handler)
   216 	{
   217 		handler->m_pFunc = NULL;
   218 		handler->m_pPlugin = NULL;
   219 		m_FreePanelHandlers.push(handler);
   220 	}
   221 
   222 	CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
   223 	{
   224 		CMenuHandler *handler;
   225 		if (m_FreeMenuHandlers.empty())
   226 		{
   227 			handler = new CMenuHandler(pFunction, flags);
   228 		} else {
   229 			handler = m_FreeMenuHandlers.front();
   230 			m_FreeMenuHandlers.pop();
   231 			handler->m_pBasic = pFunction;
   232 			handler->m_Flags = flags;
   233 			handler->m_pVoteResults = NULL;
   234 		}
   235 		return handler;
   236 	}
   237 
   238 	void FreeMenuHandler(CMenuHandler *handler)
   239 	{
   240 		m_FreeMenuHandlers.push(handler);
   241 	}
   242 
   243 private:
   244 	HandleType_t m_PanelType;
   245 	HandleType_t m_TempPanelType;
   246 	CStack<CPanelHandler *> m_FreePanelHandlers;
   247 	CStack<CMenuHandler *> m_FreeMenuHandlers;
   248 	CVector<CPanelHandler *> m_PanelHandlers;
   249 } g_MenuHelpers;
   250 
   251 /**
   252  * BASIC PANEL HANDLER WRAPPER
   253  */
   254 
   255 void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   256 {
   257 	if (m_pFunc)
   258 	{
   259 		m_pFunc->PushCell(BAD_HANDLE);
   260 		m_pFunc->PushCell(MenuAction_Cancel);
   261 		m_pFunc->PushCell(client);
   262 		m_pFunc->PushCell(reason);
   263 		m_pFunc->Execute(NULL);
   264 	}
   265 	g_MenuHelpers.FreePanelHandler(this);
   266 }
   267 
   268 void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   269 {
   270 	if (m_pFunc)
   271 	{
   272 		unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
   273 		m_pFunc->PushCell(BAD_HANDLE);
   274 		m_pFunc->PushCell(MenuAction_Select);
   275 		m_pFunc->PushCell(client);
   276 		m_pFunc->PushCell(item);
   277 		m_pFunc->Execute(NULL);
   278 		g_ChatTriggers.SetReplyTo(old_reply);
   279 	}
   280 	g_MenuHelpers.FreePanelHandler(this);
   281 }
   282 
   283 static IMenuPanel *s_pCurPanel = NULL;
   284 static unsigned int s_CurPanelReturn = 0;
   285 static const ItemDrawInfo *s_CurDrawInfo = NULL;
   286 static unsigned int *s_CurSelectPosition = NULL;
   287 
   288 /**
   289  * MENU HANDLER WRAPPER
   290  */
   291 CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) : 
   292 	m_pBasic(pBasic), m_Flags(flags), m_pVoteResults(NULL)
   293 {
   294 	/* :TODO: We can probably cache the handle ahead of time */
   295 }
   296 
   297 void CMenuHandler::OnMenuStart(IBaseMenu *menu)
   298 {
   299 	if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
   300 	{
   301 		DoAction(menu, MenuAction_Start, 0, 0);
   302 	}
   303 }
   304 
   305 void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
   306 {
   307 	if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
   308 	{
   309 		HandleSecurity sec;
   310 		sec.pIdentity = g_pCoreIdent;
   311 		sec.pOwner = m_pBasic->GetParentContext()->GetIdentity();
   312 	
   313 		HandleAccess access;
   314 		handlesys->InitAccessDefaults(NULL, &access);
   315 		access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
   316 
   317 		Handle_t hndl = handlesys->CreateHandleEx(g_MenuHelpers.GetTempPanelType(), panel, &sec, &access, NULL);
   318 
   319 		DoAction(menu, MenuAction_Display, client, hndl);
   320 
   321 		handlesys->FreeHandle(hndl, &sec);
   322 	}
   323 }
   324 
   325 void CMenuHandler::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
   326 {
   327 	/* Save old position first. */
   328 	unsigned int first_item = item_on_page;
   329 	unsigned int *old_pos = s_CurSelectPosition;
   330 
   331 	s_CurSelectPosition = &first_item;
   332 
   333 	unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
   334 	DoAction(menu, MenuAction_Select, client, item);
   335 	g_ChatTriggers.SetReplyTo(old_reply);
   336 
   337 	s_CurSelectPosition = old_pos;
   338 }
   339 
   340 void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   341 {
   342 	unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
   343 	DoAction(menu, MenuAction_Cancel, client, (cell_t)reason);
   344 	g_ChatTriggers.SetReplyTo(old_reply);
   345 }
   346 
   347 void CMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
   348 {
   349 	DoAction(menu, MenuAction_End, reason, 0);
   350 }
   351 
   352 void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
   353 {
   354 	g_MenuHelpers.FreeMenuHandler(this);
   355 }
   356 
   357 void CMenuHandler::OnMenuVoteStart(IBaseMenu *menu)
   358 {
   359 	DoAction(menu, MenuAction_VoteStart, 0, 0);
   360 }
   361 
   362 void CMenuHandler::OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason)
   363 {
   364 	DoAction(menu, MenuAction_VoteCancel, reason, 0);
   365 }
   366 
   367 void CMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
   368 {
   369 	if ((m_Flags & (int)MenuAction_DrawItem) == (int)MenuAction_DrawItem)
   370 	{
   371 		cell_t result = DoAction(menu, MenuAction_DrawItem, client, item, style);
   372 		style = (unsigned int)result;
   373 	}
   374 }
   375 
   376 unsigned int CMenuHandler::OnMenuDisplayItem(IBaseMenu *menu,
   377 									 int client,
   378 									 IMenuPanel *panel,
   379 									 unsigned int item,
   380 									 const ItemDrawInfo &dr)
   381 {
   382 	if ((m_Flags & (int)MenuAction_DisplayItem) == (int)MenuAction_DisplayItem)
   383 	{
   384 		IMenuPanel *oldpanel = s_pCurPanel;
   385 		unsigned int oldret = s_CurPanelReturn;
   386 		const ItemDrawInfo *oldinfo = s_CurDrawInfo;
   387 		s_pCurPanel = panel;
   388 		s_CurPanelReturn = 0;
   389 		s_CurDrawInfo = &dr;
   390 
   391 		cell_t res = DoAction(menu, MenuAction_DisplayItem, client, item, 0);
   392 
   393 		if (!res)
   394 		{
   395 			res = s_CurPanelReturn;
   396 		}
   397 
   398 		s_pCurPanel = oldpanel;
   399 		s_CurPanelReturn = oldret;
   400 		s_CurDrawInfo = oldinfo;
   401 
   402 		return res;
   403 	}
   404 
   405 	return 0;
   406 }
   407 
   408 cell_t CMenuHandler::DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res)
   409 {
   410 #if defined MENU_DEBUG
   411 	g_Logger.LogMessage("[SM_MENU] CMenuHandler::DoAction() (menu %p/%08x) (action %d) (param1 %d) (param2 %d)",
   412 		menu,
   413 		menu->GetHandle(),
   414 		action,
   415 		param1,
   416 		param2);
   417 #endif
   418 	cell_t res = def_res;
   419 	m_pBasic->PushCell(menu->GetHandle());
   420 	m_pBasic->PushCell((cell_t)action);
   421 	m_pBasic->PushCell(param1);
   422 	m_pBasic->PushCell(param2);
   423 	m_pBasic->Execute(&res);
   424 	return res;
   425 }
   426 
   427 void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
   428 {
   429 	if (!m_pVoteResults)
   430 	{
   431 		/* Call MenuAction_VoteEnd instead.  See if there are any extra winners. */
   432 		unsigned int num_items = 1;
   433 		for (unsigned int i=1; i<results->num_items; i++)
   434 		{
   435 			if (results->item_list[i].count != results->item_list[0].count)
   436 			{
   437 				break;
   438 			}
   439 			num_items++;
   440 		}
   441 
   442 		/* See if we need to pick a random winner. */
   443 		unsigned int winning_item;
   444 		if (num_items > 1)
   445 		{
   446 			/* Yes, we do. */
   447 			srand(time(NULL));
   448 			winning_item = rand() % num_items;
   449 			winning_item = results->item_list[winning_item].item;
   450 		} else {
   451 			/* No, take the first. */
   452 			winning_item = results->item_list[0].item;
   453 		}
   454 
   455 		unsigned int total_votes = results->num_votes;
   456 		unsigned int winning_votes = results->item_list[0].count;
   457 
   458 		DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
   459 	} else {
   460 		IPluginContext *pContext = m_pVoteResults->GetParentContext();
   461 		bool no_call = false;
   462 		int err;
   463 
   464 		/* First array */
   465 		cell_t client_array_address = -1;
   466 		cell_t *client_array_base = NULL;
   467 		cell_t client_array_size = results->num_clients + (results->num_clients * 2);
   468 		if (client_array_size)
   469 		{
   470 			if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
   471 				!= SP_ERROR_NONE)
   472 			{
   473 				logicore.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
   474 				no_call = true;
   475 			} else {
   476 				cell_t target_offs = sizeof(cell_t) * results->num_clients;
   477 				cell_t *cur_index = client_array_base;
   478 				cell_t *cur_array;
   479 				for (unsigned int i=0; i<results->num_clients; i++)
   480 				{
   481 					/* Copy the array index */
   482 					*cur_index = target_offs;
   483 					/* Get the current array address */
   484 					cur_array = (cell_t *)((char *)cur_index + target_offs);
   485 					/* Store information */
   486 					cur_array[0] = results->client_list[i].client;
   487 					cur_array[1] = results->client_list[i].item;
   488 					/* Adjust for the new target by subtracting one indirection
   489 					 * and adding one array.
   490 					 */
   491 					target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
   492 					cur_index++;
   493 				}
   494 			}
   495 		}
   496 
   497 		/* Second array */
   498 		cell_t item_array_address = -1;
   499 		cell_t *item_array_base = NULL;
   500 		cell_t item_array_size = results->num_items + (results->num_items * 2);
   501 		if (item_array_size)
   502 		{
   503 			if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
   504 				!= SP_ERROR_NONE)
   505 			{
   506 				logicore.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
   507 				no_call = true;
   508 			} else {
   509 				cell_t target_offs = sizeof(cell_t) * results->num_items;
   510 				cell_t *cur_index = item_array_base;
   511 				cell_t *cur_array;
   512 				for (unsigned int i=0; i<results->num_items; i++)
   513 				{
   514 					/* Copy the array index */
   515 					*cur_index = target_offs;
   516 					/* Get the current array address */
   517 					cur_array = (cell_t *)((char *)cur_index + target_offs);
   518 					/* Store information */
   519 					cur_array[0] = results->item_list[i].item;
   520 					cur_array[1] = results->item_list[i].count;
   521 					/* Adjust for the new target by subtracting one indirection
   522 					 * and adding one array.
   523 					 */
   524 					target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
   525 					cur_index++;
   526 				}
   527 			}
   528 		}
   529 
   530 		/* Finally, push everything */
   531 		if (!no_call)
   532 		{
   533 			m_pVoteResults->PushCell(menu->GetHandle());
   534 			m_pVoteResults->PushCell(results->num_votes);
   535 			m_pVoteResults->PushCell(results->num_clients);
   536 			m_pVoteResults->PushCell(client_array_address);
   537 			m_pVoteResults->PushCell(results->num_items);
   538 			m_pVoteResults->PushCell(item_array_address);
   539 			m_pVoteResults->Execute(NULL);
   540 		}
   541 
   542 		/* Free what we allocated, in reverse order as required */
   543 		if (item_array_address != -1)
   544 		{
   545 			pContext->HeapPop(item_array_address);
   546 		}
   547 		if (client_array_address != -1)
   548 		{
   549 			pContext->HeapPop(client_array_address);
   550 		}
   551 	}
   552 }
   553 
   554 bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
   555 {
   556 	if (strcmp(option, "set_vote_results_handler") == 0)
   557 	{
   558 		void **array = (void **)data;
   559 		m_pVoteResults = (IPluginFunction *)array[0];
   560 		m_fnVoteResult = *(cell_t *)((cell_t *)array[1]);
   561 		return true;
   562 	}
   563 
   564 	return false;
   565 }
   566 
   567 /**
   568  * INLINE FUNCTIONS FOR NATIVES
   569  */
   570 
   571 inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
   572 {
   573 	return handlesys->CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
   574 }
   575 
   576 inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
   577 {
   578 	HandleSecurity sec;
   579 	sec.pIdentity = g_pCoreIdent;
   580 	sec.pOwner = NULL;
   581 	return handlesys->ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
   582 }
   583 
   584 inline IMenuStyle *GetStyleFromCell(cell_t cell)
   585 {
   586 	enum MenuStyle
   587 	{
   588 		MenuStyle_Default = 0,		/**< The "default" menu style for the mod */
   589 		MenuStyle_Valve = 1,		/**< The Valve provided menu style (Used on HL2DM) */
   590 		MenuStyle_Radio = 2,		/**< The simpler menu style commonly used on CS:S */
   591 	};
   592 
   593 	if (cell == MenuStyle_Valve)
   594 	{
   595 		return &g_ValveMenuStyle;
   596 	} else if (cell == MenuStyle_Radio
   597 			   && g_RadioMenuStyle.IsSupported())
   598 	{
   599 		return &g_RadioMenuStyle;
   600 	}
   601 
   602 	return g_Menus.GetDefaultStyle();
   603 }
   604 
   605 /***********************************
   606  **** NATIVE DEFINITIONS ***********
   607  ***********************************/
   608 
   609 static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
   610 {
   611 	IMenuStyle *style = g_Menus.GetDefaultStyle();
   612 	IPluginFunction *pFunction;
   613 
   614 	if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
   615 	{
   616 		return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
   617 	}
   618 
   619 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
   620 	IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
   621 
   622 	Handle_t hndl = menu->GetHandle();
   623 	if (!hndl)
   624 	{
   625 		menu->Destroy();
   626 		return BAD_HANDLE;
   627 	}
   628 
   629 	return hndl;
   630 }
   631 
   632 static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
   633 {
   634 	Handle_t hndl = (Handle_t)params[1];
   635 	HandleError err;
   636 	IBaseMenu *menu;
   637 
   638 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   639 	{
   640 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   641 	}
   642 
   643 	return menu->Display(params[2], params[3]) ? 1 : 0;
   644 }
   645 
   646 static cell_t DisplayMenuAtItem(IPluginContext *pContext, const cell_t *params)
   647 {
   648 	Handle_t hndl = (Handle_t)params[1];
   649 	HandleError err;
   650 	IBaseMenu *menu;
   651 
   652 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   653 	{
   654 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   655 	}
   656 
   657 	return menu->DisplayAtItem(params[2], params[4], params[3]) ? 1 : 0;
   658 }
   659 
   660 static cell_t VoteMenu(IPluginContext *pContext, const cell_t *params)
   661 {
   662 	if (g_Menus.IsVoteInProgress())
   663 	{
   664 		return pContext->ThrowNativeError("A vote is already in progress");
   665 	}
   666 
   667 	Handle_t hndl = (Handle_t)params[1];
   668 	HandleError err;
   669 	IBaseMenu *menu;
   670 
   671 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   672 	{
   673 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   674 	}
   675 
   676 	cell_t *addr;
   677 	pContext->LocalToPhysAddr(params[2], &addr);
   678 
   679 	cell_t flags = 0;
   680 	if (params[0] >= 5)
   681 	{
   682 		flags = params[5];
   683 	}
   684 
   685 	if (!g_Menus.StartVote(menu, params[3], addr, params[4], flags))
   686 	{
   687 		return 0;
   688 	}
   689 
   690 	return 1;
   691 }
   692 
   693 static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
   694 {
   695 	Handle_t hndl = (Handle_t)params[1];
   696 	HandleError err;
   697 	IBaseMenu *menu;
   698 
   699 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   700 	{
   701 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   702 	}
   703 
   704 	char *info;
   705 	ItemDrawInfo dr;
   706 
   707 	pContext->LocalToString(params[2], &info);
   708 	pContext->LocalToString(params[3], (char **)&dr.display);
   709 	dr.style = params[4];
   710 
   711 	return menu->AppendItem(info, dr) ? 1 : 0;
   712 }
   713 
   714 static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
   715 {
   716 	Handle_t hndl = (Handle_t)params[1];
   717 	HandleError err;
   718 	IBaseMenu *menu;
   719 
   720 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   721 	{
   722 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   723 	}
   724 
   725 	char *info;
   726 	ItemDrawInfo dr;
   727 
   728 	pContext->LocalToString(params[3], &info);
   729 	pContext->LocalToString(params[4], (char **)&dr.display);
   730 	dr.style = params[5];
   731 
   732 	return menu->InsertItem(params[2], info, dr) ? 1 : 0;
   733 }
   734 
   735 static cell_t RemoveMenuItem(IPluginContext *pContext, const cell_t *params)
   736 {
   737 	Handle_t hndl = (Handle_t)params[1];
   738 	HandleError err;
   739 	IBaseMenu *menu;
   740 
   741 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   742 	{
   743 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   744 	}
   745 
   746 	return menu->RemoveItem(params[2]) ? 1 : 0;
   747 }
   748 
   749 static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
   750 {
   751 	Handle_t hndl = (Handle_t)params[1];
   752 	HandleError err;
   753 	IBaseMenu *menu;
   754 
   755 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   756 	{
   757 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   758 	}
   759 
   760 	menu->RemoveAllItems();
   761 
   762 	return 1;
   763 }
   764 
   765 static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
   766 {
   767 	Handle_t hndl = (Handle_t)params[1];
   768 	HandleError err;
   769 	IBaseMenu *menu;
   770 
   771 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   772 	{
   773 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   774 	}
   775 
   776 	ItemDrawInfo dr;
   777 	const char *info;
   778 
   779 	if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
   780 	{
   781 		return 0;
   782 	}
   783 
   784 	pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
   785 	pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
   786 
   787 	cell_t *addr;
   788 	pContext->LocalToPhysAddr(params[5], &addr);
   789 	*addr = dr.style;
   790 
   791 	return 1;
   792 }
   793 
   794 static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
   795 {
   796 	Handle_t hndl = (Handle_t)params[1];
   797 	HandleError err;
   798 	IBaseMenu *menu;
   799 
   800 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   801 	{
   802 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   803 	}
   804 
   805 	return menu->SetPagination(params[2]) ? 1 : 0;
   806 }
   807 
   808 static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
   809 {
   810 	Handle_t hndl = (Handle_t)params[1];
   811 	HandleError err;
   812 	IBaseMenu *menu;
   813 
   814 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   815 	{
   816 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   817 	}
   818 
   819 	return menu->GetPagination();
   820 }
   821 
   822 static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
   823 {
   824 	Handle_t hndl = (Handle_t)params[1];
   825 	HandleError err;
   826 	IBaseMenu *menu;
   827 
   828 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   829 	{
   830 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   831 	}
   832 
   833 	return menu->GetItemCount();
   834 }
   835 
   836 static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
   837 {
   838 	Handle_t hndl = (Handle_t)params[1];
   839 	HandleError err;
   840 	IBaseMenu *menu;
   841 
   842 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   843 	{
   844 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   845 	}
   846 
   847 	char buffer[1024];
   848 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   849 
   850 	menu->SetDefaultTitle(buffer);
   851 
   852 	return 1;
   853 }
   854 
   855 static cell_t GetMenuTitle(IPluginContext *pContext, const cell_t *params)
   856 {
   857 	Handle_t hndl = (Handle_t)params[1];
   858 	HandleError err;
   859 	IBaseMenu *menu;
   860 
   861 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   862 	{
   863 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   864 	}
   865 
   866 	size_t written;
   867 	const char *title = menu->GetDefaultTitle();
   868 	pContext->StringToLocalUTF8(params[2], params[3], title, &written);
   869 
   870 	return (cell_t)written;
   871 }
   872 
   873 
   874 static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
   875 {
   876 	Handle_t hndl = (Handle_t)params[1];
   877 	HandleError err;
   878 	IBaseMenu *menu;
   879 
   880 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   881 	{
   882 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   883 	}
   884 
   885 	IMenuPanel *panel = menu->CreatePanel();
   886 	hndl = MakePanelHandle(panel, pContext);
   887 	if (!hndl)
   888 	{
   889 		panel->DeleteThis();
   890 	}
   891 
   892 	return hndl;
   893 }
   894 
   895 static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   896 {
   897 	Handle_t hndl = (Handle_t)params[1];
   898 	HandleError err;
   899 	IBaseMenu *menu;
   900 
   901 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   902 	{
   903 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   904 	}
   905 
   906 	return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
   907 }
   908 
   909 static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
   910 {
   911 	Handle_t hndl = (Handle_t)params[1];
   912 	HandleError err;
   913 	IBaseMenu *menu;
   914 
   915 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   916 	{
   917 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   918 	}
   919 
   920 	return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
   921 }
   922 
   923 static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   924 {
   925 	Handle_t hndl = (Handle_t)params[1];
   926 	HandleError err;
   927 	IBaseMenu *menu;
   928 
   929 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   930 	{
   931 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   932 	}
   933 
   934 	unsigned int flags = menu->GetMenuOptionFlags();
   935 
   936 	if (params[2])
   937 	{
   938 		flags |= MENUFLAG_BUTTON_EXIT;
   939 	} else {
   940 		flags &= ~MENUFLAG_BUTTON_EXIT;
   941 	}
   942 
   943 	menu->SetMenuOptionFlags(flags);
   944 	unsigned int new_flags = menu->GetMenuOptionFlags();
   945 
   946 	return (flags == new_flags);
   947 }
   948 
   949 static cell_t SetMenuNoVoteButton(IPluginContext *pContext, const cell_t *params)
   950 {
   951 	Handle_t hndl = (Handle_t)params[1];
   952 	HandleError err;
   953 	IBaseMenu *menu;
   954 
   955 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   956 	{
   957 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   958 	}
   959 
   960 	unsigned int flags = menu->GetMenuOptionFlags();
   961 
   962 	if (params[2])
   963 	{
   964 		flags |= MENUFLAG_BUTTON_NOVOTE;
   965 	} else {
   966 		flags &= ~MENUFLAG_BUTTON_NOVOTE;
   967 	}
   968 
   969 	menu->SetMenuOptionFlags(flags);
   970 	unsigned int new_flags = menu->GetMenuOptionFlags();
   971 
   972 	return (flags == new_flags);
   973 }
   974 
   975 static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
   976 {
   977 	Handle_t hndl = (Handle_t)params[1];
   978 	HandleError err;
   979 	IBaseMenu *menu;
   980 
   981 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   982 	{
   983 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   984 	}
   985 
   986 	unsigned int flags = menu->GetMenuOptionFlags();
   987 
   988 	if (params[2])
   989 	{
   990 		flags |= MENUFLAG_BUTTON_EXITBACK;
   991 	} else {
   992 		flags &= ~MENUFLAG_BUTTON_EXITBACK;
   993 	}
   994 
   995 	menu->SetMenuOptionFlags(flags);
   996 
   997 	return 1;
   998 }
   999 
  1000 static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
  1001 {
  1002 	Handle_t hndl = (Handle_t)params[1];
  1003 	HandleError err;
  1004 	IBaseMenu *menu;
  1005 
  1006 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1007 	{
  1008 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1009 	}
  1010 
  1011 	g_Menus.CancelMenu(menu);
  1012 
  1013 	return 1;
  1014 }
  1015 
  1016 static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
  1017 {
  1018 	return g_Menus.IsVoteInProgress() ? 1 : 0;
  1019 }
  1020 
  1021 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
  1022 {
  1023 	Handle_t hndl = (Handle_t)params[1];
  1024 	HandleError err;
  1025 	IBaseMenu *menu;
  1026 
  1027 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1028 	{
  1029 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1030 	}
  1031 
  1032 	return menu->GetDrawStyle()->GetHandle();
  1033 }
  1034 
  1035 static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
  1036 {
  1037 	IMenuStyle *style = GetStyleFromCell(params[1]);
  1038 	if (!style)
  1039 	{
  1040 		return BAD_HANDLE;
  1041 	}
  1042 
  1043 	return style->GetHandle();
  1044 }
  1045 
  1046 static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
  1047 {
  1048 	Handle_t hndl = (Handle_t)params[1];
  1049 	HandleError err;
  1050 	IMenuStyle *style;
  1051 
  1052 	if (hndl != 0)
  1053 	{
  1054 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1055 		{
  1056 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1057 		}
  1058 	} else {
  1059 		style = g_Menus.GetDefaultStyle();
  1060 	}
  1061 
  1062 	IMenuPanel *panel = style->CreatePanel();
  1063 
  1064 	hndl = MakePanelHandle(panel, pContext);
  1065 	if (!hndl)
  1066 	{
  1067 		panel->DeleteThis();
  1068 		return BAD_HANDLE;
  1069 	}
  1070 
  1071 	return hndl;
  1072 }
  1073 
  1074 static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
  1075 {
  1076 	Handle_t hndl = (Handle_t)params[1];
  1077 	HandleError err;
  1078 	IMenuStyle *style;
  1079 
  1080 	if (hndl != 0)
  1081 	{
  1082 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1083 		{
  1084 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1085 		}
  1086 	} else {
  1087 		style = g_Menus.GetDefaultStyle();
  1088 	}
  1089 
  1090 	IPluginFunction *pFunction;
  1091 	if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
  1092 	{
  1093 		return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
  1094 	}
  1095 
  1096 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
  1097 
  1098 	IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
  1099 	hndl = pMenu->GetHandle();
  1100 	if (!hndl)
  1101 	{
  1102 		pMenu->Destroy();
  1103 		return BAD_HANDLE;
  1104 	}
  1105 
  1106 	return hndl;
  1107 }
  1108 
  1109 static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
  1110 {
  1111 	Handle_t hndl = (Handle_t)params[2];
  1112 	HandleError err;
  1113 	IMenuStyle *style;
  1114 
  1115 	if (hndl != 0)
  1116 	{
  1117 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1118 		{
  1119 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1120 		}
  1121 	} else {
  1122 		style = g_Menus.GetDefaultStyle();
  1123 	}
  1124 
  1125 	return style->GetClientMenu(params[1], NULL);
  1126 }
  1127 
  1128 static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
  1129 {
  1130 	Handle_t hndl = (Handle_t)params[3];
  1131 	HandleError err;
  1132 	IMenuStyle *style;
  1133 
  1134 	if (hndl != 0)
  1135 	{
  1136 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1137 		{
  1138 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1139 		}
  1140 	} else {
  1141 		style = g_Menus.GetDefaultStyle();
  1142 	}
  1143 
  1144 	return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
  1145 }
  1146 
  1147 static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
  1148 {
  1149 	Handle_t hndl = (Handle_t)params[1];
  1150 	HandleError err;
  1151 	IMenuStyle *style;
  1152 
  1153 	if (hndl != 0)
  1154 	{
  1155 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1156 		{
  1157 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1158 		}
  1159 	} else {
  1160 		style = g_Menus.GetDefaultStyle();
  1161 	}
  1162 
  1163 	return style->GetMaxPageItems();
  1164 }
  1165 
  1166 static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
  1167 {
  1168 	Handle_t hndl = (Handle_t)params[1];
  1169 	HandleError err;
  1170 	IMenuPanel *panel;
  1171 
  1172 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1173 	{
  1174 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1175 	}
  1176 
  1177 	return panel->GetParentStyle()->GetHandle();
  1178 }
  1179 
  1180 static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
  1181 {
  1182 	Handle_t hndl = (Handle_t)params[1];
  1183 	HandleError err;
  1184 	IMenuPanel *panel;
  1185 
  1186 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1187 	{
  1188 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1189 	}
  1190 
  1191 	char *text;
  1192 	pContext->LocalToString(params[2], &text);
  1193 
  1194 	panel->DrawTitle(text, params[3] ? true : false);
  1195 
  1196 	return 1;
  1197 }
  1198 
  1199 static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
  1200 {
  1201 	Handle_t hndl = (Handle_t)params[1];
  1202 	HandleError err;
  1203 	IMenuPanel *panel;
  1204 
  1205 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1206 	{
  1207 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1208 	}
  1209 
  1210 	ItemDrawInfo dr;
  1211 	pContext->LocalToString(params[2], (char **)&dr.display);
  1212 	dr.style = params[3];
  1213 
  1214 	return panel->DrawItem(dr);
  1215 }
  1216 
  1217 static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
  1218 {
  1219 	Handle_t hndl = (Handle_t)params[1];
  1220 	HandleError err;
  1221 	IMenuPanel *panel;
  1222 
  1223 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1224 	{
  1225 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1226 	}
  1227 
  1228 	char *text;
  1229 	pContext->LocalToString(params[2], &text);
  1230 
  1231 	return panel->DrawRawLine(text) ? 1 : 0;
  1232 }
  1233 
  1234 static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
  1235 {
  1236 	Handle_t hndl = (Handle_t)params[1];
  1237 	HandleError err;
  1238 	IMenuPanel *panel;
  1239 
  1240 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1241 	{
  1242 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1243 	}
  1244 
  1245 	return panel->CanDrawItem(params[2]);
  1246 }
  1247 
  1248 static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
  1249 {
  1250 	Handle_t hndl = (Handle_t)params[1];
  1251 	HandleError err;
  1252 	IMenuPanel *panel;
  1253 
  1254 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1255 	{
  1256 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1257 	}
  1258 
  1259 	IPluginFunction *pFunction;
  1260 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
  1261 	{
  1262 		return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
  1263 	}
  1264 
  1265 	CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
  1266 	if (!panel->SendDisplay(params[2], handler, params[4]))
  1267 	{
  1268 		g_MenuHelpers.FreePanelHandler(handler);
  1269 	}
  1270 
  1271 	return 1;
  1272 }
  1273 
  1274 static cell_t SetPanelKeys(IPluginContext *pContext, const cell_t *params)
  1275 {
  1276 	Handle_t hndl = (Handle_t)params[1];
  1277 	HandleError err;
  1278 	IMenuPanel *panel;
  1279 
  1280 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1281 	{
  1282 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1283 	}
  1284 
  1285 	return panel->SetSelectableKeys(params[2]);
  1286 }
  1287 
  1288 static cell_t GetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
  1289 {
  1290 	Handle_t hndl = (Handle_t)params[1];
  1291 	HandleError err;
  1292 	IMenuPanel *panel;
  1293 
  1294 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1295 	{
  1296 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1297 	}
  1298 
  1299 	return panel->GetCurrentKey();
  1300 }
  1301 
  1302 static cell_t GetPanelTextRemaining(IPluginContext *pContext, const cell_t *params)
  1303 {
  1304 	Handle_t hndl = (Handle_t)params[1];
  1305 	HandleError err;
  1306 	IMenuPanel *panel;
  1307 
  1308 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1309 	{
  1310 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1311 	}
  1312 
  1313 	return panel->GetAmountRemaining();
  1314 }
  1315 
  1316 static cell_t SetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
  1317 {
  1318 	Handle_t hndl = (Handle_t)params[1];
  1319 	HandleError err;
  1320 	IMenuPanel *panel;
  1321 
  1322 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1323 	{
  1324 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1325 	}
  1326 
  1327 	return panel->SetCurrentKey(params[2]) ? 1 : 0;
  1328 }
  1329 
  1330 static cell_t RedrawMenuItem(IPluginContext *pContext, const cell_t *params)
  1331 {
  1332 	if (!s_pCurPanel)
  1333 	{
  1334 		return pContext->ThrowNativeError("You can only call this once from a MenuAction_DisplayItem callback");
  1335 	}
  1336 
  1337 	char *str;
  1338 	pContext->LocalToString(params[1], &str);
  1339 
  1340 	ItemDrawInfo dr = *s_CurDrawInfo;
  1341 	dr.display = str;
  1342 
  1343 	if ((s_CurPanelReturn = s_pCurPanel->DrawItem(dr)) != 0)
  1344 	{
  1345 		s_pCurPanel = NULL;
  1346 	}
  1347 
  1348 	return s_CurPanelReturn;
  1349 }
  1350 
  1351 static cell_t GetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
  1352 {
  1353 	Handle_t hndl = (Handle_t)params[1];
  1354 	HandleError err;
  1355 	IBaseMenu *menu;
  1356 
  1357 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1358 	{
  1359 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1360 	}
  1361 
  1362 	return menu->GetMenuOptionFlags();
  1363 }
  1364 
  1365 static cell_t SetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
  1366 {
  1367 	Handle_t hndl = (Handle_t)params[1];
  1368 	HandleError err;
  1369 	IBaseMenu *menu;
  1370 
  1371 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1372 	{
  1373 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1374 	}
  1375 
  1376 	menu->SetMenuOptionFlags(params[2]);
  1377 
  1378 	return 1;
  1379 }
  1380 
  1381 static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
  1382 {
  1383 	if (!g_Menus.IsVoteInProgress())
  1384 	{
  1385 		return pContxt->ThrowNativeError("No vote is in progress");
  1386 	}
  1387 
  1388 	g_Menus.CancelVoting();
  1389 
  1390 	return 1;
  1391 }
  1392 
  1393 static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
  1394 {
  1395 	Handle_t hndl = (Handle_t)params[1];
  1396 	HandleError err;
  1397 	IBaseMenu *menu;
  1398 
  1399 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1400 	{
  1401 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1402 	}
  1403 
  1404 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
  1405 	if (!pFunction)
  1406 	{
  1407 		return pContext->ThrowNativeError("Invalid function %x", params[2]);
  1408 	}
  1409 
  1410 	void *array[2];
  1411 	array[0] = pFunction;
  1412 	array[1] = (void *)&params[2];
  1413 
  1414 	IMenuHandler *pHandler = menu->GetHandler();
  1415 	if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
  1416 	{
  1417 		return pContext->ThrowNativeError("The given menu does not support this option");
  1418 	}
  1419 
  1420 	return 1;
  1421 }
  1422 
  1423 static cell_t CheckVoteDelay(IPluginContext *pContext, const cell_t *params)
  1424 {
  1425 	return g_Menus.GetRemainingVoteDelay();
  1426 }
  1427 
  1428 static cell_t GetMenuSelectionPosition(IPluginContext *pContext, const cell_t *params)
  1429 {
  1430 	if (!s_CurSelectPosition)
  1431 	{
  1432 		return pContext->ThrowNativeError("Can only be called from inside a MenuAction_Select callback");
  1433 	}
  1434 
  1435 	return *s_CurSelectPosition;
  1436 }
  1437 
  1438 static cell_t IsClientInVotePool(IPluginContext *pContext, const cell_t *params)
  1439 {
  1440 	int client;
  1441 	IGamePlayer *pPlayer;
  1442 
  1443 	client = params[1];
  1444 	if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
  1445 	{
  1446 		return pContext->ThrowNativeError("Invalid client index %d", client);
  1447 	}
  1448 
  1449 	if (!g_Menus.IsVoteInProgress())
  1450 	{
  1451 		return pContext->ThrowNativeError("No vote is in progress");
  1452 	}
  1453 
  1454 	return g_Menus.IsClientInVotePool(client) ? 1 : 0;
  1455 }
  1456 
  1457 static cell_t RedrawClientVoteMenu(IPluginContext *pContext, const cell_t *params)
  1458 {
  1459 	int client;
  1460 	IGamePlayer *pPlayer;
  1461 
  1462 	client = params[1];
  1463 	if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
  1464 	{
  1465 		return pContext->ThrowNativeError("Invalid client index %d", client);
  1466 	}
  1467 
  1468 	if (!g_Menus.IsVoteInProgress())
  1469 	{
  1470 		return pContext->ThrowNativeError("No vote is in progress");
  1471 	}
  1472 
  1473 	if (!g_Menus.IsClientInVotePool(client))
  1474 	{
  1475 		return pContext->ThrowNativeError("Client is not in the voting pool");
  1476 	}
  1477 
  1478 	bool revote = true;
  1479 	if (params[0] >= 2 && !params[2])
  1480 	{
  1481 		revote = false;
  1482 	}
  1483 
  1484 	return g_Menus.RedrawClientVoteMenu2(client, revote) ? 1 : 0;
  1485 }
  1486 
  1487 class EmptyMenuHandler : public IMenuHandler
  1488 {
  1489 public:
  1490 } s_EmptyMenuHandler;
  1491 
  1492 static cell_t InternalShowMenu(IPluginContext *pContext, const cell_t *params)
  1493 {
  1494 	int client = params[1];
  1495 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
  1496 
  1497 	if (pPlayer == NULL)
  1498 	{
  1499 		return pContext->ThrowNativeError("Invalid client index %d", client);
  1500 	}
  1501 	else if (!pPlayer->IsInGame())
  1502 	{
  1503 		return pContext->ThrowNativeError("Client %d is not in game", client);
  1504 	}
  1505 
  1506 	if (!g_RadioMenuStyle.IsSupported())
  1507 	{
  1508 		return pContext->ThrowNativeError("Radio menus are not supported on this mod");
  1509 	}
  1510 
  1511 	char *str;
  1512 	pContext->LocalToString(params[2], &str);
  1513 
  1514 	IMenuPanel *pPanel = g_RadioMenuStyle.MakeRadioDisplay(str, params[4]);
  1515 
  1516 	if (pPanel == NULL)
  1517 	{
  1518 		return 0;
  1519 	}
  1520 
  1521 	IMenuHandler *pHandler;
  1522 	CPanelHandler *pActualHandler = NULL;
  1523 	if (params[5] != -1)
  1524 	{
  1525 		IPluginFunction *pFunction = pContext->GetFunctionById(params[5]);
  1526 		if (pFunction == NULL)
  1527 		{
  1528 			return pContext->ThrowNativeError("Invalid function index %x", params[5]);
  1529 		}
  1530 		pActualHandler = g_MenuHelpers.GetPanelHandler(pFunction);
  1531 	}
  1532 
  1533 	if (pActualHandler == NULL)
  1534 	{
  1535 		pHandler = &s_EmptyMenuHandler;
  1536 	}
  1537 	else
  1538 	{
  1539 		pHandler = pActualHandler;
  1540 	}
  1541 
  1542 	bool bSuccess = pPanel->SendDisplay(client, pHandler, params[3]);
  1543 
  1544 	pPanel->DeleteThis();
  1545 
  1546 	if (!bSuccess && pActualHandler != NULL)
  1547 	{
  1548 		g_MenuHelpers.FreePanelHandler(pActualHandler);
  1549 	}
  1550 
  1551 	return bSuccess ? 1 : 0;
  1552 }
  1553 
  1554 REGISTER_NATIVES(menuNatives)
  1555 {
  1556 	{"AddMenuItem",				AddMenuItem},
  1557 	{"CanPanelDrawFlags",		CanPanelDrawFlags},
  1558 	{"CancelClientMenu",		CancelClientMenu},
  1559 	{"CancelMenu",				CancelMenu},
  1560 	{"CancelVote",				CancelVote},
  1561 	{"CheckVoteDelay",			CheckVoteDelay},
  1562 	{"CreateMenu",				CreateMenu},
  1563 	{"CreateMenuEx",			CreateMenuEx},
  1564 	{"CreatePanel",				CreatePanel},
  1565 	{"CreatePanelFromMenu",		CreatePanelFromMenu},
  1566 	{"DisplayMenu",				DisplayMenu},
  1567 	{"DisplayMenuAtItem",		DisplayMenuAtItem},
  1568 	{"DrawPanelItem",			DrawPanelItem},
  1569 	{"DrawPanelText",			DrawPanelText},
  1570 	{"GetClientMenu",			GetClientMenu},
  1571 	{"GetMaxPageItems",			GetMaxPageItems},
  1572 	{"GetMenuExitBackButton",	GetMenuExitBackButton},
  1573 	{"GetMenuExitButton",		GetMenuExitButton},
  1574 	{"GetMenuItem",				GetMenuItem},
  1575 	{"GetMenuItemCount",		GetMenuItemCount},
  1576 	{"GetMenuOptionFlags",		GetMenuOptionFlags},
  1577 	{"GetMenuPagination",		GetMenuPagination},
  1578 	{"GetMenuSelectionPosition",GetMenuSelectionPosition},
  1579 	{"GetMenuStyle",			GetMenuStyle},
  1580 	{"GetMenuStyleHandle",		GetMenuStyleHandle},
  1581 	{"GetMenuTitle",			GetMenuTitle},
  1582 	{"GetPanelTextRemaining",	GetPanelTextRemaining},
  1583 	{"GetPanelCurrentKey",		GetPanelCurrentKey},
  1584 	{"GetPanelStyle",			GetPanelStyle},
  1585 	{"InsertMenuItem",			InsertMenuItem},
  1586 	{"InternalShowMenu",		InternalShowMenu},
  1587 	{"IsClientInVotePool",		IsClientInVotePool},
  1588 	{"IsVoteInProgress",		IsVoteInProgress},
  1589 	{"RedrawClientVoteMenu",	RedrawClientVoteMenu},
  1590 	{"RedrawMenuItem",			RedrawMenuItem},
  1591 	{"RemoveAllMenuItems",		RemoveAllMenuItems},
  1592 	{"RemoveMenuItem",			RemoveMenuItem},
  1593 	{"SendPanelToClient",		SendPanelToClient},
  1594 	{"SetMenuExitBackButton",	SetMenuExitBackButton},
  1595 	{"SetMenuExitButton",		SetMenuExitButton},
  1596 	{"SetMenuOptionFlags",		SetMenuOptionFlags},
  1597 	{"SetMenuPagination",		SetMenuPagination},
  1598 	{"SetMenuTitle",			SetMenuTitle},
  1599 	{"SetPanelCurrentKey",		SetPanelCurrentKey},
  1600 	{"SetPanelTitle",			SetPanelTitle},
  1601 	{"SetPanelKeys",			SetPanelKeys},
  1602 	{"SetVoteResultCallback",	SetVoteResultCallback},
  1603 	{"VoteMenu",				VoteMenu},
  1604 	{"SetMenuNoVoteButton",		SetMenuNoVoteButton},
  1605 	{NULL,						NULL},
  1606 };
  1607