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