core/smn_menus.cpp
author Borja Ferrer <faluco@alliedmods.net>
Thu Jul 05 14:47:36 2007 +0000 (2007-07-05)
changeset 1041 f7b2073013df
parent 1011 b413072540fd
child 1163 0bb03cb6277b
permissions -rw-r--r--
added a time stamp argument to FormatTime
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include "sm_globals.h"
    16 #include <sh_stack.h>
    17 #include "MenuManager.h"
    18 #include "MenuStyle_Valve.h"
    19 #include "MenuStyle_Radio.h"
    20 #include "HandleSys.h"
    21 #include "PluginSys.h"
    22 #include "sm_stringutil.h"
    23 #if defined MENU_DEBUG
    24 #include "Logger.h"
    25 #endif
    26 
    27 #if defined CreateMenu
    28 #undef CreateMenu
    29 #endif
    30 
    31 /**
    32  * And God said, "let there be menus," and behold, there were menus.  
    33  * God saw the menus and they were good.  And the evening and the morning
    34  * were the third day.
    35  */
    36 
    37 enum MenuAction
    38 {
    39 	MenuAction_Start = (1<<0),		/**< A menu has been started (nothing passed) */
    40 	MenuAction_Display = (1<<1),	/**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */
    41 	MenuAction_Select = (1<<2),		/**< An item was selected (param1=client, param2=item) */
    42 	MenuAction_Cancel = (1<<3),		/**< The menu was cancelled (param1=client, param2=item) */
    43 	MenuAction_End = (1<<4),		/**< A menu's display/selection cycle is complete (nothing passed). */
    44 	MenuAction_VoteEnd = (1<<5),	/**< (VOTE ONLY): A vote sequence has ended (param1=chosen item) */
    45 	MenuAction_VoteStart = (1<<6), 	/**< (VOTE ONLY): A vote sequence has started */
    46 	MenuAction_VoteCancel = (1<<7),	/**< (VOTE ONLY): A vote sequence has been cancelled (nothing passed) */
    47 	MenuAction_DrawItem = (1<<8),	/**< A style is being drawn; return the new style (param1=client, param2=item) */
    48 };
    49 
    50 class CPanelHandler : public IMenuHandler
    51 {
    52 	friend class MenuNativeHelpers;
    53 public:
    54 	CPanelHandler()
    55 	{
    56 	}
    57 	void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
    58 	void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
    59 private:
    60 	IPluginFunction *m_pFunc;
    61 	IPlugin *m_pPlugin;
    62 };
    63 
    64 class CMenuHandler : public IMenuHandler
    65 {
    66 	friend class MenuNativeHelpers;
    67 public:
    68 	CMenuHandler(IPluginFunction *pBasic, int flags);
    69 public:
    70 	void OnMenuStart(IBaseMenu *menu);
    71 	void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
    72 	void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
    73 	void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
    74 	void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
    75 	void OnMenuDestroy(IBaseMenu *menu);
    76 	void OnMenuVoteStart(IBaseMenu *menu);
    77 	void OnMenuVoteEnd(IBaseMenu *menu,
    78 		unsigned int item, 
    79 		unsigned int winningVotes,
    80 		unsigned int totalVotes);
    81 	void OnMenuVoteCancel(IBaseMenu *menu);
    82 	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
    83 #if 0
    84 	void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
    85 	void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
    86 #endif
    87 private:
    88 	cell_t DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res=0);
    89 private:
    90 	IPluginFunction *m_pBasic;
    91 	int m_Flags;
    92 };
    93 
    94 /**
    95  * GLOBAL CLASS FOR HELPERS
    96  */
    97 
    98 class MenuNativeHelpers : 
    99 	public SMGlobalClass,
   100 	public IHandleTypeDispatch,
   101 	public IPluginsListener
   102 {
   103 public:
   104 	virtual void OnSourceModAllInitialized()
   105 	{
   106 		m_PanelType = g_HandleSys.CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
   107 		g_PluginSys.AddPluginsListener(this);
   108 	}
   109 
   110 	virtual void OnSourceModShutdown()
   111 	{
   112 		g_HandleSys.RemoveType(m_PanelType, g_pCoreIdent);
   113 
   114 		while (!m_FreePanelHandlers.empty())
   115 		{
   116 			delete m_FreePanelHandlers.front();
   117 			m_FreePanelHandlers.pop();
   118 		}
   119 
   120 		while (!m_FreeMenuHandlers.empty())
   121 		{
   122 			delete m_FreeMenuHandlers.front();
   123 			m_FreeMenuHandlers.pop();
   124 		}
   125 	}
   126 
   127 	virtual void OnHandleDestroy(HandleType_t type, void *object)
   128 	{
   129 		IMenuPanel *panel = (IMenuPanel *)object;
   130 		panel->DeleteThis();
   131 	}
   132 
   133 	/**
   134 	 *   It is extremely important that unloaded plugins don't crash.
   135 	 * Thus, if a plugin unloads, we run through every handler we have.
   136 	 * This means we do almost no runtime work for keeping track of
   137 	 * our panel handlers (we don't have to store a list of the running 
   138 	 * ones), but when push comes to shove, we have to scan them all
   139 	 * in case any of them are active.
   140 	 */
   141 	virtual void OnPluginUnloaded(IPlugin *plugin)
   142 	{
   143 		for (size_t i = 0; i < m_PanelHandlers.size(); i++)
   144 		{
   145 			if (m_PanelHandlers[i]->m_pPlugin == plugin)
   146 			{
   147 				m_PanelHandlers[i]->m_pPlugin = NULL;
   148 				m_PanelHandlers[i]->m_pFunc = NULL;
   149 			}
   150 		}
   151 	}
   152 
   153 	inline HandleType_t GetPanelType()
   154 	{
   155 		return m_PanelType;
   156 	}
   157 
   158 	CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
   159 	{
   160 		CPanelHandler *handler;
   161 		if (m_FreePanelHandlers.empty())
   162 		{
   163 			handler = new CPanelHandler;
   164 			m_PanelHandlers.push_back(handler);
   165 		} else {
   166 			handler = m_FreePanelHandlers.front();
   167 			m_FreePanelHandlers.pop();
   168 		}
   169 		handler->m_pFunc = pFunction;
   170 		handler->m_pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
   171 		return handler;
   172 	}
   173 
   174 	void FreePanelHandler(CPanelHandler *handler)
   175 	{
   176 		handler->m_pFunc = NULL;
   177 		handler->m_pPlugin = NULL;
   178 		m_FreePanelHandlers.push(handler);
   179 	}
   180 
   181 	CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
   182 	{
   183 		CMenuHandler *handler;
   184 		if (m_FreeMenuHandlers.empty())
   185 		{
   186 			handler = new CMenuHandler(pFunction, flags);
   187 		} else {
   188 			handler = m_FreeMenuHandlers.front();
   189 			m_FreeMenuHandlers.pop();
   190 			handler->m_pBasic = pFunction;
   191 			handler->m_Flags = flags;
   192 		}
   193 		return handler;
   194 	}
   195 
   196 	void FreeMenuHandler(CMenuHandler *handler)
   197 	{
   198 		m_FreeMenuHandlers.push(handler);
   199 	}
   200 
   201 private:
   202 	HandleType_t m_PanelType;
   203 	CStack<CPanelHandler *> m_FreePanelHandlers;
   204 	CStack<CMenuHandler *> m_FreeMenuHandlers;
   205 	CVector<CPanelHandler *> m_PanelHandlers;
   206 } g_MenuHelpers;
   207 
   208 /**
   209  * BASIC PANEL HANDLER WRAPPER
   210  */
   211 
   212 void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   213 {
   214 	if (m_pFunc)
   215 	{
   216 		m_pFunc->PushCell(BAD_HANDLE);
   217 		m_pFunc->PushCell(MenuAction_Cancel);
   218 		m_pFunc->PushCell(client);
   219 		m_pFunc->PushCell(reason);
   220 		m_pFunc->Execute(NULL);
   221 	}
   222 	g_MenuHelpers.FreePanelHandler(this);
   223 }
   224 
   225 void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   226 {
   227 	if (m_pFunc)
   228 	{
   229 		m_pFunc->PushCell(BAD_HANDLE);
   230 		m_pFunc->PushCell(MenuAction_Select);
   231 		m_pFunc->PushCell(client);
   232 		m_pFunc->PushCell(item);
   233 		m_pFunc->Execute(NULL);
   234 	}
   235 	g_MenuHelpers.FreePanelHandler(this);
   236 }
   237 
   238 /**
   239  * MENU HANDLER WRAPPER
   240  */
   241 CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) : 
   242 	m_pBasic(pBasic), m_Flags(flags)
   243 {
   244 	/* :TODO: We can probably cache the handle ahead of time */
   245 }
   246 
   247 void CMenuHandler::OnMenuStart(IBaseMenu *menu)
   248 {
   249 	if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
   250 	{
   251 		DoAction(menu, MenuAction_Start, 0, 0);
   252 	}
   253 }
   254 
   255 void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
   256 {
   257 	if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
   258 	{
   259 		HandleSecurity sec;
   260 		sec.pIdentity = m_pBasic->GetParentContext()->GetIdentity();
   261 		sec.pOwner = g_pCoreIdent;
   262 	
   263 		HandleAccess access;
   264 		g_HandleSys.InitAccessDefaults(NULL, &access);
   265 		access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
   266 
   267 		Handle_t hndl =  g_HandleSys.CreateHandleEx(g_MenuHelpers.GetPanelType(), panel, &sec, &access, NULL);
   268 
   269 		DoAction(menu, MenuAction_Display, client, hndl);
   270 
   271 		g_HandleSys.FreeHandle(hndl, &sec);
   272 	}
   273 }
   274 
   275 void CMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
   276 {
   277 	DoAction(menu, MenuAction_Select, client, item);
   278 }
   279 
   280 void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
   281 {
   282 	DoAction(menu, MenuAction_Cancel, client, (cell_t)reason);
   283 }
   284 
   285 void CMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
   286 {
   287 	DoAction(menu, MenuAction_End, reason, 0);
   288 }
   289 
   290 void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
   291 {
   292 	g_MenuHelpers.FreeMenuHandler(this);
   293 }
   294 
   295 void CMenuHandler::OnMenuVoteStart(IBaseMenu *menu)
   296 {
   297 	DoAction(menu, MenuAction_VoteStart, 0, 0);
   298 }
   299 
   300 void CMenuHandler::OnMenuVoteEnd(IBaseMenu *menu,
   301 								 unsigned int item,
   302 								 unsigned int winningVotes,
   303 								 unsigned int totalVotes)
   304 {
   305 	DoAction(menu, MenuAction_VoteEnd, item, (totalVotes << 16) | (winningVotes & 0xFFFF));
   306 }
   307 
   308 void CMenuHandler::OnMenuVoteCancel(IBaseMenu *menu)
   309 {
   310 	DoAction(menu, MenuAction_VoteCancel, 0, 0);
   311 }
   312 
   313 void CMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
   314 {
   315 	if ((m_Flags & (int)MenuAction_DrawItem) == (int)MenuAction_DrawItem)
   316 	{
   317 		cell_t result = DoAction(menu, MenuAction_DrawItem, client, item, style);
   318 		style = (unsigned int)result;
   319 	}
   320 }
   321 
   322 cell_t CMenuHandler::DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res)
   323 {
   324 #if defined MENU_DEBUG
   325 	g_Logger.LogMessage("[SM_MENU] CMenuHandler::DoAction() (menu %p/%08x) (action %d) (param1 %d) (param2 %d)",
   326 		menu,
   327 		menu->GetHandle(),
   328 		action,
   329 		param1,
   330 		param2);
   331 #endif
   332 	cell_t res = def_res;
   333 	m_pBasic->PushCell(menu->GetHandle());
   334 	m_pBasic->PushCell((cell_t)action);
   335 	m_pBasic->PushCell(param1);
   336 	m_pBasic->PushCell(param2);
   337 	m_pBasic->Execute(&res);
   338 	return res;
   339 }
   340 
   341 /**
   342  * INLINE FUNCTIONS FOR NATIVES
   343  */
   344 
   345 inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
   346 {
   347 	return g_HandleSys.CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
   348 }
   349 
   350 inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
   351 {
   352 	HandleSecurity sec;
   353 	sec.pIdentity = g_pCoreIdent;
   354 	sec.pOwner = NULL;
   355 	return g_HandleSys.ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
   356 }
   357 
   358 inline IMenuStyle *GetStyleFromCell(cell_t cell)
   359 {
   360 	enum MenuStyle
   361 	{
   362 		MenuStyle_Default = 0,		/**< The "default" menu style for the mod */
   363 		MenuStyle_Valve = 1,		/**< The Valve provided menu style (Used on HL2DM) */
   364 		MenuStyle_Radio = 2,		/**< The simpler menu style commonly used on CS:S */
   365 	};
   366 
   367 	if (cell == MenuStyle_Valve)
   368 	{
   369 		return &g_ValveMenuStyle;
   370 	} else if (cell == MenuStyle_Radio
   371 			   && g_RadioMenuStyle.IsSupported())
   372 	{
   373 		return &g_RadioMenuStyle;
   374 	}
   375 
   376 	return g_Menus.GetDefaultStyle();
   377 }
   378 
   379 /***********************************
   380  **** NATIVE DEFINITIONS ***********
   381  ***********************************/
   382 
   383 static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
   384 {
   385 	IMenuStyle *style = g_Menus.GetDefaultStyle();
   386 	IPluginFunction *pFunction;
   387 
   388 	if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
   389 	{
   390 		return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
   391 	}
   392 
   393 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
   394 	IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
   395 
   396 	Handle_t hndl = menu->GetHandle();
   397 	if (!hndl)
   398 	{
   399 		menu->Destroy();
   400 		return BAD_HANDLE;
   401 	}
   402 
   403 	return hndl;
   404 }
   405 
   406 static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
   407 {
   408 	Handle_t hndl = (Handle_t)params[1];
   409 	HandleError err;
   410 	IBaseMenu *menu;
   411 
   412 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   413 	{
   414 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   415 	}
   416 
   417 	return menu->Display(params[2], params[3]) ? 1 : 0;
   418 }
   419 
   420 static cell_t VoteMenu(IPluginContext *pContext, const cell_t *params)
   421 {
   422 	Handle_t hndl = (Handle_t)params[1];
   423 	HandleError err;
   424 	IBaseMenu *menu;
   425 
   426 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   427 	{
   428 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   429 	}
   430 
   431 	cell_t *addr;
   432 	pContext->LocalToPhysAddr(params[2], &addr);
   433 
   434 	return menu->BroadcastVote(addr, params[3], params[4]) ? 1 : 0;
   435 }
   436 
   437 static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
   438 {
   439 	Handle_t hndl = (Handle_t)params[1];
   440 	HandleError err;
   441 	IBaseMenu *menu;
   442 
   443 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   444 	{
   445 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   446 	}
   447 
   448 	char *info;
   449 	ItemDrawInfo dr;
   450 
   451 	pContext->LocalToString(params[2], &info);
   452 	pContext->LocalToString(params[3], (char **)&dr.display);
   453 	dr.style = params[4];
   454 
   455 	return menu->AppendItem(info, dr) ? 1 : 0;
   456 }
   457 
   458 static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
   459 {
   460 	Handle_t hndl = (Handle_t)params[1];
   461 	HandleError err;
   462 	IBaseMenu *menu;
   463 
   464 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   465 	{
   466 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   467 	}
   468 
   469 	char *info;
   470 	ItemDrawInfo dr;
   471 
   472 	pContext->LocalToString(params[3], &info);
   473 	pContext->LocalToString(params[4], (char **)&dr.display);
   474 	dr.style = params[5];
   475 
   476 	return menu->InsertItem(params[2], info, dr) ? 1 : 0;
   477 }
   478 
   479 static cell_t RemoveMenuItem(IPluginContext *pContext, const cell_t *params)
   480 {
   481 	Handle_t hndl = (Handle_t)params[1];
   482 	HandleError err;
   483 	IBaseMenu *menu;
   484 
   485 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   486 	{
   487 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   488 	}
   489 
   490 	return menu->RemoveItem(params[2]) ? 1 : 0;
   491 }
   492 
   493 static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
   494 {
   495 	Handle_t hndl = (Handle_t)params[1];
   496 	HandleError err;
   497 	IBaseMenu *menu;
   498 
   499 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   500 	{
   501 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   502 	}
   503 
   504 	menu->RemoveAllItems();
   505 
   506 	return 1;
   507 }
   508 
   509 static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
   510 {
   511 	Handle_t hndl = (Handle_t)params[1];
   512 	HandleError err;
   513 	IBaseMenu *menu;
   514 
   515 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   516 	{
   517 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   518 	}
   519 
   520 	ItemDrawInfo dr;
   521 	const char *info;
   522 
   523 	if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
   524 	{
   525 		return 0;
   526 	}
   527 
   528 	pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
   529 	pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
   530 
   531 	cell_t *addr;
   532 	pContext->LocalToPhysAddr(params[5], &addr);
   533 	*addr = dr.style;
   534 
   535 	return 1;
   536 }
   537 
   538 static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
   539 {
   540 	Handle_t hndl = (Handle_t)params[1];
   541 	HandleError err;
   542 	IBaseMenu *menu;
   543 
   544 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   545 	{
   546 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   547 	}
   548 
   549 	return menu->SetPagination(params[2]) ? 1 : 0;
   550 }
   551 
   552 static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
   553 {
   554 	Handle_t hndl = (Handle_t)params[1];
   555 	HandleError err;
   556 	IBaseMenu *menu;
   557 
   558 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   559 	{
   560 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   561 	}
   562 
   563 	return menu->GetPagination();
   564 }
   565 
   566 static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
   567 {
   568 	Handle_t hndl = (Handle_t)params[1];
   569 	HandleError err;
   570 	IBaseMenu *menu;
   571 
   572 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   573 	{
   574 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   575 	}
   576 
   577 	return menu->GetItemCount();
   578 }
   579 
   580 static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
   581 {
   582 	Handle_t hndl = (Handle_t)params[1];
   583 	HandleError err;
   584 	IBaseMenu *menu;
   585 
   586 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   587 	{
   588 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   589 	}
   590 
   591 	g_SourceMod.SetGlobalTarget(LANG_SERVER);
   592 
   593 	char buffer[1024];
   594 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   595 
   596 	menu->SetDefaultTitle(buffer);
   597 
   598 	return 1;
   599 }
   600 
   601 static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
   602 {
   603 	Handle_t hndl = (Handle_t)params[1];
   604 	HandleError err;
   605 	IBaseMenu *menu;
   606 
   607 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   608 	{
   609 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   610 	}
   611 
   612 	IMenuPanel *panel = menu->CreatePanel();
   613 	hndl = MakePanelHandle(panel, pContext);
   614 	if (!hndl)
   615 	{
   616 		panel->DeleteThis();
   617 	}
   618 
   619 	return hndl;
   620 }
   621 
   622 static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   623 {
   624 	Handle_t hndl = (Handle_t)params[1];
   625 	HandleError err;
   626 	IBaseMenu *menu;
   627 
   628 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   629 	{
   630 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   631 	}
   632 
   633 	return menu->GetExitButton() ? 1 : 0;
   634 }
   635 
   636 static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
   637 {
   638 	Handle_t hndl = (Handle_t)params[1];
   639 	HandleError err;
   640 	IBaseMenu *menu;
   641 
   642 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   643 	{
   644 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   645 	}
   646 
   647 	return menu->GetExitBackButton() ? 1 : 0;
   648 }
   649 
   650 static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
   651 {
   652 	Handle_t hndl = (Handle_t)params[1];
   653 	HandleError err;
   654 	IBaseMenu *menu;
   655 
   656 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   657 	{
   658 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   659 	}
   660 
   661 	return menu->SetExitButton(params[2] ? true : false) ? 1 : 0;
   662 }
   663 
   664 static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
   665 {
   666 	Handle_t hndl = (Handle_t)params[1];
   667 	HandleError err;
   668 	IBaseMenu *menu;
   669 
   670 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   671 	{
   672 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   673 	}
   674 
   675 	menu->SetExitBackButton(params[2] ? true : false);
   676 
   677 	return 1;
   678 }
   679 
   680 static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
   681 {
   682 	Handle_t hndl = (Handle_t)params[1];
   683 	HandleError err;
   684 	IBaseMenu *menu;
   685 
   686 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   687 	{
   688 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   689 	}
   690 
   691 	menu->Cancel();
   692 
   693 	return 1;
   694 }
   695 
   696 static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
   697 {
   698 	Handle_t hndl = (Handle_t)params[1];
   699 	HandleError err;
   700 	IBaseMenu *menu;
   701 
   702 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   703 	{
   704 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   705 	}
   706 
   707 	return menu->IsVoteInProgress() ? 1 : 0;
   708 }
   709 
   710 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
   711 {
   712 	Handle_t hndl = (Handle_t)params[1];
   713 	HandleError err;
   714 	IBaseMenu *menu;
   715 
   716 	if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
   717 	{
   718 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   719 	}
   720 
   721 	return menu->GetDrawStyle()->GetHandle();
   722 }
   723 
   724 static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
   725 {
   726 	IMenuStyle *style = GetStyleFromCell(params[1]);
   727 	if (!style)
   728 	{
   729 		return BAD_HANDLE;
   730 	}
   731 
   732 	return style->GetHandle();
   733 }
   734 
   735 static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
   736 {
   737 	Handle_t hndl = (Handle_t)params[1];
   738 	HandleError err;
   739 	IMenuStyle *style;
   740 
   741 	if (hndl != 0)
   742 	{
   743 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   744 		{
   745 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   746 		}
   747 	} else {
   748 		style = g_Menus.GetDefaultStyle();
   749 	}
   750 
   751 	IMenuPanel *panel = style->CreatePanel();
   752 
   753 	hndl = MakePanelHandle(panel, pContext);
   754 	if (!hndl)
   755 	{
   756 		panel->DeleteThis();
   757 		return BAD_HANDLE;
   758 	}
   759 
   760 	return hndl;
   761 }
   762 
   763 static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
   764 {
   765 	Handle_t hndl = (Handle_t)params[1];
   766 	HandleError err;
   767 	IMenuStyle *style;
   768 
   769 	if (hndl != 0)
   770 	{
   771 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   772 		{
   773 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   774 		}
   775 	} else {
   776 		style = g_Menus.GetDefaultStyle();
   777 	}
   778 
   779 	IPluginFunction *pFunction;
   780 	if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
   781 	{
   782 		return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
   783 	}
   784 
   785 	CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
   786 
   787 	IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
   788 	hndl = pMenu->GetHandle();
   789 	if (!hndl)
   790 	{
   791 		pMenu->Destroy();
   792 		return BAD_HANDLE;
   793 	}
   794 
   795 	return hndl;
   796 }
   797 
   798 static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
   799 {
   800 	Handle_t hndl = (Handle_t)params[2];
   801 	HandleError err;
   802 	IMenuStyle *style;
   803 
   804 	if (hndl != 0)
   805 	{
   806 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   807 		{
   808 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   809 		}
   810 	} else {
   811 		style = g_Menus.GetDefaultStyle();
   812 	}
   813 
   814 	return style->GetClientMenu(params[1], NULL);
   815 }
   816 
   817 static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
   818 {
   819 	Handle_t hndl = (Handle_t)params[3];
   820 	HandleError err;
   821 	IMenuStyle *style;
   822 
   823 	if (hndl != 0)
   824 	{
   825 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   826 		{
   827 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   828 		}
   829 	} else {
   830 		style = g_Menus.GetDefaultStyle();
   831 	}
   832 
   833 	return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
   834 }
   835 
   836 static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
   837 {
   838 	Handle_t hndl = (Handle_t)params[1];
   839 	HandleError err;
   840 	IMenuStyle *style;
   841 
   842 	if (hndl != 0)
   843 	{
   844 		if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
   845 		{
   846 			return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
   847 		}
   848 	} else {
   849 		style = g_Menus.GetDefaultStyle();
   850 	}
   851 
   852 	return style->GetMaxPageItems();
   853 }
   854 
   855 static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
   856 {
   857 	Handle_t hndl = (Handle_t)params[1];
   858 	HandleError err;
   859 	IMenuPanel *panel;
   860 
   861 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   862 	{
   863 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   864 	}
   865 
   866 	return panel->GetParentStyle()->GetHandle();
   867 }
   868 
   869 static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
   870 {
   871 	Handle_t hndl = (Handle_t)params[1];
   872 	HandleError err;
   873 	IMenuPanel *panel;
   874 
   875 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   876 	{
   877 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   878 	}
   879 
   880 	char *text;
   881 	pContext->LocalToString(params[2], &text);
   882 
   883 	panel->DrawTitle(text, params[3] ? true : false);
   884 
   885 	return 1;
   886 }
   887 
   888 static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
   889 {
   890 	Handle_t hndl = (Handle_t)params[1];
   891 	HandleError err;
   892 	IMenuPanel *panel;
   893 
   894 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   895 	{
   896 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   897 	}
   898 
   899 	ItemDrawInfo dr;
   900 	pContext->LocalToString(params[2], (char **)&dr.display);
   901 	dr.style = params[3];
   902 
   903 	return panel->DrawItem(dr);
   904 }
   905 
   906 static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
   907 {
   908 	Handle_t hndl = (Handle_t)params[1];
   909 	HandleError err;
   910 	IMenuPanel *panel;
   911 
   912 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   913 	{
   914 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   915 	}
   916 
   917 	char *text;
   918 	pContext->LocalToString(params[2], &text);
   919 
   920 	return panel->DrawRawLine(text) ? 1 : 0;
   921 }
   922 
   923 static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
   924 {
   925 	Handle_t hndl = (Handle_t)params[1];
   926 	HandleError err;
   927 	IMenuPanel *panel;
   928 
   929 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   930 	{
   931 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   932 	}
   933 
   934 	return panel->CanDrawItem(params[2]);
   935 }
   936 
   937 static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
   938 {
   939 	Handle_t hndl = (Handle_t)params[1];
   940 	HandleError err;
   941 	IMenuPanel *panel;
   942 
   943 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   944 	{
   945 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   946 	}
   947 
   948 	IPluginFunction *pFunction;
   949 	if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
   950 	{
   951 		return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
   952 	}
   953 
   954 	CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
   955 	if (!panel->SendDisplay(params[2], handler, params[4]))
   956 	{
   957 		g_MenuHelpers.FreePanelHandler(handler);
   958 	}
   959 
   960 	return 1;
   961 }
   962 
   963 static cell_t SetPanelKeys(IPluginContext *pContext, const cell_t *params)
   964 {
   965 	Handle_t hndl = (Handle_t)params[1];
   966 	HandleError err;
   967 	IMenuPanel *panel;
   968 
   969 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   970 	{
   971 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   972 	}
   973 
   974 	return panel->SetSelectableKeys(params[2]);
   975 }
   976 
   977 static cell_t GetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
   978 {
   979 	Handle_t hndl = (Handle_t)params[1];
   980 	HandleError err;
   981 	IMenuPanel *panel;
   982 
   983 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   984 	{
   985 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
   986 	}
   987 
   988 	return panel->GetCurrentKey();
   989 }
   990 
   991 static cell_t SetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
   992 {
   993 	Handle_t hndl = (Handle_t)params[1];
   994 	HandleError err;
   995 	IMenuPanel *panel;
   996 
   997 	if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
   998 	{
   999 		return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
  1000 	}
  1001 
  1002 	return panel->SetCurrentKey(params[2]) ? 1 : 0;
  1003 }
  1004 
  1005 REGISTER_NATIVES(menuNatives)
  1006 {
  1007 	{"AddMenuItem",				AddMenuItem},
  1008 	{"CanPanelDrawFlags",		CanPanelDrawFlags},
  1009 	{"CancelClientMenu",		CancelClientMenu},
  1010 	{"CancelMenu",				CancelMenu},
  1011 	{"CreateMenu",				CreateMenu},
  1012 	{"CreateMenuEx",			CreateMenuEx},
  1013 	{"CreatePanel",				CreatePanel},
  1014 	{"CreatePanelFromMenu",		CreatePanelFromMenu},
  1015 	{"DisplayMenu",				DisplayMenu},
  1016 	{"DrawPanelItem",			DrawPanelItem},
  1017 	{"DrawPanelText",			DrawPanelText},
  1018 	{"GetClientMenu",			GetClientMenu},
  1019 	{"GetMaxPageItems",			GetMaxPageItems},
  1020 	{"GetMenuExitBackButton",	GetMenuExitBackButton},
  1021 	{"GetMenuExitButton",		GetMenuExitButton},
  1022 	{"GetMenuItem",				GetMenuItem},
  1023 	{"GetMenuItemCount",		GetMenuItemCount},
  1024 	{"GetMenuPagination",		GetMenuPagination},
  1025 	{"GetMenuStyle",			GetMenuStyle},
  1026 	{"GetMenuStyleHandle",		GetMenuStyleHandle},
  1027 	{"GetPanelCurrentKey",		GetPanelCurrentKey},
  1028 	{"GetPanelStyle",			GetPanelStyle},
  1029 	{"InsertMenuItem",			InsertMenuItem},
  1030 	{"IsVoteInProgress",		IsVoteInProgress},
  1031 	{"RemoveAllMenuItems",		RemoveAllMenuItems},
  1032 	{"RemoveMenuItem",			RemoveMenuItem},
  1033 	{"SendPanelToClient",		SendPanelToClient},
  1034 	{"SetMenuExitBackButton",	SetMenuExitBackButton},
  1035 	{"SetMenuExitButton",		SetMenuExitButton},
  1036 	{"SetMenuPagination",		SetMenuPagination},
  1037 	{"SetMenuTitle",			SetMenuTitle},
  1038 	{"SetPanelCurrentKey",		SetPanelCurrentKey},
  1039 	{"SetPanelTitle",			SetPanelTitle},
  1040 	{"SetPanelKeys",			SetPanelKeys},
  1041 	{"VoteMenu",				VoteMenu},
  1042 	{NULL,						NULL},
  1043 };