1693 lines
46 KiB
C++
1693 lines
46 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 "common_logic.h"
|
|
#include <sh_stack.h>
|
|
#include <IMenuManager.h>
|
|
#include <IPlayerHelpers.h>
|
|
#include "DebugReporter.h"
|
|
#if defined MENU_DEBUG
|
|
#include "Logger.h"
|
|
#endif
|
|
#include <ISourceMod.h>
|
|
#include <stdlib.h>
|
|
#include <bridge/include/IScriptManager.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 */
|
|
};
|
|
|
|
static HandleError ReadMenuHandle(Handle_t handle, IBaseMenu **menu)
|
|
{
|
|
static HandleType_t menuType = NO_HANDLE_TYPE;
|
|
if (menuType == NO_HANDLE_TYPE && !handlesys->FindHandleType("IBaseMenu", &menuType))
|
|
{
|
|
// This should never happen so exact error doesn't matter.
|
|
return HandleError_Index;
|
|
}
|
|
|
|
HandleSecurity sec;
|
|
sec.pIdentity = g_pCoreIdent;
|
|
sec.pOwner = NULL;
|
|
return handlesys->ReadHandle(handle, menuType, &sec, (void **)menu);
|
|
}
|
|
|
|
static HandleError ReadStyleHandle(Handle_t handle, IMenuStyle **style)
|
|
{
|
|
static HandleType_t styleType = NO_HANDLE_TYPE;
|
|
if (styleType == NO_HANDLE_TYPE && !handlesys->FindHandleType("IMenuStyle", &styleType))
|
|
{
|
|
// This should never happen so exact error doesn't matter.
|
|
return HandleError_Index;
|
|
}
|
|
|
|
HandleSecurity sec;
|
|
|
|
sec.pIdentity = g_pCoreIdent;
|
|
sec.pOwner = g_pCoreIdent;
|
|
|
|
return handlesys->ReadHandle(handle, styleType, &sec, (void **)style);
|
|
}
|
|
|
|
static IMenuStyle &ValveMenuStyle()
|
|
{
|
|
static IMenuStyle *valveMenuStyle = menus->FindStyleByName("valve");
|
|
return *valveMenuStyle;
|
|
}
|
|
|
|
static IMenuStyle &RadioMenuStyle()
|
|
{
|
|
static IMenuStyle *radioMenuStyle = menus->FindStyleByName("radio");
|
|
return *radioMenuStyle;
|
|
}
|
|
|
|
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 = handlesys->CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
|
|
m_TempPanelType = handlesys->CreateType("TempIMenuPanel", this, m_PanelType, NULL, NULL, g_pCoreIdent, NULL);
|
|
scripts->AddPluginsListener(this);
|
|
}
|
|
|
|
virtual void OnSourceModShutdown()
|
|
{
|
|
scripts->RemovePluginsListener(this);
|
|
handlesys->RemoveType(m_TempPanelType, g_pCoreIdent);
|
|
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 = scripts->FindPluginByContext(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 = playerhelpers->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);
|
|
playerhelpers->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;
|
|
handlesys->InitAccessDefaults(NULL, &access);
|
|
access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
|
|
|
|
Handle_t hndl = handlesys->CreateHandleEx(g_MenuHelpers.GetTempPanelType(), panel, &sec, &access, NULL);
|
|
|
|
DoAction(menu, MenuAction_Display, client, hndl);
|
|
|
|
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 = playerhelpers->SetReplyTo(SM_REPLY_CHAT);
|
|
DoAction(menu, MenuAction_Select, client, item);
|
|
playerhelpers->SetReplyTo(old_reply);
|
|
|
|
s_CurSelectPosition = old_pos;
|
|
}
|
|
|
|
void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
|
|
{
|
|
unsigned int old_reply = playerhelpers->SetReplyTo(SM_REPLY_CHAT);
|
|
DoAction(menu, MenuAction_Cancel, client, (cell_t)reason);
|
|
playerhelpers->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 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 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 &ValveMenuStyle();
|
|
} else if (cell == MenuStyle_Radio
|
|
&& RadioMenuStyle().IsSupported())
|
|
{
|
|
return &RadioMenuStyle();
|
|
}
|
|
|
|
return menus->GetDefaultStyle();
|
|
}
|
|
|
|
/***********************************
|
|
**** NATIVE DEFINITIONS ***********
|
|
***********************************/
|
|
|
|
static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
|
|
{
|
|
IMenuStyle *style = 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 = 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 = 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 (menus->IsVoteInProgress())
|
|
{
|
|
return pContext->ThrowNativeError("A vote is already in progress");
|
|
}
|
|
|
|
Handle_t hndl = (Handle_t)params[1];
|
|
HandleError err;
|
|
IBaseMenu *menu;
|
|
|
|
if ((err = 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);
|
|
|
|
cell_t flags = 0;
|
|
if (params[0] >= 5)
|
|
{
|
|
flags = params[5];
|
|
}
|
|
|
|
if (!menus->StartVote(menu, params[3], addr, params[4], flags))
|
|
{
|
|
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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = ReadMenuHandle(params[1], &menu)) != HandleError_None)
|
|
{
|
|
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
|
|
}
|
|
|
|
char buffer[1024];
|
|
g_pSM->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 = 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 = 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 = 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 = 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 = 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 SetMenuNoVoteButton(IPluginContext *pContext, const cell_t *params)
|
|
{
|
|
Handle_t hndl = (Handle_t)params[1];
|
|
HandleError err;
|
|
IBaseMenu *menu;
|
|
|
|
if ((err = 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_NOVOTE;
|
|
} else {
|
|
flags &= ~MENUFLAG_BUTTON_NOVOTE;
|
|
}
|
|
|
|
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 = 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 = ReadMenuHandle(params[1], &menu)) != HandleError_None)
|
|
{
|
|
return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
|
|
}
|
|
|
|
menus->CancelMenu(menu);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
|
|
{
|
|
return 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 = 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 = ReadStyleHandle(params[1], &style)) != HandleError_None)
|
|
{
|
|
return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
|
|
}
|
|
} else {
|
|
style = 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 = ReadStyleHandle(params[1], &style)) != HandleError_None)
|
|
{
|
|
return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
|
|
}
|
|
} else {
|
|
style = 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 = ReadStyleHandle(params[1], &style)) != HandleError_None)
|
|
{
|
|
return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
|
|
}
|
|
} else {
|
|
style = 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 = ReadStyleHandle(params[1], &style)) != HandleError_None)
|
|
{
|
|
return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
|
|
}
|
|
} else {
|
|
style = 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 = ReadStyleHandle(params[1], &style)) != HandleError_None)
|
|
{
|
|
return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
|
|
}
|
|
} else {
|
|
style = 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 = 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 = 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 (!menus->IsVoteInProgress())
|
|
{
|
|
return pContxt->ThrowNativeError("No vote is in progress");
|
|
}
|
|
|
|
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 = 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 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 = playerhelpers->GetGamePlayer(client)) == NULL)
|
|
{
|
|
return pContext->ThrowNativeError("Invalid client index %d", client);
|
|
}
|
|
|
|
if (!menus->IsVoteInProgress())
|
|
{
|
|
return pContext->ThrowNativeError("No vote is in progress");
|
|
}
|
|
|
|
return menus->IsClientInVotePool(client) ? 1 : 0;
|
|
}
|
|
|
|
static cell_t RedrawClientVoteMenu(IPluginContext *pContext, const cell_t *params)
|
|
{
|
|
int client;
|
|
IGamePlayer *pPlayer;
|
|
|
|
client = params[1];
|
|
if ((pPlayer = playerhelpers->GetGamePlayer(client)) == NULL)
|
|
{
|
|
return pContext->ThrowNativeError("Invalid client index %d", client);
|
|
}
|
|
|
|
if (!menus->IsVoteInProgress())
|
|
{
|
|
return pContext->ThrowNativeError("No vote is in progress");
|
|
}
|
|
|
|
if (!menus->IsClientInVotePool(client))
|
|
{
|
|
return pContext->ThrowNativeError("Client is not in the voting pool");
|
|
}
|
|
|
|
bool revote = true;
|
|
if (params[0] >= 2 && !params[2])
|
|
{
|
|
revote = false;
|
|
}
|
|
|
|
return menus->RedrawClientVoteMenu2(client, revote) ? 1 : 0;
|
|
}
|
|
|
|
class EmptyMenuHandler : public IMenuHandler
|
|
{
|
|
public:
|
|
} s_EmptyMenuHandler;
|
|
|
|
static cell_t InternalShowMenu(IPluginContext *pContext, const cell_t *params)
|
|
{
|
|
int client = params[1];
|
|
IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(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 (!RadioMenuStyle().IsSupported())
|
|
{
|
|
return pContext->ThrowNativeError("Radio menus are not supported on this mod");
|
|
}
|
|
|
|
char *str;
|
|
pContext->LocalToString(params[2], &str);
|
|
|
|
IMenuPanel *pPanel = RadioMenuStyle().CreatePanel();
|
|
|
|
if (pPanel == NULL)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
pPanel->DirectSet(str);
|
|
pPanel->SetSelectableKeys(params[4]);
|
|
|
|
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},
|
|
{"SetMenuNoVoteButton", SetMenuNoVoteButton},
|
|
|
|
// Transitional syntax support.
|
|
{"Panel.Panel", CreatePanel},
|
|
{"Panel.TextRemaining.get", GetPanelTextRemaining},
|
|
{"Panel.CurrentKey.get", GetPanelCurrentKey},
|
|
{"Panel.CurrentKey.set", SetPanelCurrentKey},
|
|
{"Panel.Style", GetPanelStyle},
|
|
{"Panel.CanDrawFlags", CanPanelDrawFlags},
|
|
{"Panel.SetTitle", SetPanelTitle},
|
|
{"Panel.SetKeys", SetPanelKeys},
|
|
{"Panel.Send", SendPanelToClient},
|
|
{"Panel.DrawItem", DrawPanelItem},
|
|
{"Panel.DrawText", DrawPanelText},
|
|
|
|
{"Menu.Menu", CreateMenu},
|
|
{"Menu.Display", DisplayMenu},
|
|
{"Menu.DisplayAt", DisplayMenuAtItem},
|
|
{"Menu.AddItem", AddMenuItem},
|
|
{"Menu.InsertItem", InsertMenuItem},
|
|
{"Menu.RemoveItem", RemoveMenuItem},
|
|
{"Menu.RemoveAllItems", RemoveAllMenuItems},
|
|
{"Menu.GetItem", GetMenuItem},
|
|
{"Menu.GetTitle", GetMenuTitle},
|
|
{"Menu.SetTitle", SetMenuTitle},
|
|
{"Menu.ToPanel", CreatePanelFromMenu},
|
|
{"Menu.Cancel", CancelMenu},
|
|
{"Menu.DisplayVote", VoteMenu},
|
|
{"Menu.Pagination.get", GetMenuPagination},
|
|
{"Menu.Pagination.set", SetMenuPagination},
|
|
{"Menu.OptionFlags.get", GetMenuOptionFlags},
|
|
{"Menu.OptionFlags.set", SetMenuOptionFlags},
|
|
{"Menu.ExitButton.get", GetMenuExitButton},
|
|
{"Menu.ExitButton.set", SetMenuExitButton},
|
|
{"Menu.ExitBackButton.get", GetMenuExitBackButton},
|
|
{"Menu.ExitBackButton.set", SetMenuExitBackButton},
|
|
{"Menu.NoVoteButton.set", SetMenuNoVoteButton},
|
|
{"Menu.VoteResultCallback.set", SetVoteResultCallback},
|
|
{"Menu.ItemCount.get", GetMenuItemCount},
|
|
{"Menu.Style.get", GetMenuStyle},
|
|
{"Menu.Selection.get", GetMenuSelectionPosition},
|
|
|
|
{NULL, NULL},
|
|
};
|
|
|