3 * =============================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
32 #include "sm_globals.h"
34 #include "DebugReporter.h"
35 #include "MenuManager.h"
36 #include "MenuStyle_Valve.h"
37 #include "MenuStyle_Radio.h"
38 #include "HandleSys.h"
39 #include "PluginSys.h"
40 #include "sm_stringutil.h"
41 #include "sourcemm_api.h"
42 #if defined MENU_DEBUG
46 #if defined CreateMenu
51 * And God said, "let there be menus," and behold, there were menus.
52 * God saw the menus and they were good. And the evening and the morning
58 MenuAction_Start = (1<<0), /**< A menu has been started (nothing passed) */
59 MenuAction_Display = (1<<1), /**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */
60 MenuAction_Select = (1<<2), /**< An item was selected (param1=client, param2=item) */
61 MenuAction_Cancel = (1<<3), /**< The menu was cancelled (param1=client, param2=item) */
62 MenuAction_End = (1<<4), /**< A menu's display/selection cycle is complete (nothing passed). */
63 MenuAction_VoteEnd = (1<<5), /**< (VOTE ONLY): A vote sequence has ended (param1=chosen item) */
64 MenuAction_VoteStart = (1<<6), /**< (VOTE ONLY): A vote sequence has started */
65 MenuAction_VoteCancel = (1<<7), /**< (VOTE ONLY): A vote sequence has been cancelled (nothing passed) */
66 MenuAction_DrawItem = (1<<8), /**< A style is being drawn; return the new style (param1=client, param2=item) */
67 MenuAction_DisplayItem = (1<<9), /**< the odd duck */
70 class CPanelHandler : public IMenuHandler
72 friend class MenuNativeHelpers;
77 void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
78 void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
80 IPluginFunction *m_pFunc;
84 class CMenuHandler : public IMenuHandler
86 friend class MenuNativeHelpers;
88 CMenuHandler(IPluginFunction *pBasic, int flags);
90 void OnMenuStart(IBaseMenu *menu);
91 void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
92 void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
93 void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
94 void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
95 void OnMenuDestroy(IBaseMenu *menu);
96 void OnMenuVoteStart(IBaseMenu *menu);
97 void OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results);
98 void OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason);
99 void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
100 unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
101 bool OnSetHandlerOption(const char *option, const void *data);
103 void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
104 void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
107 cell_t DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res=0);
109 IPluginFunction *m_pBasic;
111 IPluginFunction *m_pVoteResults;
112 cell_t m_fnVoteResult;
116 * GLOBAL CLASS FOR HELPERS
119 class MenuNativeHelpers :
120 public SMGlobalClass,
121 public IHandleTypeDispatch,
122 public IPluginsListener
125 virtual void OnSourceModAllInitialized()
127 m_PanelType = g_HandleSys.CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
128 m_TempPanelType = g_HandleSys.CreateType("TempIMenuPanel", this, m_PanelType, NULL, NULL, g_pCoreIdent, NULL);
129 g_PluginSys.AddPluginsListener(this);
132 virtual void OnSourceModShutdown()
134 g_HandleSys.RemoveType(m_TempPanelType, g_pCoreIdent);
135 g_HandleSys.RemoveType(m_PanelType, g_pCoreIdent);
137 while (!m_FreePanelHandlers.empty())
139 delete m_FreePanelHandlers.front();
140 m_FreePanelHandlers.pop();
143 while (!m_FreeMenuHandlers.empty())
145 delete m_FreeMenuHandlers.front();
146 m_FreeMenuHandlers.pop();
150 virtual void OnHandleDestroy(HandleType_t type, void *object)
152 if (type == m_TempPanelType)
157 IMenuPanel *panel = (IMenuPanel *)object;
162 * It is extremely important that unloaded plugins don't crash.
163 * Thus, if a plugin unloads, we run through every handler we have.
164 * This means we do almost no runtime work for keeping track of
165 * our panel handlers (we don't have to store a list of the running
166 * ones), but when push comes to shove, we have to scan them all
167 * in case any of them are active.
169 virtual void OnPluginUnloaded(IPlugin *plugin)
171 for (size_t i = 0; i < m_PanelHandlers.size(); i++)
173 if (m_PanelHandlers[i]->m_pPlugin == plugin)
175 m_PanelHandlers[i]->m_pPlugin = NULL;
176 m_PanelHandlers[i]->m_pFunc = NULL;
181 inline HandleType_t GetPanelType()
186 inline HandleType_t GetTempPanelType()
188 return m_TempPanelType;
191 CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
193 CPanelHandler *handler;
194 if (m_FreePanelHandlers.empty())
196 handler = new CPanelHandler;
197 m_PanelHandlers.push_back(handler);
199 handler = m_FreePanelHandlers.front();
200 m_FreePanelHandlers.pop();
202 handler->m_pFunc = pFunction;
203 handler->m_pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
207 void FreePanelHandler(CPanelHandler *handler)
209 handler->m_pFunc = NULL;
210 handler->m_pPlugin = NULL;
211 m_FreePanelHandlers.push(handler);
214 CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
216 CMenuHandler *handler;
217 if (m_FreeMenuHandlers.empty())
219 handler = new CMenuHandler(pFunction, flags);
221 handler = m_FreeMenuHandlers.front();
222 m_FreeMenuHandlers.pop();
223 handler->m_pBasic = pFunction;
224 handler->m_Flags = flags;
225 handler->m_pVoteResults = NULL;
230 void FreeMenuHandler(CMenuHandler *handler)
232 m_FreeMenuHandlers.push(handler);
236 HandleType_t m_PanelType;
237 HandleType_t m_TempPanelType;
238 CStack<CPanelHandler *> m_FreePanelHandlers;
239 CStack<CMenuHandler *> m_FreeMenuHandlers;
240 CVector<CPanelHandler *> m_PanelHandlers;
244 * BASIC PANEL HANDLER WRAPPER
247 void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
251 m_pFunc->PushCell(BAD_HANDLE);
252 m_pFunc->PushCell(MenuAction_Cancel);
253 m_pFunc->PushCell(client);
254 m_pFunc->PushCell(reason);
255 m_pFunc->Execute(NULL);
257 g_MenuHelpers.FreePanelHandler(this);
260 void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
264 m_pFunc->PushCell(BAD_HANDLE);
265 m_pFunc->PushCell(MenuAction_Select);
266 m_pFunc->PushCell(client);
267 m_pFunc->PushCell(item);
268 m_pFunc->Execute(NULL);
270 g_MenuHelpers.FreePanelHandler(this);
273 static IMenuPanel *s_pCurPanel = NULL;
274 static unsigned int s_CurPanelReturn = 0;
275 static const ItemDrawInfo *s_CurDrawInfo = NULL;
278 * MENU HANDLER WRAPPER
280 CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) :
281 m_pBasic(pBasic), m_Flags(flags), m_pVoteResults(NULL)
283 /* :TODO: We can probably cache the handle ahead of time */
286 void CMenuHandler::OnMenuStart(IBaseMenu *menu)
288 if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
290 DoAction(menu, MenuAction_Start, 0, 0);
294 void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
296 if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
299 sec.pIdentity = g_pCoreIdent;
300 sec.pOwner = m_pBasic->GetParentContext()->GetIdentity();
303 g_HandleSys.InitAccessDefaults(NULL, &access);
304 access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
306 Handle_t hndl = g_HandleSys.CreateHandleEx(g_MenuHelpers.GetTempPanelType(), panel, &sec, &access, NULL);
308 DoAction(menu, MenuAction_Display, client, hndl);
310 g_HandleSys.FreeHandle(hndl, &sec);
314 void CMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
316 DoAction(menu, MenuAction_Select, client, item);
319 void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
321 DoAction(menu, MenuAction_Cancel, client, (cell_t)reason);
324 void CMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
326 DoAction(menu, MenuAction_End, reason, 0);
329 void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
331 g_MenuHelpers.FreeMenuHandler(this);
334 void CMenuHandler::OnMenuVoteStart(IBaseMenu *menu)
336 DoAction(menu, MenuAction_VoteStart, 0, 0);
339 void CMenuHandler::OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason)
341 DoAction(menu, MenuAction_VoteCancel, reason, 0);
344 void CMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
346 if ((m_Flags & (int)MenuAction_DrawItem) == (int)MenuAction_DrawItem)
348 cell_t result = DoAction(menu, MenuAction_DrawItem, client, item, style);
349 style = (unsigned int)result;
353 unsigned int CMenuHandler::OnMenuDisplayItem(IBaseMenu *menu,
357 const ItemDrawInfo &dr)
359 if ((m_Flags & (int)MenuAction_DisplayItem) == (int)MenuAction_DisplayItem)
361 IMenuPanel *oldpanel = s_pCurPanel;
362 unsigned int oldret = s_CurPanelReturn;
363 const ItemDrawInfo *oldinfo = s_CurDrawInfo;
365 s_CurPanelReturn = 0;
368 cell_t res = DoAction(menu, MenuAction_DisplayItem, client, item, 0);
372 res = s_CurPanelReturn;
375 s_pCurPanel = oldpanel;
376 s_CurPanelReturn = oldret;
377 s_CurDrawInfo = oldinfo;
385 cell_t CMenuHandler::DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res)
387 #if defined MENU_DEBUG
388 g_Logger.LogMessage("[SM_MENU] CMenuHandler::DoAction() (menu %p/%08x) (action %d) (param1 %d) (param2 %d)",
395 cell_t res = def_res;
396 m_pBasic->PushCell(menu->GetHandle());
397 m_pBasic->PushCell((cell_t)action);
398 m_pBasic->PushCell(param1);
399 m_pBasic->PushCell(param2);
400 m_pBasic->Execute(&res);
404 void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
408 /* Call MenuAction_VoteEnd instead. See if there are any extra winners. */
409 unsigned int num_items = 1;
410 for (unsigned int i=1; i<results->num_items; i++)
412 if (results->item_list[i].count != results->item_list[0].count)
419 /* See if we need to pick a random winner. */
420 unsigned int winning_item;
425 winning_item = rand() % num_items;
426 winning_item = results->item_list[winning_item].item;
428 /* No, take the first. */
429 winning_item = results->item_list[0].item;
432 unsigned int total_votes = results->num_votes;
433 unsigned int winning_votes = results->item_list[0].count;
435 DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
437 IPluginContext *pContext = m_pVoteResults->GetParentContext();
438 bool no_call = false;
442 cell_t client_array_address = -1;
443 cell_t *client_array_base = NULL;
444 cell_t client_array_size = results->num_clients + (results->num_clients * 2);
445 if (client_array_size)
447 if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
450 g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
453 cell_t target_offs = sizeof(cell_t) * results->num_clients;
454 cell_t *cur_index = client_array_base;
456 for (unsigned int i=0; i<results->num_clients; i++)
458 /* Copy the array index */
459 *cur_index = target_offs;
460 /* Get the current array address */
461 cur_array = (cell_t *)((char *)cur_index + target_offs);
462 /* Store information */
463 cur_array[0] = results->client_list[i].client;
464 cur_array[1] = results->client_list[i].item;
465 /* Adjust for the new target by subtracting one indirection
466 * and adding one array.
468 target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
475 cell_t item_array_address = -1;
476 cell_t *item_array_base = NULL;
477 cell_t item_array_size = results->num_items + (results->num_items * 2);
480 if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
483 g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
486 cell_t target_offs = sizeof(cell_t) * results->num_items;
487 cell_t *cur_index = item_array_base;
489 for (unsigned int i=0; i<results->num_items; i++)
491 /* Copy the array index */
492 *cur_index = target_offs;
493 /* Get the current array address */
494 cur_array = (cell_t *)((char *)cur_index + target_offs);
495 /* Store information */
496 cur_array[0] = results->item_list[i].item;
497 cur_array[1] = results->item_list[i].count;
498 /* Adjust for the new target by subtracting one indirection
499 * and adding one array.
501 target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
507 /* Finally, push everything */
510 m_pVoteResults->PushCell(menu->GetHandle());
511 m_pVoteResults->PushCell(results->num_votes);
512 m_pVoteResults->PushCell(results->num_clients);
513 m_pVoteResults->PushCell(client_array_address);
514 m_pVoteResults->PushCell(results->num_items);
515 m_pVoteResults->PushCell(item_array_address);
516 m_pVoteResults->Execute(NULL);
519 /* Free what we allocated, in reverse order as required */
520 if (item_array_address != -1)
522 pContext->HeapPop(item_array_address);
524 if (client_array_address != -1)
526 pContext->HeapPop(client_array_address);
531 bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
533 if (strcmp(option, "set_vote_results_handler") == 0)
535 void **array = (void **)data;
536 m_pVoteResults = (IPluginFunction *)array[0];
537 m_fnVoteResult = *(cell_t *)((cell_t *)array[1]);
545 * INLINE FUNCTIONS FOR NATIVES
548 inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
550 return g_HandleSys.CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
553 inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
556 sec.pIdentity = g_pCoreIdent;
558 return g_HandleSys.ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
561 inline IMenuStyle *GetStyleFromCell(cell_t cell)
565 MenuStyle_Default = 0, /**< The "default" menu style for the mod */
566 MenuStyle_Valve = 1, /**< The Valve provided menu style (Used on HL2DM) */
567 MenuStyle_Radio = 2, /**< The simpler menu style commonly used on CS:S */
570 if (cell == MenuStyle_Valve)
572 return &g_ValveMenuStyle;
573 } else if (cell == MenuStyle_Radio
574 && g_RadioMenuStyle.IsSupported())
576 return &g_RadioMenuStyle;
579 return g_Menus.GetDefaultStyle();
582 /***********************************
583 **** NATIVE DEFINITIONS ***********
584 ***********************************/
586 static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
588 IMenuStyle *style = g_Menus.GetDefaultStyle();
589 IPluginFunction *pFunction;
591 if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
593 return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
596 CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
597 IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
599 Handle_t hndl = menu->GetHandle();
609 static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
611 Handle_t hndl = (Handle_t)params[1];
615 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
617 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
620 return menu->Display(params[2], params[3]) ? 1 : 0;
623 static cell_t VoteMenu(IPluginContext *pContext, const cell_t *params)
625 if (g_Menus.IsVoteInProgress())
627 return pContext->ThrowNativeError("A vote is already in progress");
630 Handle_t hndl = (Handle_t)params[1];
634 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
636 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
640 pContext->LocalToPhysAddr(params[2], &addr);
642 if (!g_Menus.StartVote(menu, params[3], addr, params[4]))
650 static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
652 Handle_t hndl = (Handle_t)params[1];
656 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
658 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
664 pContext->LocalToString(params[2], &info);
665 pContext->LocalToString(params[3], (char **)&dr.display);
666 dr.style = params[4];
668 return menu->AppendItem(info, dr) ? 1 : 0;
671 static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
673 Handle_t hndl = (Handle_t)params[1];
677 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
679 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
685 pContext->LocalToString(params[3], &info);
686 pContext->LocalToString(params[4], (char **)&dr.display);
687 dr.style = params[5];
689 return menu->InsertItem(params[2], info, dr) ? 1 : 0;
692 static cell_t RemoveMenuItem(IPluginContext *pContext, const cell_t *params)
694 Handle_t hndl = (Handle_t)params[1];
698 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
700 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
703 return menu->RemoveItem(params[2]) ? 1 : 0;
706 static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
708 Handle_t hndl = (Handle_t)params[1];
712 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
714 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
717 menu->RemoveAllItems();
722 static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
724 Handle_t hndl = (Handle_t)params[1];
728 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
730 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
736 if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
741 pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
742 pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
745 pContext->LocalToPhysAddr(params[5], &addr);
751 static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
753 Handle_t hndl = (Handle_t)params[1];
757 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
759 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
762 return menu->SetPagination(params[2]) ? 1 : 0;
765 static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
767 Handle_t hndl = (Handle_t)params[1];
771 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
773 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
776 return menu->GetPagination();
779 static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
781 Handle_t hndl = (Handle_t)params[1];
785 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
787 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
790 return menu->GetItemCount();
793 static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
795 Handle_t hndl = (Handle_t)params[1];
799 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
801 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
804 g_SourceMod.SetGlobalTarget(LANG_SERVER);
807 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
809 menu->SetDefaultTitle(buffer);
814 static cell_t GetMenuTitle(IPluginContext *pContext, const cell_t *params)
816 Handle_t hndl = (Handle_t)params[1];
820 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
822 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
826 const char *title = menu->GetDefaultTitle();
827 pContext->StringToLocalUTF8(params[2], params[3], title, &written);
829 return (cell_t)written;
833 static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
835 Handle_t hndl = (Handle_t)params[1];
839 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
841 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
844 IMenuPanel *panel = menu->CreatePanel();
845 hndl = MakePanelHandle(panel, pContext);
854 static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
856 Handle_t hndl = (Handle_t)params[1];
860 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
862 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
865 return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
868 static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
870 Handle_t hndl = (Handle_t)params[1];
874 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
876 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
879 return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
882 static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
884 Handle_t hndl = (Handle_t)params[1];
888 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
890 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
893 unsigned int flags = menu->GetMenuOptionFlags();
894 flags |= MENUFLAG_BUTTON_EXIT;
895 menu->SetMenuOptionFlags(flags);
896 flags = menu->GetMenuOptionFlags();
897 return ((flags & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
900 static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
902 Handle_t hndl = (Handle_t)params[1];
906 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
908 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
911 unsigned int flags = menu->GetMenuOptionFlags();
912 flags |= MENUFLAG_BUTTON_EXITBACK;
913 menu->SetMenuOptionFlags(flags);
918 static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
920 Handle_t hndl = (Handle_t)params[1];
924 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
926 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
929 g_Menus.CancelMenu(menu);
934 static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
936 return g_Menus.IsVoteInProgress() ? 1 : 0;
939 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
941 Handle_t hndl = (Handle_t)params[1];
945 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
947 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
950 return menu->GetDrawStyle()->GetHandle();
953 static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
955 IMenuStyle *style = GetStyleFromCell(params[1]);
961 return style->GetHandle();
964 static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
966 Handle_t hndl = (Handle_t)params[1];
972 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
974 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
977 style = g_Menus.GetDefaultStyle();
980 IMenuPanel *panel = style->CreatePanel();
982 hndl = MakePanelHandle(panel, pContext);
992 static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
994 Handle_t hndl = (Handle_t)params[1];
1000 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1002 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1005 style = g_Menus.GetDefaultStyle();
1008 IPluginFunction *pFunction;
1009 if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
1011 return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
1014 CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
1016 IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
1017 hndl = pMenu->GetHandle();
1027 static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
1029 Handle_t hndl = (Handle_t)params[2];
1035 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1037 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1040 style = g_Menus.GetDefaultStyle();
1043 return style->GetClientMenu(params[1], NULL);
1046 static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
1048 Handle_t hndl = (Handle_t)params[3];
1054 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1056 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1059 style = g_Menus.GetDefaultStyle();
1062 return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
1065 static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
1067 Handle_t hndl = (Handle_t)params[1];
1073 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1075 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1078 style = g_Menus.GetDefaultStyle();
1081 return style->GetMaxPageItems();
1084 static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
1086 Handle_t hndl = (Handle_t)params[1];
1090 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1092 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1095 return panel->GetParentStyle()->GetHandle();
1098 static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
1100 Handle_t hndl = (Handle_t)params[1];
1104 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1106 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1110 pContext->LocalToString(params[2], &text);
1112 panel->DrawTitle(text, params[3] ? true : false);
1117 static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
1119 Handle_t hndl = (Handle_t)params[1];
1123 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1125 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1129 pContext->LocalToString(params[2], (char **)&dr.display);
1130 dr.style = params[3];
1132 return panel->DrawItem(dr);
1135 static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
1137 Handle_t hndl = (Handle_t)params[1];
1141 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1143 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1147 pContext->LocalToString(params[2], &text);
1149 return panel->DrawRawLine(text) ? 1 : 0;
1152 static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
1154 Handle_t hndl = (Handle_t)params[1];
1158 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1160 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1163 return panel->CanDrawItem(params[2]);
1166 static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
1168 Handle_t hndl = (Handle_t)params[1];
1172 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1174 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1177 IPluginFunction *pFunction;
1178 if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
1180 return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
1183 CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
1184 if (!panel->SendDisplay(params[2], handler, params[4]))
1186 g_MenuHelpers.FreePanelHandler(handler);
1192 static cell_t SetPanelKeys(IPluginContext *pContext, const cell_t *params)
1194 Handle_t hndl = (Handle_t)params[1];
1198 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1200 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1203 return panel->SetSelectableKeys(params[2]);
1206 static cell_t GetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
1208 Handle_t hndl = (Handle_t)params[1];
1212 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1214 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1217 return panel->GetCurrentKey();
1220 static cell_t GetPanelTextRemaining(IPluginContext *pContext, const cell_t *params)
1222 Handle_t hndl = (Handle_t)params[1];
1226 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1228 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1231 return panel->GetAmountRemaining();
1234 static cell_t SetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
1236 Handle_t hndl = (Handle_t)params[1];
1240 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1242 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1245 return panel->SetCurrentKey(params[2]) ? 1 : 0;
1248 static cell_t RedrawMenuItem(IPluginContext *pContext, const cell_t *params)
1252 return pContext->ThrowNativeError("You can only call this once from a MenuAction_DisplayItem callback");
1256 pContext->LocalToString(params[1], &str);
1258 ItemDrawInfo dr = *s_CurDrawInfo;
1261 if ((s_CurPanelReturn = s_pCurPanel->DrawItem(dr)) != 0)
1266 return s_CurPanelReturn;
1269 static cell_t GetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
1271 Handle_t hndl = (Handle_t)params[1];
1275 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1277 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1280 return menu->GetMenuOptionFlags();
1283 static cell_t SetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
1285 Handle_t hndl = (Handle_t)params[1];
1289 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1291 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1294 menu->SetMenuOptionFlags(params[2]);
1299 static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
1301 if (!g_Menus.IsVoteInProgress())
1303 return pContxt->ThrowNativeError("No vote is in progress");
1306 g_Menus.CancelVoting();
1311 static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
1313 Handle_t hndl = (Handle_t)params[1];
1317 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1319 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1322 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
1325 return pContext->ThrowNativeError("Invalid function %x", params[2]);
1329 array[0] = pFunction;
1330 array[1] = (void *)¶ms[2];
1332 IMenuHandler *pHandler = menu->GetHandler();
1333 if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
1335 return pContext->ThrowNativeError("The given menu does not support this option");
1341 static cell_t CheckVoteDelay(IPluginContext *pContext, const cell_t *params)
1343 return g_Menus.GetRemainingVoteDelay();
1346 REGISTER_NATIVES(menuNatives)
1348 {"AddMenuItem", AddMenuItem},
1349 {"CanPanelDrawFlags", CanPanelDrawFlags},
1350 {"CancelClientMenu", CancelClientMenu},
1351 {"CancelMenu", CancelMenu},
1352 {"CancelVote", CancelVote},
1353 {"CheckVoteDelay", CheckVoteDelay},
1354 {"CreateMenu", CreateMenu},
1355 {"CreateMenuEx", CreateMenuEx},
1356 {"CreatePanel", CreatePanel},
1357 {"CreatePanelFromMenu", CreatePanelFromMenu},
1358 {"DisplayMenu", DisplayMenu},
1359 {"DrawPanelItem", DrawPanelItem},
1360 {"DrawPanelText", DrawPanelText},
1361 {"GetClientMenu", GetClientMenu},
1362 {"GetMaxPageItems", GetMaxPageItems},
1363 {"GetMenuExitBackButton", GetMenuExitBackButton},
1364 {"GetMenuExitButton", GetMenuExitButton},
1365 {"GetMenuItem", GetMenuItem},
1366 {"GetMenuItemCount", GetMenuItemCount},
1367 {"GetMenuOptionFlags", GetMenuOptionFlags},
1368 {"GetMenuPagination", GetMenuPagination},
1369 {"GetMenuStyle", GetMenuStyle},
1370 {"GetMenuStyleHandle", GetMenuStyleHandle},
1371 {"GetMenuTitle", GetMenuTitle},
1372 {"GetPanelTextRemaining", GetPanelTextRemaining},
1373 {"GetPanelCurrentKey", GetPanelCurrentKey},
1374 {"GetPanelStyle", GetPanelStyle},
1375 {"InsertMenuItem", InsertMenuItem},
1376 {"IsVoteInProgress", IsVoteInProgress},
1377 {"RedrawMenuItem", RedrawMenuItem},
1378 {"RemoveAllMenuItems", RemoveAllMenuItems},
1379 {"RemoveMenuItem", RemoveMenuItem},
1380 {"SendPanelToClient", SendPanelToClient},
1381 {"SetMenuExitBackButton", SetMenuExitBackButton},
1382 {"SetMenuExitButton", SetMenuExitButton},
1383 {"SetMenuOptionFlags", SetMenuOptionFlags},
1384 {"SetMenuPagination", SetMenuPagination},
1385 {"SetMenuTitle", SetMenuTitle},
1386 {"SetPanelCurrentKey", SetPanelCurrentKey},
1387 {"SetPanelTitle", SetPanelTitle},
1388 {"SetPanelKeys", SetPanelKeys},
1389 {"SetVoteResultCallback", SetVoteResultCallback},
1390 {"VoteMenu", VoteMenu},