1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/smn_menus.cpp Sun Jul 06 04:49:55 2008 +0000
1.3 @@ -0,0 +1,1569 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include "sm_globals.h"
1.36 +#include <sh_stack.h>
1.37 +#include "DebugReporter.h"
1.38 +#include "MenuManager.h"
1.39 +#include "MenuStyle_Valve.h"
1.40 +#include "MenuStyle_Radio.h"
1.41 +#include "HandleSys.h"
1.42 +#include "PluginSys.h"
1.43 +#include "PlayerManager.h"
1.44 +#include "sm_stringutil.h"
1.45 +#include "sourcemm_api.h"
1.46 +#if defined MENU_DEBUG
1.47 +#include "Logger.h"
1.48 +#endif
1.49 +#include "ChatTriggers.h"
1.50 +
1.51 +#if defined CreateMenu
1.52 +#undef CreateMenu
1.53 +#endif
1.54 +
1.55 +/**
1.56 + * And God said, "let there be menus," and behold, there were menus.
1.57 + * God saw the menus and they were good. And the evening and the morning
1.58 + * were the third day.
1.59 + */
1.60 +
1.61 +enum MenuAction
1.62 +{
1.63 + MenuAction_Start = (1<<0), /**< A menu has been started (nothing passed) */
1.64 + MenuAction_Display = (1<<1), /**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */
1.65 + MenuAction_Select = (1<<2), /**< An item was selected (param1=client, param2=item) */
1.66 + MenuAction_Cancel = (1<<3), /**< The menu was cancelled (param1=client, param2=item) */
1.67 + MenuAction_End = (1<<4), /**< A menu's display/selection cycle is complete (nothing passed). */
1.68 + MenuAction_VoteEnd = (1<<5), /**< (VOTE ONLY): A vote sequence has ended (param1=chosen item) */
1.69 + MenuAction_VoteStart = (1<<6), /**< (VOTE ONLY): A vote sequence has started */
1.70 + MenuAction_VoteCancel = (1<<7), /**< (VOTE ONLY): A vote sequence has been cancelled (nothing passed) */
1.71 + MenuAction_DrawItem = (1<<8), /**< A style is being drawn; return the new style (param1=client, param2=item) */
1.72 + MenuAction_DisplayItem = (1<<9), /**< the odd duck */
1.73 +};
1.74 +
1.75 +class CPanelHandler : public IMenuHandler
1.76 +{
1.77 + friend class MenuNativeHelpers;
1.78 +public:
1.79 + CPanelHandler()
1.80 + {
1.81 + }
1.82 + void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
1.83 + void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
1.84 +private:
1.85 + IPluginFunction *m_pFunc;
1.86 + IPlugin *m_pPlugin;
1.87 +};
1.88 +
1.89 +class CMenuHandler : public IMenuHandler
1.90 +{
1.91 + friend class MenuNativeHelpers;
1.92 +public:
1.93 + CMenuHandler(IPluginFunction *pBasic, int flags);
1.94 +public:
1.95 + void OnMenuStart(IBaseMenu *menu);
1.96 + void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
1.97 + void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page);
1.98 + void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
1.99 + void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
1.100 + void OnMenuDestroy(IBaseMenu *menu);
1.101 + void OnMenuVoteStart(IBaseMenu *menu);
1.102 + void OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results);
1.103 + void OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason);
1.104 + void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
1.105 + unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
1.106 + bool OnSetHandlerOption(const char *option, const void *data);
1.107 +#if 0
1.108 + void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
1.109 + void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
1.110 +#endif
1.111 +private:
1.112 + cell_t DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res=0);
1.113 +private:
1.114 + IPluginFunction *m_pBasic;
1.115 + int m_Flags;
1.116 + IPluginFunction *m_pVoteResults;
1.117 + cell_t m_fnVoteResult;
1.118 +};
1.119 +
1.120 +/**
1.121 + * GLOBAL CLASS FOR HELPERS
1.122 + */
1.123 +
1.124 +class MenuNativeHelpers :
1.125 + public SMGlobalClass,
1.126 + public IHandleTypeDispatch,
1.127 + public IPluginsListener
1.128 +{
1.129 +public:
1.130 + virtual void OnSourceModAllInitialized()
1.131 + {
1.132 + m_PanelType = g_HandleSys.CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
1.133 + m_TempPanelType = g_HandleSys.CreateType("TempIMenuPanel", this, m_PanelType, NULL, NULL, g_pCoreIdent, NULL);
1.134 + g_PluginSys.AddPluginsListener(this);
1.135 + }
1.136 +
1.137 + virtual void OnSourceModShutdown()
1.138 + {
1.139 + g_HandleSys.RemoveType(m_TempPanelType, g_pCoreIdent);
1.140 + g_HandleSys.RemoveType(m_PanelType, g_pCoreIdent);
1.141 +
1.142 + while (!m_FreePanelHandlers.empty())
1.143 + {
1.144 + delete m_FreePanelHandlers.front();
1.145 + m_FreePanelHandlers.pop();
1.146 + }
1.147 +
1.148 + while (!m_FreeMenuHandlers.empty())
1.149 + {
1.150 + delete m_FreeMenuHandlers.front();
1.151 + m_FreeMenuHandlers.pop();
1.152 + }
1.153 + }
1.154 +
1.155 + virtual void OnHandleDestroy(HandleType_t type, void *object)
1.156 + {
1.157 + if (type == m_TempPanelType)
1.158 + {
1.159 + return;
1.160 + }
1.161 +
1.162 + IMenuPanel *panel = (IMenuPanel *)object;
1.163 + panel->DeleteThis();
1.164 + }
1.165 +
1.166 + virtual bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
1.167 + {
1.168 + *pSize = ((IMenuPanel *)object)->GetApproxMemUsage();
1.169 + return true;
1.170 + }
1.171 +
1.172 + /**
1.173 + * It is extremely important that unloaded plugins don't crash.
1.174 + * Thus, if a plugin unloads, we run through every handler we have.
1.175 + * This means we do almost no runtime work for keeping track of
1.176 + * our panel handlers (we don't have to store a list of the running
1.177 + * ones), but when push comes to shove, we have to scan them all
1.178 + * in case any of them are active.
1.179 + */
1.180 + virtual void OnPluginUnloaded(IPlugin *plugin)
1.181 + {
1.182 + for (size_t i = 0; i < m_PanelHandlers.size(); i++)
1.183 + {
1.184 + if (m_PanelHandlers[i]->m_pPlugin == plugin)
1.185 + {
1.186 + m_PanelHandlers[i]->m_pPlugin = NULL;
1.187 + m_PanelHandlers[i]->m_pFunc = NULL;
1.188 + }
1.189 + }
1.190 + }
1.191 +
1.192 + inline HandleType_t GetPanelType()
1.193 + {
1.194 + return m_PanelType;
1.195 + }
1.196 +
1.197 + inline HandleType_t GetTempPanelType()
1.198 + {
1.199 + return m_TempPanelType;
1.200 + }
1.201 +
1.202 + CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
1.203 + {
1.204 + CPanelHandler *handler;
1.205 + if (m_FreePanelHandlers.empty())
1.206 + {
1.207 + handler = new CPanelHandler;
1.208 + m_PanelHandlers.push_back(handler);
1.209 + } else {
1.210 + handler = m_FreePanelHandlers.front();
1.211 + m_FreePanelHandlers.pop();
1.212 + }
1.213 + handler->m_pFunc = pFunction;
1.214 + handler->m_pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
1.215 + return handler;
1.216 + }
1.217 +
1.218 + void FreePanelHandler(CPanelHandler *handler)
1.219 + {
1.220 + handler->m_pFunc = NULL;
1.221 + handler->m_pPlugin = NULL;
1.222 + m_FreePanelHandlers.push(handler);
1.223 + }
1.224 +
1.225 + CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
1.226 + {
1.227 + CMenuHandler *handler;
1.228 + if (m_FreeMenuHandlers.empty())
1.229 + {
1.230 + handler = new CMenuHandler(pFunction, flags);
1.231 + } else {
1.232 + handler = m_FreeMenuHandlers.front();
1.233 + m_FreeMenuHandlers.pop();
1.234 + handler->m_pBasic = pFunction;
1.235 + handler->m_Flags = flags;
1.236 + handler->m_pVoteResults = NULL;
1.237 + }
1.238 + return handler;
1.239 + }
1.240 +
1.241 + void FreeMenuHandler(CMenuHandler *handler)
1.242 + {
1.243 + m_FreeMenuHandlers.push(handler);
1.244 + }
1.245 +
1.246 +private:
1.247 + HandleType_t m_PanelType;
1.248 + HandleType_t m_TempPanelType;
1.249 + CStack<CPanelHandler *> m_FreePanelHandlers;
1.250 + CStack<CMenuHandler *> m_FreeMenuHandlers;
1.251 + CVector<CPanelHandler *> m_PanelHandlers;
1.252 +} g_MenuHelpers;
1.253 +
1.254 +/**
1.255 + * BASIC PANEL HANDLER WRAPPER
1.256 + */
1.257 +
1.258 +void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
1.259 +{
1.260 + if (m_pFunc)
1.261 + {
1.262 + m_pFunc->PushCell(BAD_HANDLE);
1.263 + m_pFunc->PushCell(MenuAction_Cancel);
1.264 + m_pFunc->PushCell(client);
1.265 + m_pFunc->PushCell(reason);
1.266 + m_pFunc->Execute(NULL);
1.267 + }
1.268 + g_MenuHelpers.FreePanelHandler(this);
1.269 +}
1.270 +
1.271 +void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
1.272 +{
1.273 + if (m_pFunc)
1.274 + {
1.275 + unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
1.276 + m_pFunc->PushCell(BAD_HANDLE);
1.277 + m_pFunc->PushCell(MenuAction_Select);
1.278 + m_pFunc->PushCell(client);
1.279 + m_pFunc->PushCell(item);
1.280 + m_pFunc->Execute(NULL);
1.281 + g_ChatTriggers.SetReplyTo(old_reply);
1.282 + }
1.283 + g_MenuHelpers.FreePanelHandler(this);
1.284 +}
1.285 +
1.286 +static IMenuPanel *s_pCurPanel = NULL;
1.287 +static unsigned int s_CurPanelReturn = 0;
1.288 +static const ItemDrawInfo *s_CurDrawInfo = NULL;
1.289 +static unsigned int *s_CurSelectPosition = NULL;
1.290 +
1.291 +/**
1.292 + * MENU HANDLER WRAPPER
1.293 + */
1.294 +CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) :
1.295 + m_pBasic(pBasic), m_Flags(flags), m_pVoteResults(NULL)
1.296 +{
1.297 + /* :TODO: We can probably cache the handle ahead of time */
1.298 +}
1.299 +
1.300 +void CMenuHandler::OnMenuStart(IBaseMenu *menu)
1.301 +{
1.302 + if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
1.303 + {
1.304 + DoAction(menu, MenuAction_Start, 0, 0);
1.305 + }
1.306 +}
1.307 +
1.308 +void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
1.309 +{
1.310 + if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
1.311 + {
1.312 + HandleSecurity sec;
1.313 + sec.pIdentity = g_pCoreIdent;
1.314 + sec.pOwner = m_pBasic->GetParentContext()->GetIdentity();
1.315 +
1.316 + HandleAccess access;
1.317 + g_HandleSys.InitAccessDefaults(NULL, &access);
1.318 + access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
1.319 +
1.320 + Handle_t hndl = g_HandleSys.CreateHandleEx(g_MenuHelpers.GetTempPanelType(), panel, &sec, &access, NULL);
1.321 +
1.322 + DoAction(menu, MenuAction_Display, client, hndl);
1.323 +
1.324 + g_HandleSys.FreeHandle(hndl, &sec);
1.325 + }
1.326 +}
1.327 +
1.328 +void CMenuHandler::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
1.329 +{
1.330 + /* Save old position first. */
1.331 + unsigned int first_item = item_on_page;
1.332 + unsigned int *old_pos = s_CurSelectPosition;
1.333 +
1.334 + s_CurSelectPosition = &first_item;
1.335 +
1.336 + unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
1.337 + DoAction(menu, MenuAction_Select, client, item);
1.338 + g_ChatTriggers.SetReplyTo(old_reply);
1.339 +
1.340 + s_CurSelectPosition = old_pos;
1.341 +}
1.342 +
1.343 +void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
1.344 +{
1.345 + unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
1.346 + DoAction(menu, MenuAction_Cancel, client, (cell_t)reason);
1.347 + g_ChatTriggers.SetReplyTo(old_reply);
1.348 +}
1.349 +
1.350 +void CMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
1.351 +{
1.352 + DoAction(menu, MenuAction_End, reason, 0);
1.353 +}
1.354 +
1.355 +void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
1.356 +{
1.357 + g_MenuHelpers.FreeMenuHandler(this);
1.358 +}
1.359 +
1.360 +void CMenuHandler::OnMenuVoteStart(IBaseMenu *menu)
1.361 +{
1.362 + DoAction(menu, MenuAction_VoteStart, 0, 0);
1.363 +}
1.364 +
1.365 +void CMenuHandler::OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason)
1.366 +{
1.367 + DoAction(menu, MenuAction_VoteCancel, reason, 0);
1.368 +}
1.369 +
1.370 +void CMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
1.371 +{
1.372 + if ((m_Flags & (int)MenuAction_DrawItem) == (int)MenuAction_DrawItem)
1.373 + {
1.374 + cell_t result = DoAction(menu, MenuAction_DrawItem, client, item, style);
1.375 + style = (unsigned int)result;
1.376 + }
1.377 +}
1.378 +
1.379 +unsigned int CMenuHandler::OnMenuDisplayItem(IBaseMenu *menu,
1.380 + int client,
1.381 + IMenuPanel *panel,
1.382 + unsigned int item,
1.383 + const ItemDrawInfo &dr)
1.384 +{
1.385 + if ((m_Flags & (int)MenuAction_DisplayItem) == (int)MenuAction_DisplayItem)
1.386 + {
1.387 + IMenuPanel *oldpanel = s_pCurPanel;
1.388 + unsigned int oldret = s_CurPanelReturn;
1.389 + const ItemDrawInfo *oldinfo = s_CurDrawInfo;
1.390 + s_pCurPanel = panel;
1.391 + s_CurPanelReturn = 0;
1.392 + s_CurDrawInfo = &dr;
1.393 +
1.394 + cell_t res = DoAction(menu, MenuAction_DisplayItem, client, item, 0);
1.395 +
1.396 + if (!res)
1.397 + {
1.398 + res = s_CurPanelReturn;
1.399 + }
1.400 +
1.401 + s_pCurPanel = oldpanel;
1.402 + s_CurPanelReturn = oldret;
1.403 + s_CurDrawInfo = oldinfo;
1.404 +
1.405 + return res;
1.406 + }
1.407 +
1.408 + return 0;
1.409 +}
1.410 +
1.411 +cell_t CMenuHandler::DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res)
1.412 +{
1.413 +#if defined MENU_DEBUG
1.414 + g_Logger.LogMessage("[SM_MENU] CMenuHandler::DoAction() (menu %p/%08x) (action %d) (param1 %d) (param2 %d)",
1.415 + menu,
1.416 + menu->GetHandle(),
1.417 + action,
1.418 + param1,
1.419 + param2);
1.420 +#endif
1.421 + cell_t res = def_res;
1.422 + m_pBasic->PushCell(menu->GetHandle());
1.423 + m_pBasic->PushCell((cell_t)action);
1.424 + m_pBasic->PushCell(param1);
1.425 + m_pBasic->PushCell(param2);
1.426 + m_pBasic->Execute(&res);
1.427 + return res;
1.428 +}
1.429 +
1.430 +void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
1.431 +{
1.432 + if (!m_pVoteResults)
1.433 + {
1.434 + /* Call MenuAction_VoteEnd instead. See if there are any extra winners. */
1.435 + unsigned int num_items = 1;
1.436 + for (unsigned int i=1; i<results->num_items; i++)
1.437 + {
1.438 + if (results->item_list[i].count != results->item_list[0].count)
1.439 + {
1.440 + break;
1.441 + }
1.442 + num_items++;
1.443 + }
1.444 +
1.445 + /* See if we need to pick a random winner. */
1.446 + unsigned int winning_item;
1.447 + if (num_items > 1)
1.448 + {
1.449 + /* Yes, we do. */
1.450 + srand(time(NULL));
1.451 + winning_item = rand() % num_items;
1.452 + winning_item = results->item_list[winning_item].item;
1.453 + } else {
1.454 + /* No, take the first. */
1.455 + winning_item = results->item_list[0].item;
1.456 + }
1.457 +
1.458 + unsigned int total_votes = results->num_votes;
1.459 + unsigned int winning_votes = results->item_list[0].count;
1.460 +
1.461 + DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
1.462 + } else {
1.463 + IPluginContext *pContext = m_pVoteResults->GetParentContext();
1.464 + bool no_call = false;
1.465 + int err;
1.466 +
1.467 + /* First array */
1.468 + cell_t client_array_address = -1;
1.469 + cell_t *client_array_base = NULL;
1.470 + cell_t client_array_size = results->num_clients + (results->num_clients * 2);
1.471 + if (client_array_size)
1.472 + {
1.473 + if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
1.474 + != SP_ERROR_NONE)
1.475 + {
1.476 + g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
1.477 + no_call = true;
1.478 + } else {
1.479 + cell_t target_offs = sizeof(cell_t) * results->num_clients;
1.480 + cell_t *cur_index = client_array_base;
1.481 + cell_t *cur_array;
1.482 + for (unsigned int i=0; i<results->num_clients; i++)
1.483 + {
1.484 + /* Copy the array index */
1.485 + *cur_index = target_offs;
1.486 + /* Get the current array address */
1.487 + cur_array = (cell_t *)((char *)cur_index + target_offs);
1.488 + /* Store information */
1.489 + cur_array[0] = results->client_list[i].client;
1.490 + cur_array[1] = results->client_list[i].item;
1.491 + /* Adjust for the new target by subtracting one indirection
1.492 + * and adding one array.
1.493 + */
1.494 + target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
1.495 + cur_index++;
1.496 + }
1.497 + }
1.498 + }
1.499 +
1.500 + /* Second array */
1.501 + cell_t item_array_address = -1;
1.502 + cell_t *item_array_base = NULL;
1.503 + cell_t item_array_size = results->num_items + (results->num_items * 2);
1.504 + if (item_array_size)
1.505 + {
1.506 + if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
1.507 + != SP_ERROR_NONE)
1.508 + {
1.509 + g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
1.510 + no_call = true;
1.511 + } else {
1.512 + cell_t target_offs = sizeof(cell_t) * results->num_items;
1.513 + cell_t *cur_index = item_array_base;
1.514 + cell_t *cur_array;
1.515 + for (unsigned int i=0; i<results->num_items; i++)
1.516 + {
1.517 + /* Copy the array index */
1.518 + *cur_index = target_offs;
1.519 + /* Get the current array address */
1.520 + cur_array = (cell_t *)((char *)cur_index + target_offs);
1.521 + /* Store information */
1.522 + cur_array[0] = results->item_list[i].item;
1.523 + cur_array[1] = results->item_list[i].count;
1.524 + /* Adjust for the new target by subtracting one indirection
1.525 + * and adding one array.
1.526 + */
1.527 + target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
1.528 + cur_index++;
1.529 + }
1.530 + }
1.531 + }
1.532 +
1.533 + /* Finally, push everything */
1.534 + if (!no_call)
1.535 + {
1.536 + m_pVoteResults->PushCell(menu->GetHandle());
1.537 + m_pVoteResults->PushCell(results->num_votes);
1.538 + m_pVoteResults->PushCell(results->num_clients);
1.539 + m_pVoteResults->PushCell(client_array_address);
1.540 + m_pVoteResults->PushCell(results->num_items);
1.541 + m_pVoteResults->PushCell(item_array_address);
1.542 + m_pVoteResults->Execute(NULL);
1.543 + }
1.544 +
1.545 + /* Free what we allocated, in reverse order as required */
1.546 + if (item_array_address != -1)
1.547 + {
1.548 + pContext->HeapPop(item_array_address);
1.549 + }
1.550 + if (client_array_address != -1)
1.551 + {
1.552 + pContext->HeapPop(client_array_address);
1.553 + }
1.554 + }
1.555 +}
1.556 +
1.557 +bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
1.558 +{
1.559 + if (strcmp(option, "set_vote_results_handler") == 0)
1.560 + {
1.561 + void **array = (void **)data;
1.562 + m_pVoteResults = (IPluginFunction *)array[0];
1.563 + m_fnVoteResult = *(cell_t *)((cell_t *)array[1]);
1.564 + return true;
1.565 + }
1.566 +
1.567 + return false;
1.568 +}
1.569 +
1.570 +/**
1.571 + * INLINE FUNCTIONS FOR NATIVES
1.572 + */
1.573 +
1.574 +inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
1.575 +{
1.576 + return g_HandleSys.CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
1.577 +}
1.578 +
1.579 +inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
1.580 +{
1.581 + HandleSecurity sec;
1.582 + sec.pIdentity = g_pCoreIdent;
1.583 + sec.pOwner = NULL;
1.584 + return g_HandleSys.ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
1.585 +}
1.586 +
1.587 +inline IMenuStyle *GetStyleFromCell(cell_t cell)
1.588 +{
1.589 + enum MenuStyle
1.590 + {
1.591 + MenuStyle_Default = 0, /**< The "default" menu style for the mod */
1.592 + MenuStyle_Valve = 1, /**< The Valve provided menu style (Used on HL2DM) */
1.593 + MenuStyle_Radio = 2, /**< The simpler menu style commonly used on CS:S */
1.594 + };
1.595 +
1.596 + if (cell == MenuStyle_Valve)
1.597 + {
1.598 + return &g_ValveMenuStyle;
1.599 + } else if (cell == MenuStyle_Radio
1.600 + && g_RadioMenuStyle.IsSupported())
1.601 + {
1.602 + return &g_RadioMenuStyle;
1.603 + }
1.604 +
1.605 + return g_Menus.GetDefaultStyle();
1.606 +}
1.607 +
1.608 +/***********************************
1.609 + **** NATIVE DEFINITIONS ***********
1.610 + ***********************************/
1.611 +
1.612 +static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
1.613 +{
1.614 + IMenuStyle *style = g_Menus.GetDefaultStyle();
1.615 + IPluginFunction *pFunction;
1.616 +
1.617 + if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
1.618 + {
1.619 + return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
1.620 + }
1.621 +
1.622 + CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
1.623 + IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
1.624 +
1.625 + Handle_t hndl = menu->GetHandle();
1.626 + if (!hndl)
1.627 + {
1.628 + menu->Destroy();
1.629 + return BAD_HANDLE;
1.630 + }
1.631 +
1.632 + return hndl;
1.633 +}
1.634 +
1.635 +static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
1.636 +{
1.637 + Handle_t hndl = (Handle_t)params[1];
1.638 + HandleError err;
1.639 + IBaseMenu *menu;
1.640 +
1.641 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.642 + {
1.643 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.644 + }
1.645 +
1.646 + return menu->Display(params[2], params[3]) ? 1 : 0;
1.647 +}
1.648 +
1.649 +static cell_t DisplayMenuAtItem(IPluginContext *pContext, const cell_t *params)
1.650 +{
1.651 + Handle_t hndl = (Handle_t)params[1];
1.652 + HandleError err;
1.653 + IBaseMenu *menu;
1.654 +
1.655 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.656 + {
1.657 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.658 + }
1.659 +
1.660 + return menu->DisplayAtItem(params[2], params[4], params[3]) ? 1 : 0;
1.661 +}
1.662 +
1.663 +static cell_t VoteMenu(IPluginContext *pContext, const cell_t *params)
1.664 +{
1.665 + if (g_Menus.IsVoteInProgress())
1.666 + {
1.667 + return pContext->ThrowNativeError("A vote is already in progress");
1.668 + }
1.669 +
1.670 + Handle_t hndl = (Handle_t)params[1];
1.671 + HandleError err;
1.672 + IBaseMenu *menu;
1.673 +
1.674 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.675 + {
1.676 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.677 + }
1.678 +
1.679 + cell_t *addr;
1.680 + pContext->LocalToPhysAddr(params[2], &addr);
1.681 +
1.682 + if (!g_Menus.StartVote(menu, params[3], addr, params[4]))
1.683 + {
1.684 + return 0;
1.685 + }
1.686 +
1.687 + return 1;
1.688 +}
1.689 +
1.690 +static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
1.691 +{
1.692 + Handle_t hndl = (Handle_t)params[1];
1.693 + HandleError err;
1.694 + IBaseMenu *menu;
1.695 +
1.696 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.697 + {
1.698 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.699 + }
1.700 +
1.701 + char *info;
1.702 + ItemDrawInfo dr;
1.703 +
1.704 + pContext->LocalToString(params[2], &info);
1.705 + pContext->LocalToString(params[3], (char **)&dr.display);
1.706 + dr.style = params[4];
1.707 +
1.708 + return menu->AppendItem(info, dr) ? 1 : 0;
1.709 +}
1.710 +
1.711 +static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
1.712 +{
1.713 + Handle_t hndl = (Handle_t)params[1];
1.714 + HandleError err;
1.715 + IBaseMenu *menu;
1.716 +
1.717 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.718 + {
1.719 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.720 + }
1.721 +
1.722 + char *info;
1.723 + ItemDrawInfo dr;
1.724 +
1.725 + pContext->LocalToString(params[3], &info);
1.726 + pContext->LocalToString(params[4], (char **)&dr.display);
1.727 + dr.style = params[5];
1.728 +
1.729 + return menu->InsertItem(params[2], info, dr) ? 1 : 0;
1.730 +}
1.731 +
1.732 +static cell_t RemoveMenuItem(IPluginContext *pContext, const cell_t *params)
1.733 +{
1.734 + Handle_t hndl = (Handle_t)params[1];
1.735 + HandleError err;
1.736 + IBaseMenu *menu;
1.737 +
1.738 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.739 + {
1.740 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.741 + }
1.742 +
1.743 + return menu->RemoveItem(params[2]) ? 1 : 0;
1.744 +}
1.745 +
1.746 +static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
1.747 +{
1.748 + Handle_t hndl = (Handle_t)params[1];
1.749 + HandleError err;
1.750 + IBaseMenu *menu;
1.751 +
1.752 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.753 + {
1.754 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.755 + }
1.756 +
1.757 + menu->RemoveAllItems();
1.758 +
1.759 + return 1;
1.760 +}
1.761 +
1.762 +static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
1.763 +{
1.764 + Handle_t hndl = (Handle_t)params[1];
1.765 + HandleError err;
1.766 + IBaseMenu *menu;
1.767 +
1.768 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.769 + {
1.770 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.771 + }
1.772 +
1.773 + ItemDrawInfo dr;
1.774 + const char *info;
1.775 +
1.776 + if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
1.777 + {
1.778 + return 0;
1.779 + }
1.780 +
1.781 + pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
1.782 + pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
1.783 +
1.784 + cell_t *addr;
1.785 + pContext->LocalToPhysAddr(params[5], &addr);
1.786 + *addr = dr.style;
1.787 +
1.788 + return 1;
1.789 +}
1.790 +
1.791 +static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
1.792 +{
1.793 + Handle_t hndl = (Handle_t)params[1];
1.794 + HandleError err;
1.795 + IBaseMenu *menu;
1.796 +
1.797 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.798 + {
1.799 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.800 + }
1.801 +
1.802 + return menu->SetPagination(params[2]) ? 1 : 0;
1.803 +}
1.804 +
1.805 +static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
1.806 +{
1.807 + Handle_t hndl = (Handle_t)params[1];
1.808 + HandleError err;
1.809 + IBaseMenu *menu;
1.810 +
1.811 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.812 + {
1.813 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.814 + }
1.815 +
1.816 + return menu->GetPagination();
1.817 +}
1.818 +
1.819 +static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
1.820 +{
1.821 + Handle_t hndl = (Handle_t)params[1];
1.822 + HandleError err;
1.823 + IBaseMenu *menu;
1.824 +
1.825 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.826 + {
1.827 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.828 + }
1.829 +
1.830 + return menu->GetItemCount();
1.831 +}
1.832 +
1.833 +static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
1.834 +{
1.835 + Handle_t hndl = (Handle_t)params[1];
1.836 + HandleError err;
1.837 + IBaseMenu *menu;
1.838 +
1.839 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.840 + {
1.841 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.842 + }
1.843 +
1.844 + g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
1.845 +
1.846 + char buffer[1024];
1.847 + g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
1.848 +
1.849 + menu->SetDefaultTitle(buffer);
1.850 +
1.851 + return 1;
1.852 +}
1.853 +
1.854 +static cell_t GetMenuTitle(IPluginContext *pContext, const cell_t *params)
1.855 +{
1.856 + Handle_t hndl = (Handle_t)params[1];
1.857 + HandleError err;
1.858 + IBaseMenu *menu;
1.859 +
1.860 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.861 + {
1.862 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.863 + }
1.864 +
1.865 + size_t written;
1.866 + const char *title = menu->GetDefaultTitle();
1.867 + pContext->StringToLocalUTF8(params[2], params[3], title, &written);
1.868 +
1.869 + return (cell_t)written;
1.870 +}
1.871 +
1.872 +
1.873 +static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
1.874 +{
1.875 + Handle_t hndl = (Handle_t)params[1];
1.876 + HandleError err;
1.877 + IBaseMenu *menu;
1.878 +
1.879 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.880 + {
1.881 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.882 + }
1.883 +
1.884 + IMenuPanel *panel = menu->CreatePanel();
1.885 + hndl = MakePanelHandle(panel, pContext);
1.886 + if (!hndl)
1.887 + {
1.888 + panel->DeleteThis();
1.889 + }
1.890 +
1.891 + return hndl;
1.892 +}
1.893 +
1.894 +static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
1.895 +{
1.896 + Handle_t hndl = (Handle_t)params[1];
1.897 + HandleError err;
1.898 + IBaseMenu *menu;
1.899 +
1.900 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.901 + {
1.902 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.903 + }
1.904 +
1.905 + return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
1.906 +}
1.907 +
1.908 +static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
1.909 +{
1.910 + Handle_t hndl = (Handle_t)params[1];
1.911 + HandleError err;
1.912 + IBaseMenu *menu;
1.913 +
1.914 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.915 + {
1.916 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.917 + }
1.918 +
1.919 + return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
1.920 +}
1.921 +
1.922 +static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
1.923 +{
1.924 + Handle_t hndl = (Handle_t)params[1];
1.925 + HandleError err;
1.926 + IBaseMenu *menu;
1.927 +
1.928 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.929 + {
1.930 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.931 + }
1.932 +
1.933 + unsigned int flags = menu->GetMenuOptionFlags();
1.934 +
1.935 + if (params[2])
1.936 + {
1.937 + flags |= MENUFLAG_BUTTON_EXIT;
1.938 + } else {
1.939 + flags &= ~MENUFLAG_BUTTON_EXIT;
1.940 + }
1.941 +
1.942 + menu->SetMenuOptionFlags(flags);
1.943 + unsigned int new_flags = menu->GetMenuOptionFlags();
1.944 +
1.945 + return (flags == new_flags);
1.946 +}
1.947 +
1.948 +static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
1.949 +{
1.950 + Handle_t hndl = (Handle_t)params[1];
1.951 + HandleError err;
1.952 + IBaseMenu *menu;
1.953 +
1.954 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.955 + {
1.956 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.957 + }
1.958 +
1.959 + unsigned int flags = menu->GetMenuOptionFlags();
1.960 +
1.961 + if (params[2])
1.962 + {
1.963 + flags |= MENUFLAG_BUTTON_EXITBACK;
1.964 + } else {
1.965 + flags &= ~MENUFLAG_BUTTON_EXITBACK;
1.966 + }
1.967 +
1.968 + menu->SetMenuOptionFlags(flags);
1.969 +
1.970 + return 1;
1.971 +}
1.972 +
1.973 +static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
1.974 +{
1.975 + Handle_t hndl = (Handle_t)params[1];
1.976 + HandleError err;
1.977 + IBaseMenu *menu;
1.978 +
1.979 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.980 + {
1.981 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.982 + }
1.983 +
1.984 + g_Menus.CancelMenu(menu);
1.985 +
1.986 + return 1;
1.987 +}
1.988 +
1.989 +static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
1.990 +{
1.991 + return g_Menus.IsVoteInProgress() ? 1 : 0;
1.992 +}
1.993 +
1.994 +static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
1.995 +{
1.996 + Handle_t hndl = (Handle_t)params[1];
1.997 + HandleError err;
1.998 + IBaseMenu *menu;
1.999 +
1.1000 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.1001 + {
1.1002 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1003 + }
1.1004 +
1.1005 + return menu->GetDrawStyle()->GetHandle();
1.1006 +}
1.1007 +
1.1008 +static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
1.1009 +{
1.1010 + IMenuStyle *style = GetStyleFromCell(params[1]);
1.1011 + if (!style)
1.1012 + {
1.1013 + return BAD_HANDLE;
1.1014 + }
1.1015 +
1.1016 + return style->GetHandle();
1.1017 +}
1.1018 +
1.1019 +static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
1.1020 +{
1.1021 + Handle_t hndl = (Handle_t)params[1];
1.1022 + HandleError err;
1.1023 + IMenuStyle *style;
1.1024 +
1.1025 + if (hndl != 0)
1.1026 + {
1.1027 + if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1.1028 + {
1.1029 + return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1.1030 + }
1.1031 + } else {
1.1032 + style = g_Menus.GetDefaultStyle();
1.1033 + }
1.1034 +
1.1035 + IMenuPanel *panel = style->CreatePanel();
1.1036 +
1.1037 + hndl = MakePanelHandle(panel, pContext);
1.1038 + if (!hndl)
1.1039 + {
1.1040 + panel->DeleteThis();
1.1041 + return BAD_HANDLE;
1.1042 + }
1.1043 +
1.1044 + return hndl;
1.1045 +}
1.1046 +
1.1047 +static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
1.1048 +{
1.1049 + Handle_t hndl = (Handle_t)params[1];
1.1050 + HandleError err;
1.1051 + IMenuStyle *style;
1.1052 +
1.1053 + if (hndl != 0)
1.1054 + {
1.1055 + if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1.1056 + {
1.1057 + return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1.1058 + }
1.1059 + } else {
1.1060 + style = g_Menus.GetDefaultStyle();
1.1061 + }
1.1062 +
1.1063 + IPluginFunction *pFunction;
1.1064 + if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
1.1065 + {
1.1066 + return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
1.1067 + }
1.1068 +
1.1069 + CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
1.1070 +
1.1071 + IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
1.1072 + hndl = pMenu->GetHandle();
1.1073 + if (!hndl)
1.1074 + {
1.1075 + pMenu->Destroy();
1.1076 + return BAD_HANDLE;
1.1077 + }
1.1078 +
1.1079 + return hndl;
1.1080 +}
1.1081 +
1.1082 +static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
1.1083 +{
1.1084 + Handle_t hndl = (Handle_t)params[2];
1.1085 + HandleError err;
1.1086 + IMenuStyle *style;
1.1087 +
1.1088 + if (hndl != 0)
1.1089 + {
1.1090 + if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1.1091 + {
1.1092 + return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1.1093 + }
1.1094 + } else {
1.1095 + style = g_Menus.GetDefaultStyle();
1.1096 + }
1.1097 +
1.1098 + return style->GetClientMenu(params[1], NULL);
1.1099 +}
1.1100 +
1.1101 +static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
1.1102 +{
1.1103 + Handle_t hndl = (Handle_t)params[3];
1.1104 + HandleError err;
1.1105 + IMenuStyle *style;
1.1106 +
1.1107 + if (hndl != 0)
1.1108 + {
1.1109 + if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1.1110 + {
1.1111 + return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1.1112 + }
1.1113 + } else {
1.1114 + style = g_Menus.GetDefaultStyle();
1.1115 + }
1.1116 +
1.1117 + return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
1.1118 +}
1.1119 +
1.1120 +static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
1.1121 +{
1.1122 + Handle_t hndl = (Handle_t)params[1];
1.1123 + HandleError err;
1.1124 + IMenuStyle *style;
1.1125 +
1.1126 + if (hndl != 0)
1.1127 + {
1.1128 + if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1.1129 + {
1.1130 + return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1.1131 + }
1.1132 + } else {
1.1133 + style = g_Menus.GetDefaultStyle();
1.1134 + }
1.1135 +
1.1136 + return style->GetMaxPageItems();
1.1137 +}
1.1138 +
1.1139 +static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
1.1140 +{
1.1141 + Handle_t hndl = (Handle_t)params[1];
1.1142 + HandleError err;
1.1143 + IMenuPanel *panel;
1.1144 +
1.1145 + if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1.1146 + {
1.1147 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1148 + }
1.1149 +
1.1150 + return panel->GetParentStyle()->GetHandle();
1.1151 +}
1.1152 +
1.1153 +static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
1.1154 +{
1.1155 + Handle_t hndl = (Handle_t)params[1];
1.1156 + HandleError err;
1.1157 + IMenuPanel *panel;
1.1158 +
1.1159 + if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1.1160 + {
1.1161 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1162 + }
1.1163 +
1.1164 + char *text;
1.1165 + pContext->LocalToString(params[2], &text);
1.1166 +
1.1167 + panel->DrawTitle(text, params[3] ? true : false);
1.1168 +
1.1169 + return 1;
1.1170 +}
1.1171 +
1.1172 +static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
1.1173 +{
1.1174 + Handle_t hndl = (Handle_t)params[1];
1.1175 + HandleError err;
1.1176 + IMenuPanel *panel;
1.1177 +
1.1178 + if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1.1179 + {
1.1180 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1181 + }
1.1182 +
1.1183 + ItemDrawInfo dr;
1.1184 + pContext->LocalToString(params[2], (char **)&dr.display);
1.1185 + dr.style = params[3];
1.1186 +
1.1187 + return panel->DrawItem(dr);
1.1188 +}
1.1189 +
1.1190 +static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
1.1191 +{
1.1192 + Handle_t hndl = (Handle_t)params[1];
1.1193 + HandleError err;
1.1194 + IMenuPanel *panel;
1.1195 +
1.1196 + if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1.1197 + {
1.1198 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1199 + }
1.1200 +
1.1201 + char *text;
1.1202 + pContext->LocalToString(params[2], &text);
1.1203 +
1.1204 + return panel->DrawRawLine(text) ? 1 : 0;
1.1205 +}
1.1206 +
1.1207 +static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
1.1208 +{
1.1209 + Handle_t hndl = (Handle_t)params[1];
1.1210 + HandleError err;
1.1211 + IMenuPanel *panel;
1.1212 +
1.1213 + if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1.1214 + {
1.1215 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1216 + }
1.1217 +
1.1218 + return panel->CanDrawItem(params[2]);
1.1219 +}
1.1220 +
1.1221 +static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
1.1222 +{
1.1223 + Handle_t hndl = (Handle_t)params[1];
1.1224 + HandleError err;
1.1225 + IMenuPanel *panel;
1.1226 +
1.1227 + if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1.1228 + {
1.1229 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1230 + }
1.1231 +
1.1232 + IPluginFunction *pFunction;
1.1233 + if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
1.1234 + {
1.1235 + return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
1.1236 + }
1.1237 +
1.1238 + CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
1.1239 + if (!panel->SendDisplay(params[2], handler, params[4]))
1.1240 + {
1.1241 + g_MenuHelpers.FreePanelHandler(handler);
1.1242 + }
1.1243 +
1.1244 + return 1;
1.1245 +}
1.1246 +
1.1247 +static cell_t SetPanelKeys(IPluginContext *pContext, const cell_t *params)
1.1248 +{
1.1249 + Handle_t hndl = (Handle_t)params[1];
1.1250 + HandleError err;
1.1251 + IMenuPanel *panel;
1.1252 +
1.1253 + if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1.1254 + {
1.1255 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1256 + }
1.1257 +
1.1258 + return panel->SetSelectableKeys(params[2]);
1.1259 +}
1.1260 +
1.1261 +static cell_t GetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
1.1262 +{
1.1263 + Handle_t hndl = (Handle_t)params[1];
1.1264 + HandleError err;
1.1265 + IMenuPanel *panel;
1.1266 +
1.1267 + if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1.1268 + {
1.1269 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1270 + }
1.1271 +
1.1272 + return panel->GetCurrentKey();
1.1273 +}
1.1274 +
1.1275 +static cell_t GetPanelTextRemaining(IPluginContext *pContext, const cell_t *params)
1.1276 +{
1.1277 + Handle_t hndl = (Handle_t)params[1];
1.1278 + HandleError err;
1.1279 + IMenuPanel *panel;
1.1280 +
1.1281 + if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1.1282 + {
1.1283 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1284 + }
1.1285 +
1.1286 + return panel->GetAmountRemaining();
1.1287 +}
1.1288 +
1.1289 +static cell_t SetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
1.1290 +{
1.1291 + Handle_t hndl = (Handle_t)params[1];
1.1292 + HandleError err;
1.1293 + IMenuPanel *panel;
1.1294 +
1.1295 + if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1.1296 + {
1.1297 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1298 + }
1.1299 +
1.1300 + return panel->SetCurrentKey(params[2]) ? 1 : 0;
1.1301 +}
1.1302 +
1.1303 +static cell_t RedrawMenuItem(IPluginContext *pContext, const cell_t *params)
1.1304 +{
1.1305 + if (!s_pCurPanel)
1.1306 + {
1.1307 + return pContext->ThrowNativeError("You can only call this once from a MenuAction_DisplayItem callback");
1.1308 + }
1.1309 +
1.1310 + char *str;
1.1311 + pContext->LocalToString(params[1], &str);
1.1312 +
1.1313 + ItemDrawInfo dr = *s_CurDrawInfo;
1.1314 + dr.display = str;
1.1315 +
1.1316 + if ((s_CurPanelReturn = s_pCurPanel->DrawItem(dr)) != 0)
1.1317 + {
1.1318 + s_pCurPanel = NULL;
1.1319 + }
1.1320 +
1.1321 + return s_CurPanelReturn;
1.1322 +}
1.1323 +
1.1324 +static cell_t GetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
1.1325 +{
1.1326 + Handle_t hndl = (Handle_t)params[1];
1.1327 + HandleError err;
1.1328 + IBaseMenu *menu;
1.1329 +
1.1330 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.1331 + {
1.1332 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1333 + }
1.1334 +
1.1335 + return menu->GetMenuOptionFlags();
1.1336 +}
1.1337 +
1.1338 +static cell_t SetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
1.1339 +{
1.1340 + Handle_t hndl = (Handle_t)params[1];
1.1341 + HandleError err;
1.1342 + IBaseMenu *menu;
1.1343 +
1.1344 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.1345 + {
1.1346 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1347 + }
1.1348 +
1.1349 + menu->SetMenuOptionFlags(params[2]);
1.1350 +
1.1351 + return 1;
1.1352 +}
1.1353 +
1.1354 +static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
1.1355 +{
1.1356 + if (!g_Menus.IsVoteInProgress())
1.1357 + {
1.1358 + return pContxt->ThrowNativeError("No vote is in progress");
1.1359 + }
1.1360 +
1.1361 + g_Menus.CancelVoting();
1.1362 +
1.1363 + return 1;
1.1364 +}
1.1365 +
1.1366 +static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
1.1367 +{
1.1368 + Handle_t hndl = (Handle_t)params[1];
1.1369 + HandleError err;
1.1370 + IBaseMenu *menu;
1.1371 +
1.1372 + if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1.1373 + {
1.1374 + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1.1375 + }
1.1376 +
1.1377 + IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
1.1378 + if (!pFunction)
1.1379 + {
1.1380 + return pContext->ThrowNativeError("Invalid function %x", params[2]);
1.1381 + }
1.1382 +
1.1383 + void *array[2];
1.1384 + array[0] = pFunction;
1.1385 + array[1] = (void *)¶ms[2];
1.1386 +
1.1387 + IMenuHandler *pHandler = menu->GetHandler();
1.1388 + if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
1.1389 + {
1.1390 + return pContext->ThrowNativeError("The given menu does not support this option");
1.1391 + }
1.1392 +
1.1393 + return 1;
1.1394 +}
1.1395 +
1.1396 +static cell_t CheckVoteDelay(IPluginContext *pContext, const cell_t *params)
1.1397 +{
1.1398 + return g_Menus.GetRemainingVoteDelay();
1.1399 +}
1.1400 +
1.1401 +static cell_t GetMenuSelectionPosition(IPluginContext *pContext, const cell_t *params)
1.1402 +{
1.1403 + if (!s_CurSelectPosition)
1.1404 + {
1.1405 + return pContext->ThrowNativeError("Can only be called from inside a MenuAction_Select callback");
1.1406 + }
1.1407 +
1.1408 + return *s_CurSelectPosition;
1.1409 +}
1.1410 +
1.1411 +static cell_t IsClientInVotePool(IPluginContext *pContext, const cell_t *params)
1.1412 +{
1.1413 + int client;
1.1414 + IGamePlayer *pPlayer;
1.1415 +
1.1416 + client = params[1];
1.1417 + if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
1.1418 + {
1.1419 + return pContext->ThrowNativeError("Invalid client index %d", client);
1.1420 + }
1.1421 +
1.1422 + if (!g_Menus.IsVoteInProgress())
1.1423 + {
1.1424 + return pContext->ThrowNativeError("No vote is in progress");
1.1425 + }
1.1426 +
1.1427 + return g_Menus.IsClientInVotePool(client) ? 1 : 0;
1.1428 +}
1.1429 +
1.1430 +static cell_t RedrawClientVoteMenu(IPluginContext *pContext, const cell_t *params)
1.1431 +{
1.1432 + int client;
1.1433 + IGamePlayer *pPlayer;
1.1434 +
1.1435 + client = params[1];
1.1436 + if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
1.1437 + {
1.1438 + return pContext->ThrowNativeError("Invalid client index %d", client);
1.1439 + }
1.1440 +
1.1441 + if (!g_Menus.IsVoteInProgress())
1.1442 + {
1.1443 + return pContext->ThrowNativeError("No vote is in progress");
1.1444 + }
1.1445 +
1.1446 + if (!g_Menus.IsClientInVotePool(client))
1.1447 + {
1.1448 + return pContext->ThrowNativeError("Client is not in the voting pool");
1.1449 + }
1.1450 +
1.1451 + return g_Menus.RedrawClientVoteMenu(client) ? 1 : 0;
1.1452 +}
1.1453 +
1.1454 +class EmptyMenuHandler : public IMenuHandler
1.1455 +{
1.1456 +public:
1.1457 +} s_EmptyMenuHandler;
1.1458 +
1.1459 +static cell_t InternalShowMenu(IPluginContext *pContext, const cell_t *params)
1.1460 +{
1.1461 + int client = params[1];
1.1462 + CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
1.1463 +
1.1464 + if (pPlayer == NULL)
1.1465 + {
1.1466 + return pContext->ThrowNativeError("Invalid client index %d", client);
1.1467 + }
1.1468 + else if (!pPlayer->IsInGame())
1.1469 + {
1.1470 + return pContext->ThrowNativeError("Client %d is not in game", client);
1.1471 + }
1.1472 +
1.1473 + if (!g_RadioMenuStyle.IsSupported())
1.1474 + {
1.1475 + return pContext->ThrowNativeError("Radio menus are not supported on this mod");
1.1476 + }
1.1477 +
1.1478 + char *str;
1.1479 + pContext->LocalToString(params[2], &str);
1.1480 +
1.1481 + IMenuPanel *pPanel = g_RadioMenuStyle.MakeRadioDisplay(str, params[4]);
1.1482 +
1.1483 + if (pPanel == NULL)
1.1484 + {
1.1485 + return 0;
1.1486 + }
1.1487 +
1.1488 + IMenuHandler *pHandler;
1.1489 + CPanelHandler *pActualHandler = NULL;
1.1490 + if (params[5] != -1)
1.1491 + {
1.1492 + IPluginFunction *pFunction = pContext->GetFunctionById(params[5]);
1.1493 + if (pFunction == NULL)
1.1494 + {
1.1495 + return pContext->ThrowNativeError("Invalid function index %x", params[5]);
1.1496 + }
1.1497 + pActualHandler = g_MenuHelpers.GetPanelHandler(pFunction);
1.1498 + }
1.1499 +
1.1500 + if (pActualHandler == NULL)
1.1501 + {
1.1502 + pHandler = &s_EmptyMenuHandler;
1.1503 + }
1.1504 + else
1.1505 + {
1.1506 + pHandler = pActualHandler;
1.1507 + }
1.1508 +
1.1509 + bool bSuccess = pPanel->SendDisplay(client, pHandler, params[3]);
1.1510 +
1.1511 + pPanel->DeleteThis();
1.1512 +
1.1513 + if (!bSuccess && pActualHandler != NULL)
1.1514 + {
1.1515 + g_MenuHelpers.FreePanelHandler(pActualHandler);
1.1516 + }
1.1517 +
1.1518 + return bSuccess ? 1 : 0;
1.1519 +}
1.1520 +
1.1521 +REGISTER_NATIVES(menuNatives)
1.1522 +{
1.1523 + {"AddMenuItem", AddMenuItem},
1.1524 + {"CanPanelDrawFlags", CanPanelDrawFlags},
1.1525 + {"CancelClientMenu", CancelClientMenu},
1.1526 + {"CancelMenu", CancelMenu},
1.1527 + {"CancelVote", CancelVote},
1.1528 + {"CheckVoteDelay", CheckVoteDelay},
1.1529 + {"CreateMenu", CreateMenu},
1.1530 + {"CreateMenuEx", CreateMenuEx},
1.1531 + {"CreatePanel", CreatePanel},
1.1532 + {"CreatePanelFromMenu", CreatePanelFromMenu},
1.1533 + {"DisplayMenu", DisplayMenu},
1.1534 + {"DisplayMenuAtItem", DisplayMenuAtItem},
1.1535 + {"DrawPanelItem", DrawPanelItem},
1.1536 + {"DrawPanelText", DrawPanelText},
1.1537 + {"GetClientMenu", GetClientMenu},
1.1538 + {"GetMaxPageItems", GetMaxPageItems},
1.1539 + {"GetMenuExitBackButton", GetMenuExitBackButton},
1.1540 + {"GetMenuExitButton", GetMenuExitButton},
1.1541 + {"GetMenuItem", GetMenuItem},
1.1542 + {"GetMenuItemCount", GetMenuItemCount},
1.1543 + {"GetMenuOptionFlags", GetMenuOptionFlags},
1.1544 + {"GetMenuPagination", GetMenuPagination},
1.1545 + {"GetMenuSelectionPosition",GetMenuSelectionPosition},
1.1546 + {"GetMenuStyle", GetMenuStyle},
1.1547 + {"GetMenuStyleHandle", GetMenuStyleHandle},
1.1548 + {"GetMenuTitle", GetMenuTitle},
1.1549 + {"GetPanelTextRemaining", GetPanelTextRemaining},
1.1550 + {"GetPanelCurrentKey", GetPanelCurrentKey},
1.1551 + {"GetPanelStyle", GetPanelStyle},
1.1552 + {"InsertMenuItem", InsertMenuItem},
1.1553 + {"InternalShowMenu", InternalShowMenu},
1.1554 + {"IsClientInVotePool", IsClientInVotePool},
1.1555 + {"IsVoteInProgress", IsVoteInProgress},
1.1556 + {"RedrawClientVoteMenu", RedrawClientVoteMenu},
1.1557 + {"RedrawMenuItem", RedrawMenuItem},
1.1558 + {"RemoveAllMenuItems", RemoveAllMenuItems},
1.1559 + {"RemoveMenuItem", RemoveMenuItem},
1.1560 + {"SendPanelToClient", SendPanelToClient},
1.1561 + {"SetMenuExitBackButton", SetMenuExitBackButton},
1.1562 + {"SetMenuExitButton", SetMenuExitButton},
1.1563 + {"SetMenuOptionFlags", SetMenuOptionFlags},
1.1564 + {"SetMenuPagination", SetMenuPagination},
1.1565 + {"SetMenuTitle", SetMenuTitle},
1.1566 + {"SetPanelCurrentKey", SetPanelCurrentKey},
1.1567 + {"SetPanelTitle", SetPanelTitle},
1.1568 + {"SetPanelKeys", SetPanelKeys},
1.1569 + {"SetVoteResultCallback", SetVoteResultCallback},
1.1570 + {"VoteMenu", VoteMenu},
1.1571 + {NULL, NULL},
1.1572 +};