diff -r 000000000000 -r c24a6fa957db core/smn_menus.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/core/smn_menus.cpp Sun Jul 06 04:49:55 2008 +0000
@@ -0,0 +1,1569 @@
+/**
+ * vim: set ts=4 :
+ * =============================================================================
+ * SourceMod
+ * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ *
+ * As a special exception, AlliedModders LLC gives you permission to link the
+ * code of this program (as well as its derivative works) to "Half-Life 2," the
+ * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
+ * by the Valve Corporation. You must obey the GNU General Public License in
+ * all respects for all other code used. Additionally, AlliedModders LLC grants
+ * this exception to all derivative works. AlliedModders LLC defines further
+ * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
+ * or .
+ *
+ * Version: $Id$
+ */
+
+#include "sm_globals.h"
+#include
+#include "DebugReporter.h"
+#include "MenuManager.h"
+#include "MenuStyle_Valve.h"
+#include "MenuStyle_Radio.h"
+#include "HandleSys.h"
+#include "PluginSys.h"
+#include "PlayerManager.h"
+#include "sm_stringutil.h"
+#include "sourcemm_api.h"
+#if defined MENU_DEBUG
+#include "Logger.h"
+#endif
+#include "ChatTriggers.h"
+
+#if defined CreateMenu
+#undef CreateMenu
+#endif
+
+/**
+ * And God said, "let there be menus," and behold, there were menus.
+ * God saw the menus and they were good. And the evening and the morning
+ * were the third day.
+ */
+
+enum MenuAction
+{
+ MenuAction_Start = (1<<0), /**< A menu has been started (nothing passed) */
+ MenuAction_Display = (1<<1), /**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */
+ MenuAction_Select = (1<<2), /**< An item was selected (param1=client, param2=item) */
+ MenuAction_Cancel = (1<<3), /**< The menu was cancelled (param1=client, param2=item) */
+ MenuAction_End = (1<<4), /**< A menu's display/selection cycle is complete (nothing passed). */
+ MenuAction_VoteEnd = (1<<5), /**< (VOTE ONLY): A vote sequence has ended (param1=chosen item) */
+ MenuAction_VoteStart = (1<<6), /**< (VOTE ONLY): A vote sequence has started */
+ MenuAction_VoteCancel = (1<<7), /**< (VOTE ONLY): A vote sequence has been cancelled (nothing passed) */
+ MenuAction_DrawItem = (1<<8), /**< A style is being drawn; return the new style (param1=client, param2=item) */
+ MenuAction_DisplayItem = (1<<9), /**< the odd duck */
+};
+
+class CPanelHandler : public IMenuHandler
+{
+ friend class MenuNativeHelpers;
+public:
+ CPanelHandler()
+ {
+ }
+ void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
+ void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
+private:
+ IPluginFunction *m_pFunc;
+ IPlugin *m_pPlugin;
+};
+
+class CMenuHandler : public IMenuHandler
+{
+ friend class MenuNativeHelpers;
+public:
+ CMenuHandler(IPluginFunction *pBasic, int flags);
+public:
+ void OnMenuStart(IBaseMenu *menu);
+ void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
+ void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page);
+ void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
+ void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
+ void OnMenuDestroy(IBaseMenu *menu);
+ void OnMenuVoteStart(IBaseMenu *menu);
+ void OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results);
+ void OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason);
+ void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
+ unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
+ bool OnSetHandlerOption(const char *option, const void *data);
+#if 0
+ void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
+ void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
+#endif
+private:
+ cell_t DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res=0);
+private:
+ IPluginFunction *m_pBasic;
+ int m_Flags;
+ IPluginFunction *m_pVoteResults;
+ cell_t m_fnVoteResult;
+};
+
+/**
+ * GLOBAL CLASS FOR HELPERS
+ */
+
+class MenuNativeHelpers :
+ public SMGlobalClass,
+ public IHandleTypeDispatch,
+ public IPluginsListener
+{
+public:
+ virtual void OnSourceModAllInitialized()
+ {
+ m_PanelType = g_HandleSys.CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
+ m_TempPanelType = g_HandleSys.CreateType("TempIMenuPanel", this, m_PanelType, NULL, NULL, g_pCoreIdent, NULL);
+ g_PluginSys.AddPluginsListener(this);
+ }
+
+ virtual void OnSourceModShutdown()
+ {
+ g_HandleSys.RemoveType(m_TempPanelType, g_pCoreIdent);
+ g_HandleSys.RemoveType(m_PanelType, g_pCoreIdent);
+
+ while (!m_FreePanelHandlers.empty())
+ {
+ delete m_FreePanelHandlers.front();
+ m_FreePanelHandlers.pop();
+ }
+
+ while (!m_FreeMenuHandlers.empty())
+ {
+ delete m_FreeMenuHandlers.front();
+ m_FreeMenuHandlers.pop();
+ }
+ }
+
+ virtual void OnHandleDestroy(HandleType_t type, void *object)
+ {
+ if (type == m_TempPanelType)
+ {
+ return;
+ }
+
+ IMenuPanel *panel = (IMenuPanel *)object;
+ panel->DeleteThis();
+ }
+
+ virtual bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
+ {
+ *pSize = ((IMenuPanel *)object)->GetApproxMemUsage();
+ return true;
+ }
+
+ /**
+ * It is extremely important that unloaded plugins don't crash.
+ * Thus, if a plugin unloads, we run through every handler we have.
+ * This means we do almost no runtime work for keeping track of
+ * our panel handlers (we don't have to store a list of the running
+ * ones), but when push comes to shove, we have to scan them all
+ * in case any of them are active.
+ */
+ virtual void OnPluginUnloaded(IPlugin *plugin)
+ {
+ for (size_t i = 0; i < m_PanelHandlers.size(); i++)
+ {
+ if (m_PanelHandlers[i]->m_pPlugin == plugin)
+ {
+ m_PanelHandlers[i]->m_pPlugin = NULL;
+ m_PanelHandlers[i]->m_pFunc = NULL;
+ }
+ }
+ }
+
+ inline HandleType_t GetPanelType()
+ {
+ return m_PanelType;
+ }
+
+ inline HandleType_t GetTempPanelType()
+ {
+ return m_TempPanelType;
+ }
+
+ CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
+ {
+ CPanelHandler *handler;
+ if (m_FreePanelHandlers.empty())
+ {
+ handler = new CPanelHandler;
+ m_PanelHandlers.push_back(handler);
+ } else {
+ handler = m_FreePanelHandlers.front();
+ m_FreePanelHandlers.pop();
+ }
+ handler->m_pFunc = pFunction;
+ handler->m_pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
+ return handler;
+ }
+
+ void FreePanelHandler(CPanelHandler *handler)
+ {
+ handler->m_pFunc = NULL;
+ handler->m_pPlugin = NULL;
+ m_FreePanelHandlers.push(handler);
+ }
+
+ CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
+ {
+ CMenuHandler *handler;
+ if (m_FreeMenuHandlers.empty())
+ {
+ handler = new CMenuHandler(pFunction, flags);
+ } else {
+ handler = m_FreeMenuHandlers.front();
+ m_FreeMenuHandlers.pop();
+ handler->m_pBasic = pFunction;
+ handler->m_Flags = flags;
+ handler->m_pVoteResults = NULL;
+ }
+ return handler;
+ }
+
+ void FreeMenuHandler(CMenuHandler *handler)
+ {
+ m_FreeMenuHandlers.push(handler);
+ }
+
+private:
+ HandleType_t m_PanelType;
+ HandleType_t m_TempPanelType;
+ CStack m_FreePanelHandlers;
+ CStack m_FreeMenuHandlers;
+ CVector m_PanelHandlers;
+} g_MenuHelpers;
+
+/**
+ * BASIC PANEL HANDLER WRAPPER
+ */
+
+void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
+{
+ if (m_pFunc)
+ {
+ m_pFunc->PushCell(BAD_HANDLE);
+ m_pFunc->PushCell(MenuAction_Cancel);
+ m_pFunc->PushCell(client);
+ m_pFunc->PushCell(reason);
+ m_pFunc->Execute(NULL);
+ }
+ g_MenuHelpers.FreePanelHandler(this);
+}
+
+void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
+{
+ if (m_pFunc)
+ {
+ unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
+ m_pFunc->PushCell(BAD_HANDLE);
+ m_pFunc->PushCell(MenuAction_Select);
+ m_pFunc->PushCell(client);
+ m_pFunc->PushCell(item);
+ m_pFunc->Execute(NULL);
+ g_ChatTriggers.SetReplyTo(old_reply);
+ }
+ g_MenuHelpers.FreePanelHandler(this);
+}
+
+static IMenuPanel *s_pCurPanel = NULL;
+static unsigned int s_CurPanelReturn = 0;
+static const ItemDrawInfo *s_CurDrawInfo = NULL;
+static unsigned int *s_CurSelectPosition = NULL;
+
+/**
+ * MENU HANDLER WRAPPER
+ */
+CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) :
+ m_pBasic(pBasic), m_Flags(flags), m_pVoteResults(NULL)
+{
+ /* :TODO: We can probably cache the handle ahead of time */
+}
+
+void CMenuHandler::OnMenuStart(IBaseMenu *menu)
+{
+ if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
+ {
+ DoAction(menu, MenuAction_Start, 0, 0);
+ }
+}
+
+void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
+{
+ if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
+ {
+ HandleSecurity sec;
+ sec.pIdentity = g_pCoreIdent;
+ sec.pOwner = m_pBasic->GetParentContext()->GetIdentity();
+
+ HandleAccess access;
+ g_HandleSys.InitAccessDefaults(NULL, &access);
+ access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
+
+ Handle_t hndl = g_HandleSys.CreateHandleEx(g_MenuHelpers.GetTempPanelType(), panel, &sec, &access, NULL);
+
+ DoAction(menu, MenuAction_Display, client, hndl);
+
+ g_HandleSys.FreeHandle(hndl, &sec);
+ }
+}
+
+void CMenuHandler::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
+{
+ /* Save old position first. */
+ unsigned int first_item = item_on_page;
+ unsigned int *old_pos = s_CurSelectPosition;
+
+ s_CurSelectPosition = &first_item;
+
+ unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
+ DoAction(menu, MenuAction_Select, client, item);
+ g_ChatTriggers.SetReplyTo(old_reply);
+
+ s_CurSelectPosition = old_pos;
+}
+
+void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
+{
+ unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
+ DoAction(menu, MenuAction_Cancel, client, (cell_t)reason);
+ g_ChatTriggers.SetReplyTo(old_reply);
+}
+
+void CMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
+{
+ DoAction(menu, MenuAction_End, reason, 0);
+}
+
+void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
+{
+ g_MenuHelpers.FreeMenuHandler(this);
+}
+
+void CMenuHandler::OnMenuVoteStart(IBaseMenu *menu)
+{
+ DoAction(menu, MenuAction_VoteStart, 0, 0);
+}
+
+void CMenuHandler::OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason)
+{
+ DoAction(menu, MenuAction_VoteCancel, reason, 0);
+}
+
+void CMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
+{
+ if ((m_Flags & (int)MenuAction_DrawItem) == (int)MenuAction_DrawItem)
+ {
+ cell_t result = DoAction(menu, MenuAction_DrawItem, client, item, style);
+ style = (unsigned int)result;
+ }
+}
+
+unsigned int CMenuHandler::OnMenuDisplayItem(IBaseMenu *menu,
+ int client,
+ IMenuPanel *panel,
+ unsigned int item,
+ const ItemDrawInfo &dr)
+{
+ if ((m_Flags & (int)MenuAction_DisplayItem) == (int)MenuAction_DisplayItem)
+ {
+ IMenuPanel *oldpanel = s_pCurPanel;
+ unsigned int oldret = s_CurPanelReturn;
+ const ItemDrawInfo *oldinfo = s_CurDrawInfo;
+ s_pCurPanel = panel;
+ s_CurPanelReturn = 0;
+ s_CurDrawInfo = &dr;
+
+ cell_t res = DoAction(menu, MenuAction_DisplayItem, client, item, 0);
+
+ if (!res)
+ {
+ res = s_CurPanelReturn;
+ }
+
+ s_pCurPanel = oldpanel;
+ s_CurPanelReturn = oldret;
+ s_CurDrawInfo = oldinfo;
+
+ return res;
+ }
+
+ return 0;
+}
+
+cell_t CMenuHandler::DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res)
+{
+#if defined MENU_DEBUG
+ g_Logger.LogMessage("[SM_MENU] CMenuHandler::DoAction() (menu %p/%08x) (action %d) (param1 %d) (param2 %d)",
+ menu,
+ menu->GetHandle(),
+ action,
+ param1,
+ param2);
+#endif
+ cell_t res = def_res;
+ m_pBasic->PushCell(menu->GetHandle());
+ m_pBasic->PushCell((cell_t)action);
+ m_pBasic->PushCell(param1);
+ m_pBasic->PushCell(param2);
+ m_pBasic->Execute(&res);
+ return res;
+}
+
+void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
+{
+ if (!m_pVoteResults)
+ {
+ /* Call MenuAction_VoteEnd instead. See if there are any extra winners. */
+ unsigned int num_items = 1;
+ for (unsigned int i=1; inum_items; i++)
+ {
+ if (results->item_list[i].count != results->item_list[0].count)
+ {
+ break;
+ }
+ num_items++;
+ }
+
+ /* See if we need to pick a random winner. */
+ unsigned int winning_item;
+ if (num_items > 1)
+ {
+ /* Yes, we do. */
+ srand(time(NULL));
+ winning_item = rand() % num_items;
+ winning_item = results->item_list[winning_item].item;
+ } else {
+ /* No, take the first. */
+ winning_item = results->item_list[0].item;
+ }
+
+ unsigned int total_votes = results->num_votes;
+ unsigned int winning_votes = results->item_list[0].count;
+
+ DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
+ } else {
+ IPluginContext *pContext = m_pVoteResults->GetParentContext();
+ bool no_call = false;
+ int err;
+
+ /* First array */
+ cell_t client_array_address = -1;
+ cell_t *client_array_base = NULL;
+ cell_t client_array_size = results->num_clients + (results->num_clients * 2);
+ if (client_array_size)
+ {
+ if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
+ != SP_ERROR_NONE)
+ {
+ g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
+ no_call = true;
+ } else {
+ cell_t target_offs = sizeof(cell_t) * results->num_clients;
+ cell_t *cur_index = client_array_base;
+ cell_t *cur_array;
+ for (unsigned int i=0; inum_clients; i++)
+ {
+ /* Copy the array index */
+ *cur_index = target_offs;
+ /* Get the current array address */
+ cur_array = (cell_t *)((char *)cur_index + target_offs);
+ /* Store information */
+ cur_array[0] = results->client_list[i].client;
+ cur_array[1] = results->client_list[i].item;
+ /* Adjust for the new target by subtracting one indirection
+ * and adding one array.
+ */
+ target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
+ cur_index++;
+ }
+ }
+ }
+
+ /* Second array */
+ cell_t item_array_address = -1;
+ cell_t *item_array_base = NULL;
+ cell_t item_array_size = results->num_items + (results->num_items * 2);
+ if (item_array_size)
+ {
+ if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
+ != SP_ERROR_NONE)
+ {
+ g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
+ no_call = true;
+ } else {
+ cell_t target_offs = sizeof(cell_t) * results->num_items;
+ cell_t *cur_index = item_array_base;
+ cell_t *cur_array;
+ for (unsigned int i=0; inum_items; i++)
+ {
+ /* Copy the array index */
+ *cur_index = target_offs;
+ /* Get the current array address */
+ cur_array = (cell_t *)((char *)cur_index + target_offs);
+ /* Store information */
+ cur_array[0] = results->item_list[i].item;
+ cur_array[1] = results->item_list[i].count;
+ /* Adjust for the new target by subtracting one indirection
+ * and adding one array.
+ */
+ target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
+ cur_index++;
+ }
+ }
+ }
+
+ /* Finally, push everything */
+ if (!no_call)
+ {
+ m_pVoteResults->PushCell(menu->GetHandle());
+ m_pVoteResults->PushCell(results->num_votes);
+ m_pVoteResults->PushCell(results->num_clients);
+ m_pVoteResults->PushCell(client_array_address);
+ m_pVoteResults->PushCell(results->num_items);
+ m_pVoteResults->PushCell(item_array_address);
+ m_pVoteResults->Execute(NULL);
+ }
+
+ /* Free what we allocated, in reverse order as required */
+ if (item_array_address != -1)
+ {
+ pContext->HeapPop(item_array_address);
+ }
+ if (client_array_address != -1)
+ {
+ pContext->HeapPop(client_array_address);
+ }
+ }
+}
+
+bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
+{
+ if (strcmp(option, "set_vote_results_handler") == 0)
+ {
+ void **array = (void **)data;
+ m_pVoteResults = (IPluginFunction *)array[0];
+ m_fnVoteResult = *(cell_t *)((cell_t *)array[1]);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * INLINE FUNCTIONS FOR NATIVES
+ */
+
+inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
+{
+ return g_HandleSys.CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
+}
+
+inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
+{
+ HandleSecurity sec;
+ sec.pIdentity = g_pCoreIdent;
+ sec.pOwner = NULL;
+ return g_HandleSys.ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
+}
+
+inline IMenuStyle *GetStyleFromCell(cell_t cell)
+{
+ enum MenuStyle
+ {
+ MenuStyle_Default = 0, /**< The "default" menu style for the mod */
+ MenuStyle_Valve = 1, /**< The Valve provided menu style (Used on HL2DM) */
+ MenuStyle_Radio = 2, /**< The simpler menu style commonly used on CS:S */
+ };
+
+ if (cell == MenuStyle_Valve)
+ {
+ return &g_ValveMenuStyle;
+ } else if (cell == MenuStyle_Radio
+ && g_RadioMenuStyle.IsSupported())
+ {
+ return &g_RadioMenuStyle;
+ }
+
+ return g_Menus.GetDefaultStyle();
+}
+
+/***********************************
+ **** NATIVE DEFINITIONS ***********
+ ***********************************/
+
+static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
+{
+ IMenuStyle *style = g_Menus.GetDefaultStyle();
+ IPluginFunction *pFunction;
+
+ if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
+ {
+ return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
+ }
+
+ CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
+ IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
+
+ Handle_t hndl = menu->GetHandle();
+ if (!hndl)
+ {
+ menu->Destroy();
+ return BAD_HANDLE;
+ }
+
+ return hndl;
+}
+
+static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return menu->Display(params[2], params[3]) ? 1 : 0;
+}
+
+static cell_t DisplayMenuAtItem(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return menu->DisplayAtItem(params[2], params[4], params[3]) ? 1 : 0;
+}
+
+static cell_t VoteMenu(IPluginContext *pContext, const cell_t *params)
+{
+ if (g_Menus.IsVoteInProgress())
+ {
+ return pContext->ThrowNativeError("A vote is already in progress");
+ }
+
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ cell_t *addr;
+ pContext->LocalToPhysAddr(params[2], &addr);
+
+ if (!g_Menus.StartVote(menu, params[3], addr, params[4]))
+ {
+ return 0;
+ }
+
+ return 1;
+}
+
+static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ char *info;
+ ItemDrawInfo dr;
+
+ pContext->LocalToString(params[2], &info);
+ pContext->LocalToString(params[3], (char **)&dr.display);
+ dr.style = params[4];
+
+ return menu->AppendItem(info, dr) ? 1 : 0;
+}
+
+static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ char *info;
+ ItemDrawInfo dr;
+
+ pContext->LocalToString(params[3], &info);
+ pContext->LocalToString(params[4], (char **)&dr.display);
+ dr.style = params[5];
+
+ return menu->InsertItem(params[2], info, dr) ? 1 : 0;
+}
+
+static cell_t RemoveMenuItem(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return menu->RemoveItem(params[2]) ? 1 : 0;
+}
+
+static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ menu->RemoveAllItems();
+
+ return 1;
+}
+
+static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ ItemDrawInfo dr;
+ const char *info;
+
+ if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
+ {
+ return 0;
+ }
+
+ pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
+ pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
+
+ cell_t *addr;
+ pContext->LocalToPhysAddr(params[5], &addr);
+ *addr = dr.style;
+
+ return 1;
+}
+
+static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return menu->SetPagination(params[2]) ? 1 : 0;
+}
+
+static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return menu->GetPagination();
+}
+
+static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return menu->GetItemCount();
+}
+
+static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
+
+ char buffer[1024];
+ g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
+
+ menu->SetDefaultTitle(buffer);
+
+ return 1;
+}
+
+static cell_t GetMenuTitle(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ size_t written;
+ const char *title = menu->GetDefaultTitle();
+ pContext->StringToLocalUTF8(params[2], params[3], title, &written);
+
+ return (cell_t)written;
+}
+
+
+static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ IMenuPanel *panel = menu->CreatePanel();
+ hndl = MakePanelHandle(panel, pContext);
+ if (!hndl)
+ {
+ panel->DeleteThis();
+ }
+
+ return hndl;
+}
+
+static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
+}
+
+static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
+}
+
+static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ unsigned int flags = menu->GetMenuOptionFlags();
+
+ if (params[2])
+ {
+ flags |= MENUFLAG_BUTTON_EXIT;
+ } else {
+ flags &= ~MENUFLAG_BUTTON_EXIT;
+ }
+
+ menu->SetMenuOptionFlags(flags);
+ unsigned int new_flags = menu->GetMenuOptionFlags();
+
+ return (flags == new_flags);
+}
+
+static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ unsigned int flags = menu->GetMenuOptionFlags();
+
+ if (params[2])
+ {
+ flags |= MENUFLAG_BUTTON_EXITBACK;
+ } else {
+ flags &= ~MENUFLAG_BUTTON_EXITBACK;
+ }
+
+ menu->SetMenuOptionFlags(flags);
+
+ return 1;
+}
+
+static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ g_Menus.CancelMenu(menu);
+
+ return 1;
+}
+
+static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
+{
+ return g_Menus.IsVoteInProgress() ? 1 : 0;
+}
+
+static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return menu->GetDrawStyle()->GetHandle();
+}
+
+static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
+{
+ IMenuStyle *style = GetStyleFromCell(params[1]);
+ if (!style)
+ {
+ return BAD_HANDLE;
+ }
+
+ return style->GetHandle();
+}
+
+static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuStyle *style;
+
+ if (hndl != 0)
+ {
+ if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
+ }
+ } else {
+ style = g_Menus.GetDefaultStyle();
+ }
+
+ IMenuPanel *panel = style->CreatePanel();
+
+ hndl = MakePanelHandle(panel, pContext);
+ if (!hndl)
+ {
+ panel->DeleteThis();
+ return BAD_HANDLE;
+ }
+
+ return hndl;
+}
+
+static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuStyle *style;
+
+ if (hndl != 0)
+ {
+ if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
+ }
+ } else {
+ style = g_Menus.GetDefaultStyle();
+ }
+
+ IPluginFunction *pFunction;
+ if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
+ {
+ return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
+ }
+
+ CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
+
+ IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
+ hndl = pMenu->GetHandle();
+ if (!hndl)
+ {
+ pMenu->Destroy();
+ return BAD_HANDLE;
+ }
+
+ return hndl;
+}
+
+static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[2];
+ HandleError err;
+ IMenuStyle *style;
+
+ if (hndl != 0)
+ {
+ if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
+ }
+ } else {
+ style = g_Menus.GetDefaultStyle();
+ }
+
+ return style->GetClientMenu(params[1], NULL);
+}
+
+static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[3];
+ HandleError err;
+ IMenuStyle *style;
+
+ if (hndl != 0)
+ {
+ if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
+ }
+ } else {
+ style = g_Menus.GetDefaultStyle();
+ }
+
+ return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
+}
+
+static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuStyle *style;
+
+ if (hndl != 0)
+ {
+ if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
+ }
+ } else {
+ style = g_Menus.GetDefaultStyle();
+ }
+
+ return style->GetMaxPageItems();
+}
+
+static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuPanel *panel;
+
+ if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return panel->GetParentStyle()->GetHandle();
+}
+
+static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuPanel *panel;
+
+ if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ char *text;
+ pContext->LocalToString(params[2], &text);
+
+ panel->DrawTitle(text, params[3] ? true : false);
+
+ return 1;
+}
+
+static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuPanel *panel;
+
+ if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ ItemDrawInfo dr;
+ pContext->LocalToString(params[2], (char **)&dr.display);
+ dr.style = params[3];
+
+ return panel->DrawItem(dr);
+}
+
+static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuPanel *panel;
+
+ if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ char *text;
+ pContext->LocalToString(params[2], &text);
+
+ return panel->DrawRawLine(text) ? 1 : 0;
+}
+
+static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuPanel *panel;
+
+ if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return panel->CanDrawItem(params[2]);
+}
+
+static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuPanel *panel;
+
+ if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ IPluginFunction *pFunction;
+ if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
+ {
+ return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
+ }
+
+ CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
+ if (!panel->SendDisplay(params[2], handler, params[4]))
+ {
+ g_MenuHelpers.FreePanelHandler(handler);
+ }
+
+ return 1;
+}
+
+static cell_t SetPanelKeys(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuPanel *panel;
+
+ if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return panel->SetSelectableKeys(params[2]);
+}
+
+static cell_t GetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuPanel *panel;
+
+ if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return panel->GetCurrentKey();
+}
+
+static cell_t GetPanelTextRemaining(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuPanel *panel;
+
+ if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return panel->GetAmountRemaining();
+}
+
+static cell_t SetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IMenuPanel *panel;
+
+ if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return panel->SetCurrentKey(params[2]) ? 1 : 0;
+}
+
+static cell_t RedrawMenuItem(IPluginContext *pContext, const cell_t *params)
+{
+ if (!s_pCurPanel)
+ {
+ return pContext->ThrowNativeError("You can only call this once from a MenuAction_DisplayItem callback");
+ }
+
+ char *str;
+ pContext->LocalToString(params[1], &str);
+
+ ItemDrawInfo dr = *s_CurDrawInfo;
+ dr.display = str;
+
+ if ((s_CurPanelReturn = s_pCurPanel->DrawItem(dr)) != 0)
+ {
+ s_pCurPanel = NULL;
+ }
+
+ return s_CurPanelReturn;
+}
+
+static cell_t GetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ return menu->GetMenuOptionFlags();
+}
+
+static cell_t SetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ menu->SetMenuOptionFlags(params[2]);
+
+ return 1;
+}
+
+static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
+{
+ if (!g_Menus.IsVoteInProgress())
+ {
+ return pContxt->ThrowNativeError("No vote is in progress");
+ }
+
+ g_Menus.CancelVoting();
+
+ return 1;
+}
+
+static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
+{
+ Handle_t hndl = (Handle_t)params[1];
+ HandleError err;
+ IBaseMenu *menu;
+
+ if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
+ {
+ return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
+ }
+
+ IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
+ if (!pFunction)
+ {
+ return pContext->ThrowNativeError("Invalid function %x", params[2]);
+ }
+
+ void *array[2];
+ array[0] = pFunction;
+ array[1] = (void *)¶ms[2];
+
+ IMenuHandler *pHandler = menu->GetHandler();
+ if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
+ {
+ return pContext->ThrowNativeError("The given menu does not support this option");
+ }
+
+ return 1;
+}
+
+static cell_t CheckVoteDelay(IPluginContext *pContext, const cell_t *params)
+{
+ return g_Menus.GetRemainingVoteDelay();
+}
+
+static cell_t GetMenuSelectionPosition(IPluginContext *pContext, const cell_t *params)
+{
+ if (!s_CurSelectPosition)
+ {
+ return pContext->ThrowNativeError("Can only be called from inside a MenuAction_Select callback");
+ }
+
+ return *s_CurSelectPosition;
+}
+
+static cell_t IsClientInVotePool(IPluginContext *pContext, const cell_t *params)
+{
+ int client;
+ IGamePlayer *pPlayer;
+
+ client = params[1];
+ if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
+ {
+ return pContext->ThrowNativeError("Invalid client index %d", client);
+ }
+
+ if (!g_Menus.IsVoteInProgress())
+ {
+ return pContext->ThrowNativeError("No vote is in progress");
+ }
+
+ return g_Menus.IsClientInVotePool(client) ? 1 : 0;
+}
+
+static cell_t RedrawClientVoteMenu(IPluginContext *pContext, const cell_t *params)
+{
+ int client;
+ IGamePlayer *pPlayer;
+
+ client = params[1];
+ if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
+ {
+ return pContext->ThrowNativeError("Invalid client index %d", client);
+ }
+
+ if (!g_Menus.IsVoteInProgress())
+ {
+ return pContext->ThrowNativeError("No vote is in progress");
+ }
+
+ if (!g_Menus.IsClientInVotePool(client))
+ {
+ return pContext->ThrowNativeError("Client is not in the voting pool");
+ }
+
+ return g_Menus.RedrawClientVoteMenu(client) ? 1 : 0;
+}
+
+class EmptyMenuHandler : public IMenuHandler
+{
+public:
+} s_EmptyMenuHandler;
+
+static cell_t InternalShowMenu(IPluginContext *pContext, const cell_t *params)
+{
+ int client = params[1];
+ CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
+
+ if (pPlayer == NULL)
+ {
+ return pContext->ThrowNativeError("Invalid client index %d", client);
+ }
+ else if (!pPlayer->IsInGame())
+ {
+ return pContext->ThrowNativeError("Client %d is not in game", client);
+ }
+
+ if (!g_RadioMenuStyle.IsSupported())
+ {
+ return pContext->ThrowNativeError("Radio menus are not supported on this mod");
+ }
+
+ char *str;
+ pContext->LocalToString(params[2], &str);
+
+ IMenuPanel *pPanel = g_RadioMenuStyle.MakeRadioDisplay(str, params[4]);
+
+ if (pPanel == NULL)
+ {
+ return 0;
+ }
+
+ IMenuHandler *pHandler;
+ CPanelHandler *pActualHandler = NULL;
+ if (params[5] != -1)
+ {
+ IPluginFunction *pFunction = pContext->GetFunctionById(params[5]);
+ if (pFunction == NULL)
+ {
+ return pContext->ThrowNativeError("Invalid function index %x", params[5]);
+ }
+ pActualHandler = g_MenuHelpers.GetPanelHandler(pFunction);
+ }
+
+ if (pActualHandler == NULL)
+ {
+ pHandler = &s_EmptyMenuHandler;
+ }
+ else
+ {
+ pHandler = pActualHandler;
+ }
+
+ bool bSuccess = pPanel->SendDisplay(client, pHandler, params[3]);
+
+ pPanel->DeleteThis();
+
+ if (!bSuccess && pActualHandler != NULL)
+ {
+ g_MenuHelpers.FreePanelHandler(pActualHandler);
+ }
+
+ return bSuccess ? 1 : 0;
+}
+
+REGISTER_NATIVES(menuNatives)
+{
+ {"AddMenuItem", AddMenuItem},
+ {"CanPanelDrawFlags", CanPanelDrawFlags},
+ {"CancelClientMenu", CancelClientMenu},
+ {"CancelMenu", CancelMenu},
+ {"CancelVote", CancelVote},
+ {"CheckVoteDelay", CheckVoteDelay},
+ {"CreateMenu", CreateMenu},
+ {"CreateMenuEx", CreateMenuEx},
+ {"CreatePanel", CreatePanel},
+ {"CreatePanelFromMenu", CreatePanelFromMenu},
+ {"DisplayMenu", DisplayMenu},
+ {"DisplayMenuAtItem", DisplayMenuAtItem},
+ {"DrawPanelItem", DrawPanelItem},
+ {"DrawPanelText", DrawPanelText},
+ {"GetClientMenu", GetClientMenu},
+ {"GetMaxPageItems", GetMaxPageItems},
+ {"GetMenuExitBackButton", GetMenuExitBackButton},
+ {"GetMenuExitButton", GetMenuExitButton},
+ {"GetMenuItem", GetMenuItem},
+ {"GetMenuItemCount", GetMenuItemCount},
+ {"GetMenuOptionFlags", GetMenuOptionFlags},
+ {"GetMenuPagination", GetMenuPagination},
+ {"GetMenuSelectionPosition",GetMenuSelectionPosition},
+ {"GetMenuStyle", GetMenuStyle},
+ {"GetMenuStyleHandle", GetMenuStyleHandle},
+ {"GetMenuTitle", GetMenuTitle},
+ {"GetPanelTextRemaining", GetPanelTextRemaining},
+ {"GetPanelCurrentKey", GetPanelCurrentKey},
+ {"GetPanelStyle", GetPanelStyle},
+ {"InsertMenuItem", InsertMenuItem},
+ {"InternalShowMenu", InternalShowMenu},
+ {"IsClientInVotePool", IsClientInVotePool},
+ {"IsVoteInProgress", IsVoteInProgress},
+ {"RedrawClientVoteMenu", RedrawClientVoteMenu},
+ {"RedrawMenuItem", RedrawMenuItem},
+ {"RemoveAllMenuItems", RemoveAllMenuItems},
+ {"RemoveMenuItem", RemoveMenuItem},
+ {"SendPanelToClient", SendPanelToClient},
+ {"SetMenuExitBackButton", SetMenuExitBackButton},
+ {"SetMenuExitButton", SetMenuExitButton},
+ {"SetMenuOptionFlags", SetMenuOptionFlags},
+ {"SetMenuPagination", SetMenuPagination},
+ {"SetMenuTitle", SetMenuTitle},
+ {"SetPanelCurrentKey", SetPanelCurrentKey},
+ {"SetPanelTitle", SetPanelTitle},
+ {"SetPanelKeys", SetPanelKeys},
+ {"SetVoteResultCallback", SetVoteResultCallback},
+ {"VoteMenu", VoteMenu},
+ {NULL, NULL},
+};