core/smn_menus.cpp
author Scott Ehlert <ds@alliedmods.net>
Sun Mar 30 07:00:22 2008 +0000 (2008-03-30)
changeset 1952 b7aa2b626807
child 2042 4a2faafca477
permissions -rw-r--r--
Spring Cleaning, Part Ichi (1)
Various minor things done to project files
Updated sample extension project file and updated makefile to the new unified version (more changes likely on the way)
Updated regex project file and makefile
     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 	if (!g_Menus.StartVote(menu, params[3], addr, params[4]))
   680 	{
   681 		return 0;
   682 	}
   683 
   684 	return 1;
   685 }
   686 
   687 static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
   688 {
   689 	Handle_t hndl = (Handle_t)params[1];
   690 	HandleError err;
   691 	IBaseMenu *menu;
   692 
   693 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   694 	{
   695 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   696 	}
   697 
   698 	char *info;
   699 	ItemDrawInfo dr;
   700 
   701 	pContext->LocalToString(params[2], &info);
   702 	pContext->LocalToString(params[3], (char **)&dr.display);
   703 	dr.style = params[4];
   704 
   705 	return menu->AppendItem(info, dr) ? 1 : 0;
   706 }
   707 
   708 static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
   709 {
   710 	Handle_t hndl = (Handle_t)params[1];
   711 	HandleError err;
   712 	IBaseMenu *menu;
   713 
   714 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   715 	{
   716 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   717 	}
   718 
   719 	char *info;
   720 	ItemDrawInfo dr;
   721 
   722 	pContext->LocalToString(params[3], &info);
   723 	pContext->LocalToString(params[4], (char **)&dr.display);
   724 	dr.style = params[5];
   725 
   726 	return menu->InsertItem(params[2], info, dr) ? 1 : 0;
   727 }
   728 
   729 static cell_t RemoveMenuItem(IPluginContext *pContext, const cell_t *params)
   730 {
   731 	Handle_t hndl = (Handle_t)params[1];
   732 	HandleError err;
   733 	IBaseMenu *menu;
   734 
   735 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   736 	{
   737 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   738 	}
   739 
   740 	return menu->RemoveItem(params[2]) ? 1 : 0;
   741 }
   742 
   743 static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
   744 {
   745 	Handle_t hndl = (Handle_t)params[1];
   746 	HandleError err;
   747 	IBaseMenu *menu;
   748 
   749 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   750 	{
   751 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   752 	}
   753 
   754 	menu->RemoveAllItems();
   755 
   756 	return 1;
   757 }
   758 
   759 static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
   760 {
   761 	Handle_t hndl = (Handle_t)params[1];
   762 	HandleError err;
   763 	IBaseMenu *menu;
   764 
   765 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   766 	{
   767 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   768 	}
   769 
   770 	ItemDrawInfo dr;
   771 	const char *info;
   772 
   773 	if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
   774 	{
   775 		return 0;
   776 	}
   777 
   778 	pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
   779 	pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
   780 
   781 	cell_t *addr;
   782 	pContext->LocalToPhysAddr(params[5], &addr);
   783 	*addr = dr.style;
   784 
   785 	return 1;
   786 }
   787 
   788 static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
   789 {
   790 	Handle_t hndl = (Handle_t)params[1];
   791 	HandleError err;
   792 	IBaseMenu *menu;
   793 
   794 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   795 	{
   796 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   797 	}
   798 
   799 	return menu->SetPagination(params[2]) ? 1 : 0;
   800 }
   801 
   802 static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
   803 {
   804 	Handle_t hndl = (Handle_t)params[1];
   805 	HandleError err;
   806 	IBaseMenu *menu;
   807 
   808 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   809 	{
   810 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   811 	}
   812 
   813 	return menu->GetPagination();
   814 }
   815 
   816 static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
   817 {
   818 	Handle_t hndl = (Handle_t)params[1];
   819 	HandleError err;
   820 	IBaseMenu *menu;
   821 
   822 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   823 	{
   824 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   825 	}
   826 
   827 	return menu->GetItemCount();
   828 }
   829 
   830 static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
   831 {
   832 	Handle_t hndl = (Handle_t)params[1];
   833 	HandleError err;
   834 	IBaseMenu *menu;
   835 
   836 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   837 	{
   838 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   839 	}
   840 
   841 	g_SourceMod.SetGlobalTarget(LANG_SERVER);
   842 
   843 	char buffer[1024];
   844 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   845 
   846 	menu->SetDefaultTitle(buffer);
   847 
   848 	return 1;
   849 }
   850 
   851 static cell_t GetMenuTitle(IPluginContext *pContext, const cell_t *params)
   852 {
   853 	Handle_t hndl = (Handle_t)params[1];
   854 	HandleError err;
   855 	IBaseMenu *menu;
   856 
   857 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   858 	{
   859 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   860 	}
   861 
   862 	size_t written;
   863 	const char *title = menu->GetDefaultTitle();
   864 	pContext->StringToLocalUTF8(params[2], params[3], title, &written);
   865 
   866 	return (cell_t)written;
   867 }
   868 
   869 
   870 static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
   871 {
   872 	Handle_t hndl = (Handle_t)params[1];
   873 	HandleError err;
   874 	IBaseMenu *menu;
   875 
   876 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   877 	{
   878 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   879 	}
   880 
   881 	IMenuPanel *panel = menu->CreatePanel();
   882 	hndl = MakePanelHandle(panel, pContext);
   883 	if (!hndl)
   884 	{
   885 		panel->DeleteThis();
   886 	}
   887 
   888 	return hndl;
   889 }
   890 
   891 static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   892 {
   893 	Handle_t hndl = (Handle_t)params[1];
   894 	HandleError err;
   895 	IBaseMenu *menu;
   896 
   897 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   898 	{
   899 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   900 	}
   901 
   902 	return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
   903 }
   904 
   905 static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
   906 {
   907 	Handle_t hndl = (Handle_t)params[1];
   908 	HandleError err;
   909 	IBaseMenu *menu;
   910 
   911 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   912 	{
   913 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   914 	}
   915 
   916 	return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
   917 }
   918 
   919 static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   920 {
   921 	Handle_t hndl = (Handle_t)params[1];
   922 	HandleError err;
   923 	IBaseMenu *menu;
   924 
   925 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   926 	{
   927 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   928 	}
   929 
   930 	unsigned int flags = menu->GetMenuOptionFlags();
   931 
   932 	if (params[2])
   933 	{
   934 		flags |= MENUFLAG_BUTTON_EXIT;
   935 	} else {
   936 		flags &= ~MENUFLAG_BUTTON_EXIT;
   937 	}
   938 
   939 	menu->SetMenuOptionFlags(flags);
   940 	unsigned int new_flags = menu->GetMenuOptionFlags();
   941 
   942 	return (flags == new_flags);
   943 }
   944 
   945 static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
   946 {
   947 	Handle_t hndl = (Handle_t)params[1];
   948 	HandleError err;
   949 	IBaseMenu *menu;
   950 
   951 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   952 	{
   953 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   954 	}
   955 
   956 	unsigned int flags = menu->GetMenuOptionFlags();
   957 
   958 	if (params[2])
   959 	{
   960 		flags |= MENUFLAG_BUTTON_EXITBACK;
   961 	} else {
   962 		flags &= ~MENUFLAG_BUTTON_EXITBACK;
   963 	}
   964 
   965 	menu->SetMenuOptionFlags(flags);
   966 
   967 	return 1;
   968 }
   969 
   970 static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
   971 {
   972 	Handle_t hndl = (Handle_t)params[1];
   973 	HandleError err;
   974 	IBaseMenu *menu;
   975 
   976 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   977 	{
   978 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   979 	}
   980 
   981 	g_Menus.CancelMenu(menu);
   982 
   983 	return 1;
   984 }
   985 
   986 static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
   987 {
   988 	return g_Menus.IsVoteInProgress() ? 1 : 0;
   989 }
   990 
   991 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
   992 {
   993 	Handle_t hndl = (Handle_t)params[1];
   994 	HandleError err;
   995 	IBaseMenu *menu;
   996 
   997 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   998 	{
   999 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1000 	}
  1001 
  1002 	return menu->GetDrawStyle()->GetHandle();
  1003 }
  1004 
  1005 static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
  1006 {
  1007 	IMenuStyle *style = GetStyleFromCell(params[1]);
  1008 	if (!style)
  1009 	{
  1010 		return BAD_HANDLE;
  1011 	}
  1012 
  1013 	return style->GetHandle();
  1014 }
  1015 
  1016 static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
  1017 {
  1018 	Handle_t hndl = (Handle_t)params[1];
  1019 	HandleError err;
  1020 	IMenuStyle *style;
  1021 
  1022 	if (hndl != 0)
  1023 	{
  1024 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1025 		{
  1026 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1027 		}
  1028 	} else {
  1029 		style = g_Menus.GetDefaultStyle();
  1030 	}
  1031 
  1032 	IMenuPanel *panel = style->CreatePanel();
  1033 
  1034 	hndl = MakePanelHandle(panel, pContext);
  1035 	if (!hndl)
  1036 	{
  1037 		panel->DeleteThis();
  1038 		return BAD_HANDLE;
  1039 	}
  1040 
  1041 	return hndl;
  1042 }
  1043 
  1044 static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
  1045 {
  1046 	Handle_t hndl = (Handle_t)params[1];
  1047 	HandleError err;
  1048 	IMenuStyle *style;
  1049 
  1050 	if (hndl != 0)
  1051 	{
  1052 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1053 		{
  1054 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1055 		}
  1056 	} else {
  1057 		style = g_Menus.GetDefaultStyle();
  1058 	}
  1059 
  1060 	IPluginFunction *pFunction;
  1061 	if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
  1062 	{
  1063 		return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
  1064 	}
  1065 
  1066 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
  1067 
  1068 	IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
  1069 	hndl = pMenu->GetHandle();
  1070 	if (!hndl)
  1071 	{
  1072 		pMenu->Destroy();
  1073 		return BAD_HANDLE;
  1074 	}
  1075 
  1076 	return hndl;
  1077 }
  1078 
  1079 static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
  1080 {
  1081 	Handle_t hndl = (Handle_t)params[2];
  1082 	HandleError err;
  1083 	IMenuStyle *style;
  1084 
  1085 	if (hndl != 0)
  1086 	{
  1087 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1088 		{
  1089 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1090 		}
  1091 	} else {
  1092 		style = g_Menus.GetDefaultStyle();
  1093 	}
  1094 
  1095 	return style->GetClientMenu(params[1], NULL);
  1096 }
  1097 
  1098 static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
  1099 {
  1100 	Handle_t hndl = (Handle_t)params[3];
  1101 	HandleError err;
  1102 	IMenuStyle *style;
  1103 
  1104 	if (hndl != 0)
  1105 	{
  1106 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1107 		{
  1108 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1109 		}
  1110 	} else {
  1111 		style = g_Menus.GetDefaultStyle();
  1112 	}
  1113 
  1114 	return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
  1115 }
  1116 
  1117 static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
  1118 {
  1119 	Handle_t hndl = (Handle_t)params[1];
  1120 	HandleError err;
  1121 	IMenuStyle *style;
  1122 
  1123 	if (hndl != 0)
  1124 	{
  1125 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
  1126 		{
  1127 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
  1128 		}
  1129 	} else {
  1130 		style = g_Menus.GetDefaultStyle();
  1131 	}
  1132 
  1133 	return style->GetMaxPageItems();
  1134 }
  1135 
  1136 static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
  1137 {
  1138 	Handle_t hndl = (Handle_t)params[1];
  1139 	HandleError err;
  1140 	IMenuPanel *panel;
  1141 
  1142 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1143 	{
  1144 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1145 	}
  1146 
  1147 	return panel->GetParentStyle()->GetHandle();
  1148 }
  1149 
  1150 static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
  1151 {
  1152 	Handle_t hndl = (Handle_t)params[1];
  1153 	HandleError err;
  1154 	IMenuPanel *panel;
  1155 
  1156 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1157 	{
  1158 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1159 	}
  1160 
  1161 	char *text;
  1162 	pContext->LocalToString(params[2], &text);
  1163 
  1164 	panel->DrawTitle(text, params[3] ? true : false);
  1165 
  1166 	return 1;
  1167 }
  1168 
  1169 static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
  1170 {
  1171 	Handle_t hndl = (Handle_t)params[1];
  1172 	HandleError err;
  1173 	IMenuPanel *panel;
  1174 
  1175 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1176 	{
  1177 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1178 	}
  1179 
  1180 	ItemDrawInfo dr;
  1181 	pContext->LocalToString(params[2], (char **)&dr.display);
  1182 	dr.style = params[3];
  1183 
  1184 	return panel->DrawItem(dr);
  1185 }
  1186 
  1187 static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
  1188 {
  1189 	Handle_t hndl = (Handle_t)params[1];
  1190 	HandleError err;
  1191 	IMenuPanel *panel;
  1192 
  1193 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1194 	{
  1195 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1196 	}
  1197 
  1198 	char *text;
  1199 	pContext->LocalToString(params[2], &text);
  1200 
  1201 	return panel->DrawRawLine(text) ? 1 : 0;
  1202 }
  1203 
  1204 static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
  1205 {
  1206 	Handle_t hndl = (Handle_t)params[1];
  1207 	HandleError err;
  1208 	IMenuPanel *panel;
  1209 
  1210 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1211 	{
  1212 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1213 	}
  1214 
  1215 	return panel->CanDrawItem(params[2]);
  1216 }
  1217 
  1218 static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
  1219 {
  1220 	Handle_t hndl = (Handle_t)params[1];
  1221 	HandleError err;
  1222 	IMenuPanel *panel;
  1223 
  1224 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1225 	{
  1226 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1227 	}
  1228 
  1229 	IPluginFunction *pFunction;
  1230 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
  1231 	{
  1232 		return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
  1233 	}
  1234 
  1235 	CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
  1236 	if (!panel->SendDisplay(params[2], handler, params[4]))
  1237 	{
  1238 		g_MenuHelpers.FreePanelHandler(handler);
  1239 	}
  1240 
  1241 	return 1;
  1242 }
  1243 
  1244 static cell_t SetPanelKeys(IPluginContext *pContext, const cell_t *params)
  1245 {
  1246 	Handle_t hndl = (Handle_t)params[1];
  1247 	HandleError err;
  1248 	IMenuPanel *panel;
  1249 
  1250 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1251 	{
  1252 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1253 	}
  1254 
  1255 	return panel->SetSelectableKeys(params[2]);
  1256 }
  1257 
  1258 static cell_t GetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
  1259 {
  1260 	Handle_t hndl = (Handle_t)params[1];
  1261 	HandleError err;
  1262 	IMenuPanel *panel;
  1263 
  1264 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1265 	{
  1266 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1267 	}
  1268 
  1269 	return panel->GetCurrentKey();
  1270 }
  1271 
  1272 static cell_t GetPanelTextRemaining(IPluginContext *pContext, const cell_t *params)
  1273 {
  1274 	Handle_t hndl = (Handle_t)params[1];
  1275 	HandleError err;
  1276 	IMenuPanel *panel;
  1277 
  1278 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1279 	{
  1280 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1281 	}
  1282 
  1283 	return panel->GetAmountRemaining();
  1284 }
  1285 
  1286 static cell_t SetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
  1287 {
  1288 	Handle_t hndl = (Handle_t)params[1];
  1289 	HandleError err;
  1290 	IMenuPanel *panel;
  1291 
  1292 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
  1293 	{
  1294 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1295 	}
  1296 
  1297 	return panel->SetCurrentKey(params[2]) ? 1 : 0;
  1298 }
  1299 
  1300 static cell_t RedrawMenuItem(IPluginContext *pContext, const cell_t *params)
  1301 {
  1302 	if (!s_pCurPanel)
  1303 	{
  1304 		return pContext->ThrowNativeError("You can only call this once from a MenuAction_DisplayItem callback");
  1305 	}
  1306 
  1307 	char *str;
  1308 	pContext->LocalToString(params[1], &str);
  1309 
  1310 	ItemDrawInfo dr = *s_CurDrawInfo;
  1311 	dr.display = str;
  1312 
  1313 	if ((s_CurPanelReturn = s_pCurPanel->DrawItem(dr)) != 0)
  1314 	{
  1315 		s_pCurPanel = NULL;
  1316 	}
  1317 
  1318 	return s_CurPanelReturn;
  1319 }
  1320 
  1321 static cell_t GetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
  1322 {
  1323 	Handle_t hndl = (Handle_t)params[1];
  1324 	HandleError err;
  1325 	IBaseMenu *menu;
  1326 
  1327 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1328 	{
  1329 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1330 	}
  1331 
  1332 	return menu->GetMenuOptionFlags();
  1333 }
  1334 
  1335 static cell_t SetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
  1336 {
  1337 	Handle_t hndl = (Handle_t)params[1];
  1338 	HandleError err;
  1339 	IBaseMenu *menu;
  1340 
  1341 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1342 	{
  1343 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1344 	}
  1345 
  1346 	menu->SetMenuOptionFlags(params[2]);
  1347 
  1348 	return 1;
  1349 }
  1350 
  1351 static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
  1352 {
  1353 	if (!g_Menus.IsVoteInProgress())
  1354 	{
  1355 		return pContxt->ThrowNativeError("No vote is in progress");
  1356 	}
  1357 
  1358 	g_Menus.CancelVoting();
  1359 
  1360 	return 1;
  1361 }
  1362 
  1363 static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
  1364 {
  1365 	Handle_t hndl = (Handle_t)params[1];
  1366 	HandleError err;
  1367 	IBaseMenu *menu;
  1368 
  1369 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
  1370 	{
  1371 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1372 	}
  1373 
  1374 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
  1375 	if (!pFunction)
  1376 	{
  1377 		return pContext->ThrowNativeError("Invalid function %x", params[2]);
  1378 	}
  1379 
  1380 	void *array[2];
  1381 	array[0] = pFunction;
  1382 	array[1] = (void *)&params[2];
  1383 
  1384 	IMenuHandler *pHandler = menu->GetHandler();
  1385 	if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
  1386 	{
  1387 		return pContext->ThrowNativeError("The given menu does not support this option");
  1388 	}
  1389 
  1390 	return 1;
  1391 }
  1392 
  1393 static cell_t CheckVoteDelay(IPluginContext *pContext, const cell_t *params)
  1394 {
  1395 	return g_Menus.GetRemainingVoteDelay();
  1396 }
  1397 
  1398 static cell_t GetMenuSelectionPosition(IPluginContext *pContext, const cell_t *params)
  1399 {
  1400 	if (!s_CurSelectPosition)
  1401 	{
  1402 		return pContext->ThrowNativeError("Can only be called from inside a MenuAction_Select callback");
  1403 	}
  1404 
  1405 	return *s_CurSelectPosition;
  1406 }
  1407 
  1408 class EmptyMenuHandler : public IMenuHandler
  1409 {
  1410 public:
  1411 } s_EmptyMenuHandler;
  1412 
  1413 static cell_t InternalShowMenu(IPluginContext *pContext, const cell_t *params)
  1414 {
  1415 	int client = params[1];
  1416 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
  1417 
  1418 	if (pPlayer == NULL)
  1419 	{
  1420 		return pContext->ThrowNativeError("Invalid client index %d", client);
  1421 	}
  1422 	else if (!pPlayer->IsInGame())
  1423 	{
  1424 		return pContext->ThrowNativeError("Client %d is not in game", client);
  1425 	}
  1426 
  1427 	if (!g_RadioMenuStyle.IsSupported())
  1428 	{
  1429 		return pContext->ThrowNativeError("Radio menus are not supported on this mod");
  1430 	}
  1431 
  1432 	char *str;
  1433 	pContext->LocalToString(params[2], &str);
  1434 
  1435 	IMenuPanel *pPanel = g_RadioMenuStyle.MakeRadioDisplay(str, params[4]);
  1436 
  1437 	if (pPanel == NULL)
  1438 	{
  1439 		return 0;
  1440 	}
  1441 
  1442 	IMenuHandler *pHandler;
  1443 	CPanelHandler *pActualHandler = NULL;
  1444 	if (params[5] != -1)
  1445 	{
  1446 		IPluginFunction *pFunction = pContext->GetFunctionById(params[5]);
  1447 		if (pFunction == NULL)
  1448 		{
  1449 			return pContext->ThrowNativeError("Invalid function index %x", params[5]);
  1450 		}
  1451 		pActualHandler = g_MenuHelpers.GetPanelHandler(pFunction);
  1452 	}
  1453 
  1454 	if (pActualHandler == NULL)
  1455 	{
  1456 		pHandler = &s_EmptyMenuHandler;
  1457 	}
  1458 	else
  1459 	{
  1460 		pHandler = pActualHandler;
  1461 	}
  1462 
  1463 	bool bSuccess = pPanel->SendDisplay(client, pHandler, params[3]);
  1464 
  1465 	pPanel->DeleteThis();
  1466 
  1467 	if (!bSuccess && pActualHandler != NULL)
  1468 	{
  1469 		g_MenuHelpers.FreePanelHandler(pActualHandler);
  1470 	}
  1471 
  1472 	return bSuccess ? 1 : 0;
  1473 }
  1474 
  1475 REGISTER_NATIVES(menuNatives)
  1476 {
  1477 	{"AddMenuItem",				AddMenuItem},
  1478 	{"CanPanelDrawFlags",		CanPanelDrawFlags},
  1479 	{"CancelClientMenu",		CancelClientMenu},
  1480 	{"CancelMenu",				CancelMenu},
  1481 	{"CancelVote",				CancelVote},
  1482 	{"CheckVoteDelay",			CheckVoteDelay},
  1483 	{"CreateMenu",				CreateMenu},
  1484 	{"CreateMenuEx",			CreateMenuEx},
  1485 	{"CreatePanel",				CreatePanel},
  1486 	{"CreatePanelFromMenu",		CreatePanelFromMenu},
  1487 	{"DisplayMenu",				DisplayMenu},
  1488 	{"DisplayMenuAtItem",		DisplayMenuAtItem},
  1489 	{"DrawPanelItem",			DrawPanelItem},
  1490 	{"DrawPanelText",			DrawPanelText},
  1491 	{"GetClientMenu",			GetClientMenu},
  1492 	{"GetMaxPageItems",			GetMaxPageItems},
  1493 	{"GetMenuExitBackButton",	GetMenuExitBackButton},
  1494 	{"GetMenuExitButton",		GetMenuExitButton},
  1495 	{"GetMenuItem",				GetMenuItem},
  1496 	{"GetMenuItemCount",		GetMenuItemCount},
  1497 	{"GetMenuOptionFlags",		GetMenuOptionFlags},
  1498 	{"GetMenuPagination",		GetMenuPagination},
  1499 	{"GetMenuSelectionPosition",GetMenuSelectionPosition},
  1500 	{"GetMenuStyle",			GetMenuStyle},
  1501 	{"GetMenuStyleHandle",		GetMenuStyleHandle},
  1502 	{"GetMenuTitle",			GetMenuTitle},
  1503 	{"GetPanelTextRemaining",	GetPanelTextRemaining},
  1504 	{"GetPanelCurrentKey",		GetPanelCurrentKey},
  1505 	{"GetPanelStyle",			GetPanelStyle},
  1506 	{"InsertMenuItem",			InsertMenuItem},
  1507 	{"InternalShowMenu",		InternalShowMenu},
  1508 	{"IsVoteInProgress",		IsVoteInProgress},
  1509 	{"RedrawMenuItem",			RedrawMenuItem},
  1510 	{"RemoveAllMenuItems",		RemoveAllMenuItems},
  1511 	{"RemoveMenuItem",			RemoveMenuItem},
  1512 	{"SendPanelToClient",		SendPanelToClient},
  1513 	{"SetMenuExitBackButton",	SetMenuExitBackButton},
  1514 	{"SetMenuExitButton",		SetMenuExitButton},
  1515 	{"SetMenuOptionFlags",		SetMenuOptionFlags},
  1516 	{"SetMenuPagination",		SetMenuPagination},
  1517 	{"SetMenuTitle",			SetMenuTitle},
  1518 	{"SetPanelCurrentKey",		SetPanelCurrentKey},
  1519 	{"SetPanelTitle",			SetPanelTitle},
  1520 	{"SetPanelKeys",			SetPanelKeys},
  1521 	{"SetVoteResultCallback",	SetVoteResultCallback},
  1522 	{"VoteMenu",				VoteMenu},
  1523 	{NULL,						NULL},
  1524 };