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