core/smn_menus.cpp
author David Anderson <dvander@alliedmods.net>
Mon Feb 08 19:58:36 2010 -0800 (2010-02-08)
changeset 2915 9aeeddddd22e
parent 2737 eb7713d87fa1
child 2928 1f7626e1a49d
permissions -rw-r--r--
Fixed GetClientCookieTime not being exported (bug 4237, r=fyren).
     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 "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 "PlayerManager.h"
    41 #include "sm_stringutil.h"
    42 #include "sourcemm_api.h"
    43 #if defined MENU_DEBUG
    44 #include "Logger.h"
    45 #endif
    46 #include "ChatTriggers.h"
    47 
    48 #if defined CreateMenu
    49 #undef CreateMenu
    50 #endif
    51 
    52 /**
    53  * And God said, "let there be menus," and behold, there were menus.  
    54  * God saw the menus and they were good.  And the evening and the morning
    55  * were the third day.
    56  */
    57 
    58 enum MenuAction
    59 {
    60 	MenuAction_Start = (1<<0),		/**< A menu has been started (nothing passed) */
    61 	MenuAction_Display = (1<<1),	/**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */
    62 	MenuAction_Select = (1<<2),		/**< An item was selected (param1=client, param2=item) */
    63 	MenuAction_Cancel = (1<<3),		/**< The menu was cancelled (param1=client, param2=item) */
    64 	MenuAction_End = (1<<4),		/**< A menu's display/selection cycle is complete (nothing passed). */
    65 	MenuAction_VoteEnd = (1<<5),	/**< (VOTE ONLY): A vote sequence has ended (param1=chosen item) */
    66 	MenuAction_VoteStart = (1<<6), 	/**< (VOTE ONLY): A vote sequence has started */
    67 	MenuAction_VoteCancel = (1<<7),	/**< (VOTE ONLY): A vote sequence has been cancelled (nothing passed) */
    68 	MenuAction_DrawItem = (1<<8),	/**< A style is being drawn; return the new style (param1=client, param2=item) */
    69 	MenuAction_DisplayItem = (1<<9),	/**< the odd duck */
    70 };
    71 
    72 class CPanelHandler : public IMenuHandler
    73 {
    74 	friend class MenuNativeHelpers;
    75 public:
    76 	CPanelHandler()
    77 	{
    78 	}
    79 	void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
    80 	void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
    81 private:
    82 	IPluginFunction *m_pFunc;
    83 	IPlugin *m_pPlugin;
    84 };
    85 
    86 class CMenuHandler : public IMenuHandler
    87 {
    88 	friend class MenuNativeHelpers;
    89 public:
    90 	CMenuHandler(IPluginFunction *pBasic, int flags);
    91 public:
    92 	void OnMenuStart(IBaseMenu *menu);
    93 	void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
    94 	void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page);
    95 	void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
    96 	void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
    97 	void OnMenuDestroy(IBaseMenu *menu);
    98 	void OnMenuVoteStart(IBaseMenu *menu);
    99 	void OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results);
   100 	void OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason);
   101 	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
   102 	unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
   103 	bool OnSetHandlerOption(const char *option, const void *data);
   104 #if 0
   105 	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
   106 	void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
   107 #endif
   108 private:
   109 	cell_t DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res=0);
   110 private:
   111 	IPluginFunction *m_pBasic;
   112 	int m_Flags;
   113 	IPluginFunction *m_pVoteResults;
   114 	cell_t m_fnVoteResult;
   115 };
   116 
   117 /**
   118  * GLOBAL CLASS FOR HELPERS
   119  */
   120 
   121 class MenuNativeHelpers : 
   122 	public SMGlobalClass,
   123 	public IHandleTypeDispatch,
   124 	public IPluginsListener
   125 {
   126 public:
   127 	virtual void OnSourceModAllInitialized()
   128 	{
   129 		m_PanelType = g_HandleSys.CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
   130 		m_TempPanelType = g_HandleSys.CreateType("TempIMenuPanel", this, m_PanelType, NULL, NULL, g_pCoreIdent, NULL);
   131 		g_PluginSys.AddPluginsListener(this);
   132 	}
   133 
   134 	virtual void OnSourceModShutdown()
   135 	{
   136 		g_HandleSys.RemoveType(m_TempPanelType, g_pCoreIdent);
   137 		g_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 = g_PluginSys.GetPluginByCtx(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 		g_HandleSys.InitAccessDefaults(NULL, &access);
   315 		access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
   316 
   317 		Handle_t hndl = g_HandleSys.CreateHandleEx(g_MenuHelpers.GetTempPanelType(), panel, &sec, &access, NULL);
   318 
   319 		DoAction(menu, MenuAction_Display, client, hndl);
   320 
   321 		g_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 				g_DbgReporter.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 				g_DbgReporter.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 g_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 g_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 SetMenuExitBackButton(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_EXITBACK;
   967 	} else {
   968 		flags &= ~MENUFLAG_BUTTON_EXITBACK;
   969 	}
   970 
   971 	menu->SetMenuOptionFlags(flags);
   972 
   973 	return 1;
   974 }
   975 
   976 static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
   977 {
   978 	Handle_t hndl = (Handle_t)params[1];
   979 	HandleError err;
   980 	IBaseMenu *menu;
   981 
   982 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   983 	{
   984 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   985 	}
   986 
   987 	g_Menus.CancelMenu(menu);
   988 
   989 	return 1;
   990 }
   991 
   992 static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
   993 {
   994 	return g_Menus.IsVoteInProgress() ? 1 : 0;
   995 }
   996 
   997 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
   998 {
   999 	Handle_t hndl = (Handle_t)params[1];
  1000 	HandleError err;
  1001 	IBaseMenu *menu;
  1002 
  1003 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1004 	{
  1005 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1006 	}
  1007 
  1008 	return menu->GetDrawStyle()->GetHandle();
  1009 }
  1010 
  1011 static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
  1012 {
  1013 	IMenuStyle *style = GetStyleFromCell(params[1]);
  1014 	if (!style)
  1015 	{
  1016 		return BAD_HANDLE;
  1017 	}
  1018 
  1019 	return style->GetHandle();
  1020 }
  1021 
  1022 static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
  1023 {
  1024 	Handle_t hndl = (Handle_t)params[1];
  1025 	HandleError err;
  1026 	IMenuStyle *style;
  1027 
  1028 	if (hndl != 0)
  1029 	{
  1030 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1031 		{
  1032 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1033 		}
  1034 	} else {
  1035 		style = g_Menus.GetDefaultStyle();
  1036 	}
  1037 
  1038 	IMenuPanel *panel = style->CreatePanel();
  1039 
  1040 	hndl = MakePanelHandle(panel, pContext);
  1041 	if (!hndl)
  1042 	{
  1043 		panel->DeleteThis();
  1044 		return BAD_HANDLE;
  1045 	}
  1046 
  1047 	return hndl;
  1048 }
  1049 
  1050 static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
  1051 {
  1052 	Handle_t hndl = (Handle_t)params[1];
  1053 	HandleError err;
  1054 	IMenuStyle *style;
  1055 
  1056 	if (hndl != 0)
  1057 	{
  1058 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1059 		{
  1060 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1061 		}
  1062 	} else {
  1063 		style = g_Menus.GetDefaultStyle();
  1064 	}
  1065 
  1066 	IPluginFunction *pFunction;
  1067 	if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
  1068 	{
  1069 		return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
  1070 	}
  1071 
  1072 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
  1073 
  1074 	IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
  1075 	hndl = pMenu->GetHandle();
  1076 	if (!hndl)
  1077 	{
  1078 		pMenu->Destroy();
  1079 		return BAD_HANDLE;
  1080 	}
  1081 
  1082 	return hndl;
  1083 }
  1084 
  1085 static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
  1086 {
  1087 	Handle_t hndl = (Handle_t)params[2];
  1088 	HandleError err;
  1089 	IMenuStyle *style;
  1090 
  1091 	if (hndl != 0)
  1092 	{
  1093 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1094 		{
  1095 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1096 		}
  1097 	} else {
  1098 		style = g_Menus.GetDefaultStyle();
  1099 	}
  1100 
  1101 	return style->GetClientMenu(params[1], NULL);
  1102 }
  1103 
  1104 static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
  1105 {
  1106 	Handle_t hndl = (Handle_t)params[3];
  1107 	HandleError err;
  1108 	IMenuStyle *style;
  1109 
  1110 	if (hndl != 0)
  1111 	{
  1112 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1113 		{
  1114 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1115 		}
  1116 	} else {
  1117 		style = g_Menus.GetDefaultStyle();
  1118 	}
  1119 
  1120 	return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
  1121 }
  1122 
  1123 static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
  1124 {
  1125 	Handle_t hndl = (Handle_t)params[1];
  1126 	HandleError err;
  1127 	IMenuStyle *style;
  1128 
  1129 	if (hndl != 0)
  1130 	{
  1131 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1132 		{
  1133 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1134 		}
  1135 	} else {
  1136 		style = g_Menus.GetDefaultStyle();
  1137 	}
  1138 
  1139 	return style->GetMaxPageItems();
  1140 }
  1141 
  1142 static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
  1143 {
  1144 	Handle_t hndl = (Handle_t)params[1];
  1145 	HandleError err;
  1146 	IMenuPanel *panel;
  1147 
  1148 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1149 	{
  1150 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1151 	}
  1152 
  1153 	return panel->GetParentStyle()->GetHandle();
  1154 }
  1155 
  1156 static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
  1157 {
  1158 	Handle_t hndl = (Handle_t)params[1];
  1159 	HandleError err;
  1160 	IMenuPanel *panel;
  1161 
  1162 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1163 	{
  1164 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1165 	}
  1166 
  1167 	char *text;
  1168 	pContext->LocalToString(params[2], &text);
  1169 
  1170 	panel->DrawTitle(text, params[3] ? true : false);
  1171 
  1172 	return 1;
  1173 }
  1174 
  1175 static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
  1176 {
  1177 	Handle_t hndl = (Handle_t)params[1];
  1178 	HandleError err;
  1179 	IMenuPanel *panel;
  1180 
  1181 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1182 	{
  1183 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1184 	}
  1185 
  1186 	ItemDrawInfo dr;
  1187 	pContext->LocalToString(params[2], (char **)&dr.display);
  1188 	dr.style = params[3];
  1189 
  1190 	return panel->DrawItem(dr);
  1191 }
  1192 
  1193 static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
  1194 {
  1195 	Handle_t hndl = (Handle_t)params[1];
  1196 	HandleError err;
  1197 	IMenuPanel *panel;
  1198 
  1199 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1200 	{
  1201 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1202 	}
  1203 
  1204 	char *text;
  1205 	pContext->LocalToString(params[2], &text);
  1206 
  1207 	return panel->DrawRawLine(text) ? 1 : 0;
  1208 }
  1209 
  1210 static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
  1211 {
  1212 	Handle_t hndl = (Handle_t)params[1];
  1213 	HandleError err;
  1214 	IMenuPanel *panel;
  1215 
  1216 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1217 	{
  1218 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1219 	}
  1220 
  1221 	return panel->CanDrawItem(params[2]);
  1222 }
  1223 
  1224 static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
  1225 {
  1226 	Handle_t hndl = (Handle_t)params[1];
  1227 	HandleError err;
  1228 	IMenuPanel *panel;
  1229 
  1230 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1231 	{
  1232 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1233 	}
  1234 
  1235 	IPluginFunction *pFunction;
  1236 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
  1237 	{
  1238 		return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
  1239 	}
  1240 
  1241 	CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
  1242 	if (!panel->SendDisplay(params[2], handler, params[4]))
  1243 	{
  1244 		g_MenuHelpers.FreePanelHandler(handler);
  1245 	}
  1246 
  1247 	return 1;
  1248 }
  1249 
  1250 static cell_t SetPanelKeys(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 	return panel->SetSelectableKeys(params[2]);
  1262 }
  1263 
  1264 static cell_t GetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
  1265 {
  1266 	Handle_t hndl = (Handle_t)params[1];
  1267 	HandleError err;
  1268 	IMenuPanel *panel;
  1269 
  1270 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1271 	{
  1272 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1273 	}
  1274 
  1275 	return panel->GetCurrentKey();
  1276 }
  1277 
  1278 static cell_t GetPanelTextRemaining(IPluginContext *pContext, const cell_t *params)
  1279 {
  1280 	Handle_t hndl = (Handle_t)params[1];
  1281 	HandleError err;
  1282 	IMenuPanel *panel;
  1283 
  1284 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1285 	{
  1286 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1287 	}
  1288 
  1289 	return panel->GetAmountRemaining();
  1290 }
  1291 
  1292 static cell_t SetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
  1293 {
  1294 	Handle_t hndl = (Handle_t)params[1];
  1295 	HandleError err;
  1296 	IMenuPanel *panel;
  1297 
  1298 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1299 	{
  1300 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1301 	}
  1302 
  1303 	return panel->SetCurrentKey(params[2]) ? 1 : 0;
  1304 }
  1305 
  1306 static cell_t RedrawMenuItem(IPluginContext *pContext, const cell_t *params)
  1307 {
  1308 	if (!s_pCurPanel)
  1309 	{
  1310 		return pContext->ThrowNativeError("You can only call this once from a MenuAction_DisplayItem callback");
  1311 	}
  1312 
  1313 	char *str;
  1314 	pContext->LocalToString(params[1], &str);
  1315 
  1316 	ItemDrawInfo dr = *s_CurDrawInfo;
  1317 	dr.display = str;
  1318 
  1319 	if ((s_CurPanelReturn = s_pCurPanel->DrawItem(dr)) != 0)
  1320 	{
  1321 		s_pCurPanel = NULL;
  1322 	}
  1323 
  1324 	return s_CurPanelReturn;
  1325 }
  1326 
  1327 static cell_t GetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
  1328 {
  1329 	Handle_t hndl = (Handle_t)params[1];
  1330 	HandleError err;
  1331 	IBaseMenu *menu;
  1332 
  1333 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1334 	{
  1335 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1336 	}
  1337 
  1338 	return menu->GetMenuOptionFlags();
  1339 }
  1340 
  1341 static cell_t SetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
  1342 {
  1343 	Handle_t hndl = (Handle_t)params[1];
  1344 	HandleError err;
  1345 	IBaseMenu *menu;
  1346 
  1347 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1348 	{
  1349 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1350 	}
  1351 
  1352 	menu->SetMenuOptionFlags(params[2]);
  1353 
  1354 	return 1;
  1355 }
  1356 
  1357 static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
  1358 {
  1359 	if (!g_Menus.IsVoteInProgress())
  1360 	{
  1361 		return pContxt->ThrowNativeError("No vote is in progress");
  1362 	}
  1363 
  1364 	g_Menus.CancelVoting();
  1365 
  1366 	return 1;
  1367 }
  1368 
  1369 static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
  1370 {
  1371 	Handle_t hndl = (Handle_t)params[1];
  1372 	HandleError err;
  1373 	IBaseMenu *menu;
  1374 
  1375 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1376 	{
  1377 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1378 	}
  1379 
  1380 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
  1381 	if (!pFunction)
  1382 	{
  1383 		return pContext->ThrowNativeError("Invalid function %x", params[2]);
  1384 	}
  1385 
  1386 	void *array[2];
  1387 	array[0] = pFunction;
  1388 	array[1] = (void *)&params[2];
  1389 
  1390 	IMenuHandler *pHandler = menu->GetHandler();
  1391 	if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
  1392 	{
  1393 		return pContext->ThrowNativeError("The given menu does not support this option");
  1394 	}
  1395 
  1396 	return 1;
  1397 }
  1398 
  1399 static cell_t CheckVoteDelay(IPluginContext *pContext, const cell_t *params)
  1400 {
  1401 	return g_Menus.GetRemainingVoteDelay();
  1402 }
  1403 
  1404 static cell_t GetMenuSelectionPosition(IPluginContext *pContext, const cell_t *params)
  1405 {
  1406 	if (!s_CurSelectPosition)
  1407 	{
  1408 		return pContext->ThrowNativeError("Can only be called from inside a MenuAction_Select callback");
  1409 	}
  1410 
  1411 	return *s_CurSelectPosition;
  1412 }
  1413 
  1414 static cell_t IsClientInVotePool(IPluginContext *pContext, const cell_t *params)
  1415 {
  1416 	int client;
  1417 	IGamePlayer *pPlayer;
  1418 
  1419 	client = params[1];
  1420 	if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
  1421 	{
  1422 		return pContext->ThrowNativeError("Invalid client index %d", client);
  1423 	}
  1424 
  1425 	if (!g_Menus.IsVoteInProgress())
  1426 	{
  1427 		return pContext->ThrowNativeError("No vote is in progress");
  1428 	}
  1429 
  1430 	return g_Menus.IsClientInVotePool(client) ? 1 : 0;
  1431 }
  1432 
  1433 static cell_t RedrawClientVoteMenu(IPluginContext *pContext, const cell_t *params)
  1434 {
  1435 	int client;
  1436 	IGamePlayer *pPlayer;
  1437 
  1438 	client = params[1];
  1439 	if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
  1440 	{
  1441 		return pContext->ThrowNativeError("Invalid client index %d", client);
  1442 	}
  1443 
  1444 	if (!g_Menus.IsVoteInProgress())
  1445 	{
  1446 		return pContext->ThrowNativeError("No vote is in progress");
  1447 	}
  1448 
  1449 	if (!g_Menus.IsClientInVotePool(client))
  1450 	{
  1451 		return pContext->ThrowNativeError("Client is not in the voting pool");
  1452 	}
  1453 
  1454 	bool revote = true;
  1455 	if (params[0] >= 2 && !params[2])
  1456 	{
  1457 		revote = false;
  1458 	}
  1459 
  1460 	return g_Menus.RedrawClientVoteMenu2(client, revote) ? 1 : 0;
  1461 }
  1462 
  1463 class EmptyMenuHandler : public IMenuHandler
  1464 {
  1465 public:
  1466 } s_EmptyMenuHandler;
  1467 
  1468 static cell_t InternalShowMenu(IPluginContext *pContext, const cell_t *params)
  1469 {
  1470 	int client = params[1];
  1471 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
  1472 
  1473 	if (pPlayer == NULL)
  1474 	{
  1475 		return pContext->ThrowNativeError("Invalid client index %d", client);
  1476 	}
  1477 	else if (!pPlayer->IsInGame())
  1478 	{
  1479 		return pContext->ThrowNativeError("Client %d is not in game", client);
  1480 	}
  1481 
  1482 	if (!g_RadioMenuStyle.IsSupported())
  1483 	{
  1484 		return pContext->ThrowNativeError("Radio menus are not supported on this mod");
  1485 	}
  1486 
  1487 	char *str;
  1488 	pContext->LocalToString(params[2], &str);
  1489 
  1490 	IMenuPanel *pPanel = g_RadioMenuStyle.MakeRadioDisplay(str, params[4]);
  1491 
  1492 	if (pPanel == NULL)
  1493 	{
  1494 		return 0;
  1495 	}
  1496 
  1497 	IMenuHandler *pHandler;
  1498 	CPanelHandler *pActualHandler = NULL;
  1499 	if (params[5] != -1)
  1500 	{
  1501 		IPluginFunction *pFunction = pContext->GetFunctionById(params[5]);
  1502 		if (pFunction == NULL)
  1503 		{
  1504 			return pContext->ThrowNativeError("Invalid function index %x", params[5]);
  1505 		}
  1506 		pActualHandler = g_MenuHelpers.GetPanelHandler(pFunction);
  1507 	}
  1508 
  1509 	if (pActualHandler == NULL)
  1510 	{
  1511 		pHandler = &s_EmptyMenuHandler;
  1512 	}
  1513 	else
  1514 	{
  1515 		pHandler = pActualHandler;
  1516 	}
  1517 
  1518 	bool bSuccess = pPanel->SendDisplay(client, pHandler, params[3]);
  1519 
  1520 	pPanel->DeleteThis();
  1521 
  1522 	if (!bSuccess && pActualHandler != NULL)
  1523 	{
  1524 		g_MenuHelpers.FreePanelHandler(pActualHandler);
  1525 	}
  1526 
  1527 	return bSuccess ? 1 : 0;
  1528 }
  1529 
  1530 REGISTER_NATIVES(menuNatives)
  1531 {
  1532 	{"AddMenuItem",				AddMenuItem},
  1533 	{"CanPanelDrawFlags",		CanPanelDrawFlags},
  1534 	{"CancelClientMenu",		CancelClientMenu},
  1535 	{"CancelMenu",				CancelMenu},
  1536 	{"CancelVote",				CancelVote},
  1537 	{"CheckVoteDelay",			CheckVoteDelay},
  1538 	{"CreateMenu",				CreateMenu},
  1539 	{"CreateMenuEx",			CreateMenuEx},
  1540 	{"CreatePanel",				CreatePanel},
  1541 	{"CreatePanelFromMenu",		CreatePanelFromMenu},
  1542 	{"DisplayMenu",				DisplayMenu},
  1543 	{"DisplayMenuAtItem",		DisplayMenuAtItem},
  1544 	{"DrawPanelItem",			DrawPanelItem},
  1545 	{"DrawPanelText",			DrawPanelText},
  1546 	{"GetClientMenu",			GetClientMenu},
  1547 	{"GetMaxPageItems",			GetMaxPageItems},
  1548 	{"GetMenuExitBackButton",	GetMenuExitBackButton},
  1549 	{"GetMenuExitButton",		GetMenuExitButton},
  1550 	{"GetMenuItem",				GetMenuItem},
  1551 	{"GetMenuItemCount",		GetMenuItemCount},
  1552 	{"GetMenuOptionFlags",		GetMenuOptionFlags},
  1553 	{"GetMenuPagination",		GetMenuPagination},
  1554 	{"GetMenuSelectionPosition",GetMenuSelectionPosition},
  1555 	{"GetMenuStyle",			GetMenuStyle},
  1556 	{"GetMenuStyleHandle",		GetMenuStyleHandle},
  1557 	{"GetMenuTitle",			GetMenuTitle},
  1558 	{"GetPanelTextRemaining",	GetPanelTextRemaining},
  1559 	{"GetPanelCurrentKey",		GetPanelCurrentKey},
  1560 	{"GetPanelStyle",			GetPanelStyle},
  1561 	{"InsertMenuItem",			InsertMenuItem},
  1562 	{"InternalShowMenu",		InternalShowMenu},
  1563 	{"IsClientInVotePool",		IsClientInVotePool},
  1564 	{"IsVoteInProgress",		IsVoteInProgress},
  1565 	{"RedrawClientVoteMenu",	RedrawClientVoteMenu},
  1566 	{"RedrawMenuItem",			RedrawMenuItem},
  1567 	{"RemoveAllMenuItems",		RemoveAllMenuItems},
  1568 	{"RemoveMenuItem",			RemoveMenuItem},
  1569 	{"SendPanelToClient",		SendPanelToClient},
  1570 	{"SetMenuExitBackButton",	SetMenuExitBackButton},
  1571 	{"SetMenuExitButton",		SetMenuExitButton},
  1572 	{"SetMenuOptionFlags",		SetMenuOptionFlags},
  1573 	{"SetMenuPagination",		SetMenuPagination},
  1574 	{"SetMenuTitle",			SetMenuTitle},
  1575 	{"SetPanelCurrentKey",		SetPanelCurrentKey},
  1576 	{"SetPanelTitle",			SetPanelTitle},
  1577 	{"SetPanelKeys",			SetPanelKeys},
  1578 	{"SetVoteResultCallback",	SetVoteResultCallback},
  1579 	{"VoteMenu",				VoteMenu},
  1580 	{NULL,						NULL},
  1581 };
  1582