core/smn_menus.cpp
author David Anderson <dvander@alliedmods.net>
Sat May 17 05:12:52 2008 +0000 (2008-05-17)
changeset 2056 f1fcde46ac7c
parent 2042 4a2faafca477
child 2289 44045ead61cd
permissions -rw-r--r--
added amb1119 - sm_revote command
     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(SOURCEMOD_SERVER_LANGUAGE);
   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 static cell_t IsClientInVotePool(IPluginContext *pContext, const cell_t *params)
  1409 {
  1410 	int client;
  1411 	IGamePlayer *pPlayer;
  1412 
  1413 	client = params[1];
  1414 	if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
  1415 	{
  1416 		return pContext->ThrowNativeError("Invalid client index %d", client);
  1417 	}
  1418 
  1419 	if (!g_Menus.IsVoteInProgress())
  1420 	{
  1421 		return pContext->ThrowNativeError("No vote is in progress");
  1422 	}
  1423 
  1424 	return g_Menus.IsClientInVotePool(client) ? 1 : 0;
  1425 }
  1426 
  1427 static cell_t RedrawClientVoteMenu(IPluginContext *pContext, const cell_t *params)
  1428 {
  1429 	int client;
  1430 	IGamePlayer *pPlayer;
  1431 
  1432 	client = params[1];
  1433 	if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
  1434 	{
  1435 		return pContext->ThrowNativeError("Invalid client index %d", client);
  1436 	}
  1437 
  1438 	if (!g_Menus.IsVoteInProgress())
  1439 	{
  1440 		return pContext->ThrowNativeError("No vote is in progress");
  1441 	}
  1442 
  1443 	if (!g_Menus.IsClientInVotePool(client))
  1444 	{
  1445 		return pContext->ThrowNativeError("Client is not in the voting pool");
  1446 	}
  1447 
  1448 	return g_Menus.RedrawClientVoteMenu(client) ? 1 : 0;
  1449 }
  1450 
  1451 class EmptyMenuHandler : public IMenuHandler
  1452 {
  1453 public:
  1454 } s_EmptyMenuHandler;
  1455 
  1456 static cell_t InternalShowMenu(IPluginContext *pContext, const cell_t *params)
  1457 {
  1458 	int client = params[1];
  1459 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
  1460 
  1461 	if (pPlayer == NULL)
  1462 	{
  1463 		return pContext->ThrowNativeError("Invalid client index %d", client);
  1464 	}
  1465 	else if (!pPlayer->IsInGame())
  1466 	{
  1467 		return pContext->ThrowNativeError("Client %d is not in game", client);
  1468 	}
  1469 
  1470 	if (!g_RadioMenuStyle.IsSupported())
  1471 	{
  1472 		return pContext->ThrowNativeError("Radio menus are not supported on this mod");
  1473 	}
  1474 
  1475 	char *str;
  1476 	pContext->LocalToString(params[2], &str);
  1477 
  1478 	IMenuPanel *pPanel = g_RadioMenuStyle.MakeRadioDisplay(str, params[4]);
  1479 
  1480 	if (pPanel == NULL)
  1481 	{
  1482 		return 0;
  1483 	}
  1484 
  1485 	IMenuHandler *pHandler;
  1486 	CPanelHandler *pActualHandler = NULL;
  1487 	if (params[5] != -1)
  1488 	{
  1489 		IPluginFunction *pFunction = pContext->GetFunctionById(params[5]);
  1490 		if (pFunction == NULL)
  1491 		{
  1492 			return pContext->ThrowNativeError("Invalid function index %x", params[5]);
  1493 		}
  1494 		pActualHandler = g_MenuHelpers.GetPanelHandler(pFunction);
  1495 	}
  1496 
  1497 	if (pActualHandler == NULL)
  1498 	{
  1499 		pHandler = &s_EmptyMenuHandler;
  1500 	}
  1501 	else
  1502 	{
  1503 		pHandler = pActualHandler;
  1504 	}
  1505 
  1506 	bool bSuccess = pPanel->SendDisplay(client, pHandler, params[3]);
  1507 
  1508 	pPanel->DeleteThis();
  1509 
  1510 	if (!bSuccess && pActualHandler != NULL)
  1511 	{
  1512 		g_MenuHelpers.FreePanelHandler(pActualHandler);
  1513 	}
  1514 
  1515 	return bSuccess ? 1 : 0;
  1516 }
  1517 
  1518 REGISTER_NATIVES(menuNatives)
  1519 {
  1520 	{"AddMenuItem",				AddMenuItem},
  1521 	{"CanPanelDrawFlags",		CanPanelDrawFlags},
  1522 	{"CancelClientMenu",		CancelClientMenu},
  1523 	{"CancelMenu",				CancelMenu},
  1524 	{"CancelVote",				CancelVote},
  1525 	{"CheckVoteDelay",			CheckVoteDelay},
  1526 	{"CreateMenu",				CreateMenu},
  1527 	{"CreateMenuEx",			CreateMenuEx},
  1528 	{"CreatePanel",				CreatePanel},
  1529 	{"CreatePanelFromMenu",		CreatePanelFromMenu},
  1530 	{"DisplayMenu",				DisplayMenu},
  1531 	{"DisplayMenuAtItem",		DisplayMenuAtItem},
  1532 	{"DrawPanelItem",			DrawPanelItem},
  1533 	{"DrawPanelText",			DrawPanelText},
  1534 	{"GetClientMenu",			GetClientMenu},
  1535 	{"GetMaxPageItems",			GetMaxPageItems},
  1536 	{"GetMenuExitBackButton",	GetMenuExitBackButton},
  1537 	{"GetMenuExitButton",		GetMenuExitButton},
  1538 	{"GetMenuItem",				GetMenuItem},
  1539 	{"GetMenuItemCount",		GetMenuItemCount},
  1540 	{"GetMenuOptionFlags",		GetMenuOptionFlags},
  1541 	{"GetMenuPagination",		GetMenuPagination},
  1542 	{"GetMenuSelectionPosition",GetMenuSelectionPosition},
  1543 	{"GetMenuStyle",			GetMenuStyle},
  1544 	{"GetMenuStyleHandle",		GetMenuStyleHandle},
  1545 	{"GetMenuTitle",			GetMenuTitle},
  1546 	{"GetPanelTextRemaining",	GetPanelTextRemaining},
  1547 	{"GetPanelCurrentKey",		GetPanelCurrentKey},
  1548 	{"GetPanelStyle",			GetPanelStyle},
  1549 	{"InsertMenuItem",			InsertMenuItem},
  1550 	{"InternalShowMenu",		InternalShowMenu},
  1551 	{"IsClientInVotePool",		IsClientInVotePool},
  1552 	{"IsVoteInProgress",		IsVoteInProgress},
  1553 	{"RedrawClientVoteMenu",	RedrawClientVoteMenu},
  1554 	{"RedrawMenuItem",			RedrawMenuItem},
  1555 	{"RemoveAllMenuItems",		RemoveAllMenuItems},
  1556 	{"RemoveMenuItem",			RemoveMenuItem},
  1557 	{"SendPanelToClient",		SendPanelToClient},
  1558 	{"SetMenuExitBackButton",	SetMenuExitBackButton},
  1559 	{"SetMenuExitButton",		SetMenuExitButton},
  1560 	{"SetMenuOptionFlags",		SetMenuOptionFlags},
  1561 	{"SetMenuPagination",		SetMenuPagination},
  1562 	{"SetMenuTitle",			SetMenuTitle},
  1563 	{"SetPanelCurrentKey",		SetPanelCurrentKey},
  1564 	{"SetPanelTitle",			SetPanelTitle},
  1565 	{"SetPanelKeys",			SetPanelKeys},
  1566 	{"SetVoteResultCallback",	SetVoteResultCallback},
  1567 	{"VoteMenu",				VoteMenu},
  1568 	{NULL,						NULL},
  1569 };