sourcemod/core/smn_menus.cpp
Scott Ehlert 251cced1f8 Spring Cleaning, Part Ichi (1)
Various minor things done to project files
Updated sample extension project file and updated makefile to the new unified version (more changes likely on the way)
Updated regex project file and makefile

--HG--
extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%401971
2008-03-30 07:00:22 +00:00

1525 lines
41 KiB
C++

/**
* 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 <http://www.gnu.org/licenses/>.
*
* 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 <http://www.sourcemod.net/license.php>.
*
* Version: $Id$
*/
#include "sm_globals.h"
#include <sh_stack.h>
#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<CPanelHandler *> m_FreePanelHandlers;
CStack<CMenuHandler *> m_FreeMenuHandlers;
CVector<CPanelHandler *> 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; i<results->num_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; i<results->num_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; i<results->num_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(LANG_SERVER);
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 *)&params[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;
}
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},
{"IsVoteInProgress", IsVoteInProgress},
{"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},
};