core/smn_menus.cpp
author David Anderson <dvander@alliedmods.net>
Sun Oct 14 17:55:02 2007 +0000 (2007-10-14)
changeset 1545 4ceff5fa5fdd
parent 1522 d39a61e1b95b
child 1705 ce47e6d42853
permissions -rw-r--r--
fixed compat wrappers including core stuff
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "sm_globals.h"
    33 #include <sh_stack.h>
    34 #include "DebugReporter.h"
    35 #include "MenuManager.h"
    36 #include "MenuStyle_Valve.h"
    37 #include "MenuStyle_Radio.h"
    38 #include "HandleSys.h"
    39 #include "PluginSys.h"
    40 #include "sm_stringutil.h"
    41 #include "sourcemm_api.h"
    42 #if defined MENU_DEBUG
    43 #include "Logger.h"
    44 #endif
    45 #include "ChatTriggers.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 = g_HandleSys.CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
   129 		m_TempPanelType = g_HandleSys.CreateType("TempIMenuPanel", this, m_PanelType, NULL, NULL, g_pCoreIdent, NULL);
   130 		g_PluginSys.AddPluginsListener(this);
   131 	}
   132 
   133 	virtual void OnSourceModShutdown()
   134 	{
   135 		g_HandleSys.RemoveType(m_TempPanelType, g_pCoreIdent);
   136 		g_HandleSys.RemoveType(m_PanelType, g_pCoreIdent);
   137 
   138 		while (!m_FreePanelHandlers.empty())
   139 		{
   140 			delete m_FreePanelHandlers.front();
   141 			m_FreePanelHandlers.pop();
   142 		}
   143 
   144 		while (!m_FreeMenuHandlers.empty())
   145 		{
   146 			delete m_FreeMenuHandlers.front();
   147 			m_FreeMenuHandlers.pop();
   148 		}
   149 	}
   150 
   151 	virtual void OnHandleDestroy(HandleType_t type, void *object)
   152 	{
   153 		if (type == m_TempPanelType)
   154 		{
   155 			return;
   156 		}
   157 
   158 		IMenuPanel *panel = (IMenuPanel *)object;
   159 		panel->DeleteThis();
   160 	}
   161 
   162 	/**
   163 	 * It is extremely important that unloaded plugins don't crash.
   164 	 * Thus, if a plugin unloads, we run through every handler we have.
   165 	 * This means we do almost no runtime work for keeping track of
   166 	 * our panel handlers (we don't have to store a list of the running 
   167 	 * ones), but when push comes to shove, we have to scan them all
   168 	 * in case any of them are active.
   169 	 */
   170 	virtual void OnPluginUnloaded(IPlugin *plugin)
   171 	{
   172 		for (size_t i = 0; i < m_PanelHandlers.size(); i++)
   173 		{
   174 			if (m_PanelHandlers[i]->m_pPlugin == plugin)
   175 			{
   176 				m_PanelHandlers[i]->m_pPlugin = NULL;
   177 				m_PanelHandlers[i]->m_pFunc = NULL;
   178 			}
   179 		}
   180 	}
   181 
   182 	inline HandleType_t GetPanelType()
   183 	{
   184 		return m_PanelType;
   185 	}
   186 
   187 	inline HandleType_t GetTempPanelType()
   188 	{
   189 		return m_TempPanelType;
   190 	}
   191 
   192 	CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
   193 	{
   194 		CPanelHandler *handler;
   195 		if (m_FreePanelHandlers.empty())
   196 		{
   197 			handler = new CPanelHandler;
   198 			m_PanelHandlers.push_back(handler);
   199 		} else {
   200 			handler = m_FreePanelHandlers.front();
   201 			m_FreePanelHandlers.pop();
   202 		}
   203 		handler->m_pFunc = pFunction;
   204 		handler->m_pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
   205 		return handler;
   206 	}
   207 
   208 	void FreePanelHandler(CPanelHandler *handler)
   209 	{
   210 		handler->m_pFunc = NULL;
   211 		handler->m_pPlugin = NULL;
   212 		m_FreePanelHandlers.push(handler);
   213 	}
   214 
   215 	CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
   216 	{
   217 		CMenuHandler *handler;
   218 		if (m_FreeMenuHandlers.empty())
   219 		{
   220 			handler = new CMenuHandler(pFunction, flags);
   221 		} else {
   222 			handler = m_FreeMenuHandlers.front();
   223 			m_FreeMenuHandlers.pop();
   224 			handler->m_pBasic = pFunction;
   225 			handler->m_Flags = flags;
   226 			handler->m_pVoteResults = NULL;
   227 		}
   228 		return handler;
   229 	}
   230 
   231 	void FreeMenuHandler(CMenuHandler *handler)
   232 	{
   233 		m_FreeMenuHandlers.push(handler);
   234 	}
   235 
   236 private:
   237 	HandleType_t m_PanelType;
   238 	HandleType_t m_TempPanelType;
   239 	CStack<CPanelHandler *> m_FreePanelHandlers;
   240 	CStack<CMenuHandler *> m_FreeMenuHandlers;
   241 	CVector<CPanelHandler *> m_PanelHandlers;
   242 } g_MenuHelpers;
   243 
   244 /**
   245  * BASIC PANEL HANDLER WRAPPER
   246  */
   247 
   248 void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   249 {
   250 	if (m_pFunc)
   251 	{
   252 		m_pFunc->PushCell(BAD_HANDLE);
   253 		m_pFunc->PushCell(MenuAction_Cancel);
   254 		m_pFunc->PushCell(client);
   255 		m_pFunc->PushCell(reason);
   256 		m_pFunc->Execute(NULL);
   257 	}
   258 	g_MenuHelpers.FreePanelHandler(this);
   259 }
   260 
   261 void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   262 {
   263 	if (m_pFunc)
   264 	{
   265 		unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
   266 		m_pFunc->PushCell(BAD_HANDLE);
   267 		m_pFunc->PushCell(MenuAction_Select);
   268 		m_pFunc->PushCell(client);
   269 		m_pFunc->PushCell(item);
   270 		m_pFunc->Execute(NULL);
   271 		g_ChatTriggers.SetReplyTo(old_reply);
   272 	}
   273 	g_MenuHelpers.FreePanelHandler(this);
   274 }
   275 
   276 static IMenuPanel *s_pCurPanel = NULL;
   277 static unsigned int s_CurPanelReturn = 0;
   278 static const ItemDrawInfo *s_CurDrawInfo = NULL;
   279 static unsigned int *s_CurSelectPosition = NULL;
   280 
   281 /**
   282  * MENU HANDLER WRAPPER
   283  */
   284 CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) : 
   285 	m_pBasic(pBasic), m_Flags(flags), m_pVoteResults(NULL)
   286 {
   287 	/* :TODO: We can probably cache the handle ahead of time */
   288 }
   289 
   290 void CMenuHandler::OnMenuStart(IBaseMenu *menu)
   291 {
   292 	if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
   293 	{
   294 		DoAction(menu, MenuAction_Start, 0, 0);
   295 	}
   296 }
   297 
   298 void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
   299 {
   300 	if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
   301 	{
   302 		HandleSecurity sec;
   303 		sec.pIdentity = g_pCoreIdent;
   304 		sec.pOwner = m_pBasic->GetParentContext()->GetIdentity();
   305 	
   306 		HandleAccess access;
   307 		g_HandleSys.InitAccessDefaults(NULL, &access);
   308 		access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
   309 
   310 		Handle_t hndl = g_HandleSys.CreateHandleEx(g_MenuHelpers.GetTempPanelType(), panel, &sec, &access, NULL);
   311 
   312 		DoAction(menu, MenuAction_Display, client, hndl);
   313 
   314 		g_HandleSys.FreeHandle(hndl, &sec);
   315 	}
   316 }
   317 
   318 void CMenuHandler::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
   319 {
   320 	/* Save old position first. */
   321 	unsigned int first_item = item_on_page;
   322 	unsigned int *old_pos = s_CurSelectPosition;
   323 
   324 	s_CurSelectPosition = &first_item;
   325 
   326 	unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
   327 	DoAction(menu, MenuAction_Select, client, item);
   328 	g_ChatTriggers.SetReplyTo(old_reply);
   329 
   330 	s_CurSelectPosition = old_pos;
   331 }
   332 
   333 void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   334 {
   335 	unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
   336 	DoAction(menu, MenuAction_Cancel, client, (cell_t)reason);
   337 	g_ChatTriggers.SetReplyTo(old_reply);
   338 }
   339 
   340 void CMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
   341 {
   342 	DoAction(menu, MenuAction_End, reason, 0);
   343 }
   344 
   345 void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
   346 {
   347 	g_MenuHelpers.FreeMenuHandler(this);
   348 }
   349 
   350 void CMenuHandler::OnMenuVoteStart(IBaseMenu *menu)
   351 {
   352 	DoAction(menu, MenuAction_VoteStart, 0, 0);
   353 }
   354 
   355 void CMenuHandler::OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason)
   356 {
   357 	DoAction(menu, MenuAction_VoteCancel, reason, 0);
   358 }
   359 
   360 void CMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
   361 {
   362 	if ((m_Flags & (int)MenuAction_DrawItem) == (int)MenuAction_DrawItem)
   363 	{
   364 		cell_t result = DoAction(menu, MenuAction_DrawItem, client, item, style);
   365 		style = (unsigned int)result;
   366 	}
   367 }
   368 
   369 unsigned int CMenuHandler::OnMenuDisplayItem(IBaseMenu *menu,
   370 									 int client,
   371 									 IMenuPanel *panel,
   372 									 unsigned int item,
   373 									 const ItemDrawInfo &dr)
   374 {
   375 	if ((m_Flags & (int)MenuAction_DisplayItem) == (int)MenuAction_DisplayItem)
   376 	{
   377 		IMenuPanel *oldpanel = s_pCurPanel;
   378 		unsigned int oldret = s_CurPanelReturn;
   379 		const ItemDrawInfo *oldinfo = s_CurDrawInfo;
   380 		s_pCurPanel = panel;
   381 		s_CurPanelReturn = 0;
   382 		s_CurDrawInfo = &dr;
   383 
   384 		cell_t res = DoAction(menu, MenuAction_DisplayItem, client, item, 0);
   385 
   386 		if (!res)
   387 		{
   388 			res = s_CurPanelReturn;
   389 		}
   390 
   391 		s_pCurPanel = oldpanel;
   392 		s_CurPanelReturn = oldret;
   393 		s_CurDrawInfo = oldinfo;
   394 
   395 		return res;
   396 	}
   397 
   398 	return 0;
   399 }
   400 
   401 cell_t CMenuHandler::DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res)
   402 {
   403 #if defined MENU_DEBUG
   404 	g_Logger.LogMessage("[SM_MENU] CMenuHandler::DoAction() (menu %p/%08x) (action %d) (param1 %d) (param2 %d)",
   405 		menu,
   406 		menu->GetHandle(),
   407 		action,
   408 		param1,
   409 		param2);
   410 #endif
   411 	cell_t res = def_res;
   412 	m_pBasic->PushCell(menu->GetHandle());
   413 	m_pBasic->PushCell((cell_t)action);
   414 	m_pBasic->PushCell(param1);
   415 	m_pBasic->PushCell(param2);
   416 	m_pBasic->Execute(&res);
   417 	return res;
   418 }
   419 
   420 void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
   421 {
   422 	if (!m_pVoteResults)
   423 	{
   424 		/* Call MenuAction_VoteEnd instead.  See if there are any extra winners. */
   425 		unsigned int num_items = 1;
   426 		for (unsigned int i=1; i<results->num_items; i++)
   427 		{
   428 			if (results->item_list[i].count != results->item_list[0].count)
   429 			{
   430 				break;
   431 			}
   432 			num_items++;
   433 		}
   434 
   435 		/* See if we need to pick a random winner. */
   436 		unsigned int winning_item;
   437 		if (num_items > 1)
   438 		{
   439 			/* Yes, we do. */
   440 			srand(time(NULL));
   441 			winning_item = rand() % num_items;
   442 			winning_item = results->item_list[winning_item].item;
   443 		} else {
   444 			/* No, take the first. */
   445 			winning_item = results->item_list[0].item;
   446 		}
   447 
   448 		unsigned int total_votes = results->num_votes;
   449 		unsigned int winning_votes = results->item_list[0].count;
   450 
   451 		DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
   452 	} else {
   453 		IPluginContext *pContext = m_pVoteResults->GetParentContext();
   454 		bool no_call = false;
   455 		int err;
   456 
   457 		/* First array */
   458 		cell_t client_array_address = -1;
   459 		cell_t *client_array_base = NULL;
   460 		cell_t client_array_size = results->num_clients + (results->num_clients * 2);
   461 		if (client_array_size)
   462 		{
   463 			if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
   464 				!= SP_ERROR_NONE)
   465 			{
   466 				g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
   467 				no_call = true;
   468 			} else {
   469 				cell_t target_offs = sizeof(cell_t) * results->num_clients;
   470 				cell_t *cur_index = client_array_base;
   471 				cell_t *cur_array;
   472 				for (unsigned int i=0; i<results->num_clients; i++)
   473 				{
   474 					/* Copy the array index */
   475 					*cur_index = target_offs;
   476 					/* Get the current array address */
   477 					cur_array = (cell_t *)((char *)cur_index + target_offs);
   478 					/* Store information */
   479 					cur_array[0] = results->client_list[i].client;
   480 					cur_array[1] = results->client_list[i].item;
   481 					/* Adjust for the new target by subtracting one indirection
   482 					 * and adding one array.
   483 					 */
   484 					target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
   485 					cur_index++;
   486 				}
   487 			}
   488 		}
   489 
   490 		/* Second array */
   491 		cell_t item_array_address = -1;
   492 		cell_t *item_array_base = NULL;
   493 		cell_t item_array_size = results->num_items + (results->num_items * 2);
   494 		if (item_array_size)
   495 		{
   496 			if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
   497 				!= SP_ERROR_NONE)
   498 			{
   499 				g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
   500 				no_call = true;
   501 			} else {
   502 				cell_t target_offs = sizeof(cell_t) * results->num_items;
   503 				cell_t *cur_index = item_array_base;
   504 				cell_t *cur_array;
   505 				for (unsigned int i=0; i<results->num_items; i++)
   506 				{
   507 					/* Copy the array index */
   508 					*cur_index = target_offs;
   509 					/* Get the current array address */
   510 					cur_array = (cell_t *)((char *)cur_index + target_offs);
   511 					/* Store information */
   512 					cur_array[0] = results->item_list[i].item;
   513 					cur_array[1] = results->item_list[i].count;
   514 					/* Adjust for the new target by subtracting one indirection
   515 					 * and adding one array.
   516 					 */
   517 					target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
   518 					cur_index++;
   519 				}
   520 			}
   521 		}
   522 
   523 		/* Finally, push everything */
   524 		if (!no_call)
   525 		{
   526 			m_pVoteResults->PushCell(menu->GetHandle());
   527 			m_pVoteResults->PushCell(results->num_votes);
   528 			m_pVoteResults->PushCell(results->num_clients);
   529 			m_pVoteResults->PushCell(client_array_address);
   530 			m_pVoteResults->PushCell(results->num_items);
   531 			m_pVoteResults->PushCell(item_array_address);
   532 			m_pVoteResults->Execute(NULL);
   533 		}
   534 
   535 		/* Free what we allocated, in reverse order as required */
   536 		if (item_array_address != -1)
   537 		{
   538 			pContext->HeapPop(item_array_address);
   539 		}
   540 		if (client_array_address != -1)
   541 		{
   542 			pContext->HeapPop(client_array_address);
   543 		}
   544 	}
   545 }
   546 
   547 bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
   548 {
   549 	if (strcmp(option, "set_vote_results_handler") == 0)
   550 	{
   551 		void **array = (void **)data;
   552 		m_pVoteResults = (IPluginFunction *)array[0];
   553 		m_fnVoteResult = *(cell_t *)((cell_t *)array[1]);
   554 		return true;
   555 	}
   556 
   557 	return false;
   558 }
   559 
   560 /**
   561  * INLINE FUNCTIONS FOR NATIVES
   562  */
   563 
   564 inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
   565 {
   566 	return g_HandleSys.CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
   567 }
   568 
   569 inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
   570 {
   571 	HandleSecurity sec;
   572 	sec.pIdentity = g_pCoreIdent;
   573 	sec.pOwner = NULL;
   574 	return g_HandleSys.ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
   575 }
   576 
   577 inline IMenuStyle *GetStyleFromCell(cell_t cell)
   578 {
   579 	enum MenuStyle
   580 	{
   581 		MenuStyle_Default = 0,		/**< The "default" menu style for the mod */
   582 		MenuStyle_Valve = 1,		/**< The Valve provided menu style (Used on HL2DM) */
   583 		MenuStyle_Radio = 2,		/**< The simpler menu style commonly used on CS:S */
   584 	};
   585 
   586 	if (cell == MenuStyle_Valve)
   587 	{
   588 		return &g_ValveMenuStyle;
   589 	} else if (cell == MenuStyle_Radio
   590 			   && g_RadioMenuStyle.IsSupported())
   591 	{
   592 		return &g_RadioMenuStyle;
   593 	}
   594 
   595 	return g_Menus.GetDefaultStyle();
   596 }
   597 
   598 /***********************************
   599  **** NATIVE DEFINITIONS ***********
   600  ***********************************/
   601 
   602 static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
   603 {
   604 	IMenuStyle *style = g_Menus.GetDefaultStyle();
   605 	IPluginFunction *pFunction;
   606 
   607 	if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
   608 	{
   609 		return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
   610 	}
   611 
   612 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
   613 	IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
   614 
   615 	Handle_t hndl = menu->GetHandle();
   616 	if (!hndl)
   617 	{
   618 		menu->Destroy();
   619 		return BAD_HANDLE;
   620 	}
   621 
   622 	return hndl;
   623 }
   624 
   625 static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
   626 {
   627 	Handle_t hndl = (Handle_t)params[1];
   628 	HandleError err;
   629 	IBaseMenu *menu;
   630 
   631 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   632 	{
   633 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   634 	}
   635 
   636 	return menu->Display(params[2], params[3]) ? 1 : 0;
   637 }
   638 
   639 static cell_t DisplayMenuAtItem(IPluginContext *pContext, const cell_t *params)
   640 {
   641 	Handle_t hndl = (Handle_t)params[1];
   642 	HandleError err;
   643 	IBaseMenu *menu;
   644 
   645 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   646 	{
   647 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   648 	}
   649 
   650 	return menu->DisplayAtItem(params[2], params[4], params[3]) ? 1 : 0;
   651 }
   652 
   653 static cell_t VoteMenu(IPluginContext *pContext, const cell_t *params)
   654 {
   655 	if (g_Menus.IsVoteInProgress())
   656 	{
   657 		return pContext->ThrowNativeError("A vote is already in progress");
   658 	}
   659 
   660 	Handle_t hndl = (Handle_t)params[1];
   661 	HandleError err;
   662 	IBaseMenu *menu;
   663 
   664 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   665 	{
   666 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   667 	}
   668 
   669 	cell_t *addr;
   670 	pContext->LocalToPhysAddr(params[2], &addr);
   671 
   672 	if (!g_Menus.StartVote(menu, params[3], addr, params[4]))
   673 	{
   674 		return 0;
   675 	}
   676 
   677 	return 1;
   678 }
   679 
   680 static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
   681 {
   682 	Handle_t hndl = (Handle_t)params[1];
   683 	HandleError err;
   684 	IBaseMenu *menu;
   685 
   686 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   687 	{
   688 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   689 	}
   690 
   691 	char *info;
   692 	ItemDrawInfo dr;
   693 
   694 	pContext->LocalToString(params[2], &info);
   695 	pContext->LocalToString(params[3], (char **)&dr.display);
   696 	dr.style = params[4];
   697 
   698 	return menu->AppendItem(info, dr) ? 1 : 0;
   699 }
   700 
   701 static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
   702 {
   703 	Handle_t hndl = (Handle_t)params[1];
   704 	HandleError err;
   705 	IBaseMenu *menu;
   706 
   707 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   708 	{
   709 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   710 	}
   711 
   712 	char *info;
   713 	ItemDrawInfo dr;
   714 
   715 	pContext->LocalToString(params[3], &info);
   716 	pContext->LocalToString(params[4], (char **)&dr.display);
   717 	dr.style = params[5];
   718 
   719 	return menu->InsertItem(params[2], info, dr) ? 1 : 0;
   720 }
   721 
   722 static cell_t RemoveMenuItem(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 	return menu->RemoveItem(params[2]) ? 1 : 0;
   734 }
   735 
   736 static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
   737 {
   738 	Handle_t hndl = (Handle_t)params[1];
   739 	HandleError err;
   740 	IBaseMenu *menu;
   741 
   742 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   743 	{
   744 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   745 	}
   746 
   747 	menu->RemoveAllItems();
   748 
   749 	return 1;
   750 }
   751 
   752 static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
   753 {
   754 	Handle_t hndl = (Handle_t)params[1];
   755 	HandleError err;
   756 	IBaseMenu *menu;
   757 
   758 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   759 	{
   760 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   761 	}
   762 
   763 	ItemDrawInfo dr;
   764 	const char *info;
   765 
   766 	if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
   767 	{
   768 		return 0;
   769 	}
   770 
   771 	pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
   772 	pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
   773 
   774 	cell_t *addr;
   775 	pContext->LocalToPhysAddr(params[5], &addr);
   776 	*addr = dr.style;
   777 
   778 	return 1;
   779 }
   780 
   781 static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
   782 {
   783 	Handle_t hndl = (Handle_t)params[1];
   784 	HandleError err;
   785 	IBaseMenu *menu;
   786 
   787 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   788 	{
   789 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   790 	}
   791 
   792 	return menu->SetPagination(params[2]) ? 1 : 0;
   793 }
   794 
   795 static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
   796 {
   797 	Handle_t hndl = (Handle_t)params[1];
   798 	HandleError err;
   799 	IBaseMenu *menu;
   800 
   801 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   802 	{
   803 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   804 	}
   805 
   806 	return menu->GetPagination();
   807 }
   808 
   809 static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
   810 {
   811 	Handle_t hndl = (Handle_t)params[1];
   812 	HandleError err;
   813 	IBaseMenu *menu;
   814 
   815 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   816 	{
   817 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   818 	}
   819 
   820 	return menu->GetItemCount();
   821 }
   822 
   823 static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
   824 {
   825 	Handle_t hndl = (Handle_t)params[1];
   826 	HandleError err;
   827 	IBaseMenu *menu;
   828 
   829 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   830 	{
   831 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   832 	}
   833 
   834 	g_SourceMod.SetGlobalTarget(LANG_SERVER);
   835 
   836 	char buffer[1024];
   837 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   838 
   839 	menu->SetDefaultTitle(buffer);
   840 
   841 	return 1;
   842 }
   843 
   844 static cell_t GetMenuTitle(IPluginContext *pContext, const cell_t *params)
   845 {
   846 	Handle_t hndl = (Handle_t)params[1];
   847 	HandleError err;
   848 	IBaseMenu *menu;
   849 
   850 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   851 	{
   852 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   853 	}
   854 
   855 	size_t written;
   856 	const char *title = menu->GetDefaultTitle();
   857 	pContext->StringToLocalUTF8(params[2], params[3], title, &written);
   858 
   859 	return (cell_t)written;
   860 }
   861 
   862 
   863 static cell_t CreatePanelFromMenu(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 	IMenuPanel *panel = menu->CreatePanel();
   875 	hndl = MakePanelHandle(panel, pContext);
   876 	if (!hndl)
   877 	{
   878 		panel->DeleteThis();
   879 	}
   880 
   881 	return hndl;
   882 }
   883 
   884 static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   885 {
   886 	Handle_t hndl = (Handle_t)params[1];
   887 	HandleError err;
   888 	IBaseMenu *menu;
   889 
   890 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   891 	{
   892 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   893 	}
   894 
   895 	return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
   896 }
   897 
   898 static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
   899 {
   900 	Handle_t hndl = (Handle_t)params[1];
   901 	HandleError err;
   902 	IBaseMenu *menu;
   903 
   904 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   905 	{
   906 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   907 	}
   908 
   909 	return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
   910 }
   911 
   912 static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   913 {
   914 	Handle_t hndl = (Handle_t)params[1];
   915 	HandleError err;
   916 	IBaseMenu *menu;
   917 
   918 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   919 	{
   920 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   921 	}
   922 
   923 	unsigned int flags = menu->GetMenuOptionFlags();
   924 
   925 	if (params[2])
   926 	{
   927 		flags |= MENUFLAG_BUTTON_EXIT;
   928 	} else {
   929 		flags &= ~MENUFLAG_BUTTON_EXIT;
   930 	}
   931 
   932 	menu->SetMenuOptionFlags(flags);
   933 	unsigned int new_flags = menu->GetMenuOptionFlags();
   934 
   935 	return (flags == new_flags);
   936 }
   937 
   938 static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
   939 {
   940 	Handle_t hndl = (Handle_t)params[1];
   941 	HandleError err;
   942 	IBaseMenu *menu;
   943 
   944 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   945 	{
   946 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   947 	}
   948 
   949 	unsigned int flags = menu->GetMenuOptionFlags();
   950 
   951 	if (params[2])
   952 	{
   953 		flags |= MENUFLAG_BUTTON_EXITBACK;
   954 	} else {
   955 		flags &= ~MENUFLAG_BUTTON_EXITBACK;
   956 	}
   957 
   958 	menu->SetMenuOptionFlags(flags);
   959 
   960 	return 1;
   961 }
   962 
   963 static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
   964 {
   965 	Handle_t hndl = (Handle_t)params[1];
   966 	HandleError err;
   967 	IBaseMenu *menu;
   968 
   969 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   970 	{
   971 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   972 	}
   973 
   974 	g_Menus.CancelMenu(menu);
   975 
   976 	return 1;
   977 }
   978 
   979 static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
   980 {
   981 	return g_Menus.IsVoteInProgress() ? 1 : 0;
   982 }
   983 
   984 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
   985 {
   986 	Handle_t hndl = (Handle_t)params[1];
   987 	HandleError err;
   988 	IBaseMenu *menu;
   989 
   990 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   991 	{
   992 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   993 	}
   994 
   995 	return menu->GetDrawStyle()->GetHandle();
   996 }
   997 
   998 static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
   999 {
  1000 	IMenuStyle *style = GetStyleFromCell(params[1]);
  1001 	if (!style)
  1002 	{
  1003 		return BAD_HANDLE;
  1004 	}
  1005 
  1006 	return style->GetHandle();
  1007 }
  1008 
  1009 static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
  1010 {
  1011 	Handle_t hndl = (Handle_t)params[1];
  1012 	HandleError err;
  1013 	IMenuStyle *style;
  1014 
  1015 	if (hndl != 0)
  1016 	{
  1017 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1018 		{
  1019 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1020 		}
  1021 	} else {
  1022 		style = g_Menus.GetDefaultStyle();
  1023 	}
  1024 
  1025 	IMenuPanel *panel = style->CreatePanel();
  1026 
  1027 	hndl = MakePanelHandle(panel, pContext);
  1028 	if (!hndl)
  1029 	{
  1030 		panel->DeleteThis();
  1031 		return BAD_HANDLE;
  1032 	}
  1033 
  1034 	return hndl;
  1035 }
  1036 
  1037 static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
  1038 {
  1039 	Handle_t hndl = (Handle_t)params[1];
  1040 	HandleError err;
  1041 	IMenuStyle *style;
  1042 
  1043 	if (hndl != 0)
  1044 	{
  1045 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1046 		{
  1047 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1048 		}
  1049 	} else {
  1050 		style = g_Menus.GetDefaultStyle();
  1051 	}
  1052 
  1053 	IPluginFunction *pFunction;
  1054 	if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
  1055 	{
  1056 		return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
  1057 	}
  1058 
  1059 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
  1060 
  1061 	IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
  1062 	hndl = pMenu->GetHandle();
  1063 	if (!hndl)
  1064 	{
  1065 		pMenu->Destroy();
  1066 		return BAD_HANDLE;
  1067 	}
  1068 
  1069 	return hndl;
  1070 }
  1071 
  1072 static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
  1073 {
  1074 	Handle_t hndl = (Handle_t)params[2];
  1075 	HandleError err;
  1076 	IMenuStyle *style;
  1077 
  1078 	if (hndl != 0)
  1079 	{
  1080 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1081 		{
  1082 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1083 		}
  1084 	} else {
  1085 		style = g_Menus.GetDefaultStyle();
  1086 	}
  1087 
  1088 	return style->GetClientMenu(params[1], NULL);
  1089 }
  1090 
  1091 static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
  1092 {
  1093 	Handle_t hndl = (Handle_t)params[3];
  1094 	HandleError err;
  1095 	IMenuStyle *style;
  1096 
  1097 	if (hndl != 0)
  1098 	{
  1099 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1100 		{
  1101 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1102 		}
  1103 	} else {
  1104 		style = g_Menus.GetDefaultStyle();
  1105 	}
  1106 
  1107 	return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
  1108 }
  1109 
  1110 static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
  1111 {
  1112 	Handle_t hndl = (Handle_t)params[1];
  1113 	HandleError err;
  1114 	IMenuStyle *style;
  1115 
  1116 	if (hndl != 0)
  1117 	{
  1118 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1119 		{
  1120 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1121 		}
  1122 	} else {
  1123 		style = g_Menus.GetDefaultStyle();
  1124 	}
  1125 
  1126 	return style->GetMaxPageItems();
  1127 }
  1128 
  1129 static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
  1130 {
  1131 	Handle_t hndl = (Handle_t)params[1];
  1132 	HandleError err;
  1133 	IMenuPanel *panel;
  1134 
  1135 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1136 	{
  1137 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1138 	}
  1139 
  1140 	return panel->GetParentStyle()->GetHandle();
  1141 }
  1142 
  1143 static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
  1144 {
  1145 	Handle_t hndl = (Handle_t)params[1];
  1146 	HandleError err;
  1147 	IMenuPanel *panel;
  1148 
  1149 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1150 	{
  1151 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1152 	}
  1153 
  1154 	char *text;
  1155 	pContext->LocalToString(params[2], &text);
  1156 
  1157 	panel->DrawTitle(text, params[3] ? true : false);
  1158 
  1159 	return 1;
  1160 }
  1161 
  1162 static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
  1163 {
  1164 	Handle_t hndl = (Handle_t)params[1];
  1165 	HandleError err;
  1166 	IMenuPanel *panel;
  1167 
  1168 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1169 	{
  1170 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1171 	}
  1172 
  1173 	ItemDrawInfo dr;
  1174 	pContext->LocalToString(params[2], (char **)&dr.display);
  1175 	dr.style = params[3];
  1176 
  1177 	return panel->DrawItem(dr);
  1178 }
  1179 
  1180 static cell_t DrawPanelText(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 	return panel->DrawRawLine(text) ? 1 : 0;
  1195 }
  1196 
  1197 static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
  1198 {
  1199 	Handle_t hndl = (Handle_t)params[1];
  1200 	HandleError err;
  1201 	IMenuPanel *panel;
  1202 
  1203 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1204 	{
  1205 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1206 	}
  1207 
  1208 	return panel->CanDrawItem(params[2]);
  1209 }
  1210 
  1211 static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
  1212 {
  1213 	Handle_t hndl = (Handle_t)params[1];
  1214 	HandleError err;
  1215 	IMenuPanel *panel;
  1216 
  1217 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1218 	{
  1219 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1220 	}
  1221 
  1222 	IPluginFunction *pFunction;
  1223 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
  1224 	{
  1225 		return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
  1226 	}
  1227 
  1228 	CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
  1229 	if (!panel->SendDisplay(params[2], handler, params[4]))
  1230 	{
  1231 		g_MenuHelpers.FreePanelHandler(handler);
  1232 	}
  1233 
  1234 	return 1;
  1235 }
  1236 
  1237 static cell_t SetPanelKeys(IPluginContext *pContext, const cell_t *params)
  1238 {
  1239 	Handle_t hndl = (Handle_t)params[1];
  1240 	HandleError err;
  1241 	IMenuPanel *panel;
  1242 
  1243 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1244 	{
  1245 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1246 	}
  1247 
  1248 	return panel->SetSelectableKeys(params[2]);
  1249 }
  1250 
  1251 static cell_t GetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
  1252 {
  1253 	Handle_t hndl = (Handle_t)params[1];
  1254 	HandleError err;
  1255 	IMenuPanel *panel;
  1256 
  1257 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1258 	{
  1259 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1260 	}
  1261 
  1262 	return panel->GetCurrentKey();
  1263 }
  1264 
  1265 static cell_t GetPanelTextRemaining(IPluginContext *pContext, const cell_t *params)
  1266 {
  1267 	Handle_t hndl = (Handle_t)params[1];
  1268 	HandleError err;
  1269 	IMenuPanel *panel;
  1270 
  1271 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1272 	{
  1273 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1274 	}
  1275 
  1276 	return panel->GetAmountRemaining();
  1277 }
  1278 
  1279 static cell_t SetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
  1280 {
  1281 	Handle_t hndl = (Handle_t)params[1];
  1282 	HandleError err;
  1283 	IMenuPanel *panel;
  1284 
  1285 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1286 	{
  1287 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1288 	}
  1289 
  1290 	return panel->SetCurrentKey(params[2]) ? 1 : 0;
  1291 }
  1292 
  1293 static cell_t RedrawMenuItem(IPluginContext *pContext, const cell_t *params)
  1294 {
  1295 	if (!s_pCurPanel)
  1296 	{
  1297 		return pContext->ThrowNativeError("You can only call this once from a MenuAction_DisplayItem callback");
  1298 	}
  1299 
  1300 	char *str;
  1301 	pContext->LocalToString(params[1], &str);
  1302 
  1303 	ItemDrawInfo dr = *s_CurDrawInfo;
  1304 	dr.display = str;
  1305 
  1306 	if ((s_CurPanelReturn = s_pCurPanel->DrawItem(dr)) != 0)
  1307 	{
  1308 		s_pCurPanel = NULL;
  1309 	}
  1310 
  1311 	return s_CurPanelReturn;
  1312 }
  1313 
  1314 static cell_t GetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
  1315 {
  1316 	Handle_t hndl = (Handle_t)params[1];
  1317 	HandleError err;
  1318 	IBaseMenu *menu;
  1319 
  1320 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1321 	{
  1322 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1323 	}
  1324 
  1325 	return menu->GetMenuOptionFlags();
  1326 }
  1327 
  1328 static cell_t SetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
  1329 {
  1330 	Handle_t hndl = (Handle_t)params[1];
  1331 	HandleError err;
  1332 	IBaseMenu *menu;
  1333 
  1334 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1335 	{
  1336 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1337 	}
  1338 
  1339 	menu->SetMenuOptionFlags(params[2]);
  1340 
  1341 	return 1;
  1342 }
  1343 
  1344 static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
  1345 {
  1346 	if (!g_Menus.IsVoteInProgress())
  1347 	{
  1348 		return pContxt->ThrowNativeError("No vote is in progress");
  1349 	}
  1350 
  1351 	g_Menus.CancelVoting();
  1352 
  1353 	return 1;
  1354 }
  1355 
  1356 static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
  1357 {
  1358 	Handle_t hndl = (Handle_t)params[1];
  1359 	HandleError err;
  1360 	IBaseMenu *menu;
  1361 
  1362 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1363 	{
  1364 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1365 	}
  1366 
  1367 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
  1368 	if (!pFunction)
  1369 	{
  1370 		return pContext->ThrowNativeError("Invalid function %x", params[2]);
  1371 	}
  1372 
  1373 	void *array[2];
  1374 	array[0] = pFunction;
  1375 	array[1] = (void *)&params[2];
  1376 
  1377 	IMenuHandler *pHandler = menu->GetHandler();
  1378 	if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
  1379 	{
  1380 		return pContext->ThrowNativeError("The given menu does not support this option");
  1381 	}
  1382 
  1383 	return 1;
  1384 }
  1385 
  1386 static cell_t CheckVoteDelay(IPluginContext *pContext, const cell_t *params)
  1387 {
  1388 	return g_Menus.GetRemainingVoteDelay();
  1389 }
  1390 
  1391 static cell_t GetMenuSelectionPosition(IPluginContext *pContext, const cell_t *params)
  1392 {
  1393 	if (!s_CurSelectPosition)
  1394 	{
  1395 		return pContext->ThrowNativeError("Can only be called from inside a MenuAction_Select callback");
  1396 	}
  1397 
  1398 	return *s_CurSelectPosition;
  1399 }
  1400 
  1401 REGISTER_NATIVES(menuNatives)
  1402 {
  1403 	{"AddMenuItem",				AddMenuItem},
  1404 	{"CanPanelDrawFlags",		CanPanelDrawFlags},
  1405 	{"CancelClientMenu",		CancelClientMenu},
  1406 	{"CancelMenu",				CancelMenu},
  1407 	{"CancelVote",				CancelVote},
  1408 	{"CheckVoteDelay",			CheckVoteDelay},
  1409 	{"CreateMenu",				CreateMenu},
  1410 	{"CreateMenuEx",			CreateMenuEx},
  1411 	{"CreatePanel",				CreatePanel},
  1412 	{"CreatePanelFromMenu",		CreatePanelFromMenu},
  1413 	{"DisplayMenu",				DisplayMenu},
  1414 	{"DisplayMenuAtItem",		DisplayMenuAtItem},
  1415 	{"DrawPanelItem",			DrawPanelItem},
  1416 	{"DrawPanelText",			DrawPanelText},
  1417 	{"GetClientMenu",			GetClientMenu},
  1418 	{"GetMaxPageItems",			GetMaxPageItems},
  1419 	{"GetMenuExitBackButton",	GetMenuExitBackButton},
  1420 	{"GetMenuExitButton",		GetMenuExitButton},
  1421 	{"GetMenuItem",				GetMenuItem},
  1422 	{"GetMenuItemCount",		GetMenuItemCount},
  1423 	{"GetMenuOptionFlags",		GetMenuOptionFlags},
  1424 	{"GetMenuPagination",		GetMenuPagination},
  1425 	{"GetMenuSelectionPosition",GetMenuSelectionPosition},
  1426 	{"GetMenuStyle",			GetMenuStyle},
  1427 	{"GetMenuStyleHandle",		GetMenuStyleHandle},
  1428 	{"GetMenuTitle",			GetMenuTitle},
  1429 	{"GetPanelTextRemaining",	GetPanelTextRemaining},
  1430 	{"GetPanelCurrentKey",		GetPanelCurrentKey},
  1431 	{"GetPanelStyle",			GetPanelStyle},
  1432 	{"InsertMenuItem",			InsertMenuItem},
  1433 	{"IsVoteInProgress",		IsVoteInProgress},
  1434 	{"RedrawMenuItem",			RedrawMenuItem},
  1435 	{"RemoveAllMenuItems",		RemoveAllMenuItems},
  1436 	{"RemoveMenuItem",			RemoveMenuItem},
  1437 	{"SendPanelToClient",		SendPanelToClient},
  1438 	{"SetMenuExitBackButton",	SetMenuExitBackButton},
  1439 	{"SetMenuExitButton",		SetMenuExitButton},
  1440 	{"SetMenuOptionFlags",		SetMenuOptionFlags},
  1441 	{"SetMenuPagination",		SetMenuPagination},
  1442 	{"SetMenuTitle",			SetMenuTitle},
  1443 	{"SetPanelCurrentKey",		SetPanelCurrentKey},
  1444 	{"SetPanelTitle",			SetPanelTitle},
  1445 	{"SetPanelKeys",			SetPanelKeys},
  1446 	{"SetVoteResultCallback",	SetVoteResultCallback},
  1447 	{"VoteMenu",				VoteMenu},
  1448 	{NULL,						NULL},
  1449 };