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
45 #include "ChatTriggers.h"
47 #if defined CreateMenu
52 * And God said, "let there be menus," and behold, there were menus.
53 * God saw the menus and they were good. And the evening and the morning
59 MenuAction_Start = (1<<0), /**< A menu has been started (nothing passed) */
60 MenuAction_Display = (1<<1), /**< A menu is about to be displayed (param1=client, param2=MenuPanel Handle) */
61 MenuAction_Select = (1<<2), /**< An item was selected (param1=client, param2=item) */
62 MenuAction_Cancel = (1<<3), /**< The menu was cancelled (param1=client, param2=item) */
63 MenuAction_End = (1<<4), /**< A menu's display/selection cycle is complete (nothing passed). */
64 MenuAction_VoteEnd = (1<<5), /**< (VOTE ONLY): A vote sequence has ended (param1=chosen item) */
65 MenuAction_VoteStart = (1<<6), /**< (VOTE ONLY): A vote sequence has started */
66 MenuAction_VoteCancel = (1<<7), /**< (VOTE ONLY): A vote sequence has been cancelled (nothing passed) */
67 MenuAction_DrawItem = (1<<8), /**< A style is being drawn; return the new style (param1=client, param2=item) */
68 MenuAction_DisplayItem = (1<<9), /**< the odd duck */
71 class CPanelHandler : public IMenuHandler
73 friend class MenuNativeHelpers;
78 void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
79 void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item);
81 IPluginFunction *m_pFunc;
85 class CMenuHandler : public IMenuHandler
87 friend class MenuNativeHelpers;
89 CMenuHandler(IPluginFunction *pBasic, int flags);
91 void OnMenuStart(IBaseMenu *menu);
92 void OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *display);
93 void OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page);
94 void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason);
95 void OnMenuEnd(IBaseMenu *menu, MenuEndReason reason);
96 void OnMenuDestroy(IBaseMenu *menu);
97 void OnMenuVoteStart(IBaseMenu *menu);
98 void OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results);
99 void OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason);
100 void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
101 unsigned int OnMenuDisplayItem(IBaseMenu *menu, int client, IMenuPanel *panel, unsigned int item, const ItemDrawInfo &dr);
102 bool OnSetHandlerOption(const char *option, const void *data);
104 void OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style);
105 void OnMenuDisplayItem(IBaseMenu *menu, int client, unsigned int item, const char **display);
108 cell_t DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res=0);
110 IPluginFunction *m_pBasic;
112 IPluginFunction *m_pVoteResults;
113 cell_t m_fnVoteResult;
117 * GLOBAL CLASS FOR HELPERS
120 class MenuNativeHelpers :
121 public SMGlobalClass,
122 public IHandleTypeDispatch,
123 public IPluginsListener
126 virtual void OnSourceModAllInitialized()
128 m_PanelType = g_HandleSys.CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
129 m_TempPanelType = g_HandleSys.CreateType("TempIMenuPanel", this, m_PanelType, NULL, NULL, g_pCoreIdent, NULL);
130 g_PluginSys.AddPluginsListener(this);
133 virtual void OnSourceModShutdown()
135 g_HandleSys.RemoveType(m_TempPanelType, g_pCoreIdent);
136 g_HandleSys.RemoveType(m_PanelType, g_pCoreIdent);
138 while (!m_FreePanelHandlers.empty())
140 delete m_FreePanelHandlers.front();
141 m_FreePanelHandlers.pop();
144 while (!m_FreeMenuHandlers.empty())
146 delete m_FreeMenuHandlers.front();
147 m_FreeMenuHandlers.pop();
151 virtual void OnHandleDestroy(HandleType_t type, void *object)
153 if (type == m_TempPanelType)
158 IMenuPanel *panel = (IMenuPanel *)object;
163 * It is extremely important that unloaded plugins don't crash.
164 * Thus, if a plugin unloads, we run through every handler we have.
165 * This means we do almost no runtime work for keeping track of
166 * our panel handlers (we don't have to store a list of the running
167 * ones), but when push comes to shove, we have to scan them all
168 * in case any of them are active.
170 virtual void OnPluginUnloaded(IPlugin *plugin)
172 for (size_t i = 0; i < m_PanelHandlers.size(); i++)
174 if (m_PanelHandlers[i]->m_pPlugin == plugin)
176 m_PanelHandlers[i]->m_pPlugin = NULL;
177 m_PanelHandlers[i]->m_pFunc = NULL;
182 inline HandleType_t GetPanelType()
187 inline HandleType_t GetTempPanelType()
189 return m_TempPanelType;
192 CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
194 CPanelHandler *handler;
195 if (m_FreePanelHandlers.empty())
197 handler = new CPanelHandler;
198 m_PanelHandlers.push_back(handler);
200 handler = m_FreePanelHandlers.front();
201 m_FreePanelHandlers.pop();
203 handler->m_pFunc = pFunction;
204 handler->m_pPlugin = g_PluginSys.GetPluginByCtx(pFunction->GetParentContext()->GetContext());
208 void FreePanelHandler(CPanelHandler *handler)
210 handler->m_pFunc = NULL;
211 handler->m_pPlugin = NULL;
212 m_FreePanelHandlers.push(handler);
215 CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
217 CMenuHandler *handler;
218 if (m_FreeMenuHandlers.empty())
220 handler = new CMenuHandler(pFunction, flags);
222 handler = m_FreeMenuHandlers.front();
223 m_FreeMenuHandlers.pop();
224 handler->m_pBasic = pFunction;
225 handler->m_Flags = flags;
226 handler->m_pVoteResults = NULL;
231 void FreeMenuHandler(CMenuHandler *handler)
233 m_FreeMenuHandlers.push(handler);
237 HandleType_t m_PanelType;
238 HandleType_t m_TempPanelType;
239 CStack<CPanelHandler *> m_FreePanelHandlers;
240 CStack<CMenuHandler *> m_FreeMenuHandlers;
241 CVector<CPanelHandler *> m_PanelHandlers;
245 * BASIC PANEL HANDLER WRAPPER
248 void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
252 m_pFunc->PushCell(BAD_HANDLE);
253 m_pFunc->PushCell(MenuAction_Cancel);
254 m_pFunc->PushCell(client);
255 m_pFunc->PushCell(reason);
256 m_pFunc->Execute(NULL);
258 g_MenuHelpers.FreePanelHandler(this);
261 void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
265 unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
266 m_pFunc->PushCell(BAD_HANDLE);
267 m_pFunc->PushCell(MenuAction_Select);
268 m_pFunc->PushCell(client);
269 m_pFunc->PushCell(item);
270 m_pFunc->Execute(NULL);
271 g_ChatTriggers.SetReplyTo(old_reply);
273 g_MenuHelpers.FreePanelHandler(this);
276 static IMenuPanel *s_pCurPanel = NULL;
277 static unsigned int s_CurPanelReturn = 0;
278 static const ItemDrawInfo *s_CurDrawInfo = NULL;
279 static unsigned int *s_CurSelectPosition = NULL;
282 * MENU HANDLER WRAPPER
284 CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) :
285 m_pBasic(pBasic), m_Flags(flags), m_pVoteResults(NULL)
287 /* :TODO: We can probably cache the handle ahead of time */
290 void CMenuHandler::OnMenuStart(IBaseMenu *menu)
292 if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
294 DoAction(menu, MenuAction_Start, 0, 0);
298 void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
300 if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
303 sec.pIdentity = g_pCoreIdent;
304 sec.pOwner = m_pBasic->GetParentContext()->GetIdentity();
307 g_HandleSys.InitAccessDefaults(NULL, &access);
308 access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
310 Handle_t hndl = g_HandleSys.CreateHandleEx(g_MenuHelpers.GetTempPanelType(), panel, &sec, &access, NULL);
312 DoAction(menu, MenuAction_Display, client, hndl);
314 g_HandleSys.FreeHandle(hndl, &sec);
318 void CMenuHandler::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
320 /* Save old position first. */
321 unsigned int first_item = item_on_page;
322 unsigned int *old_pos = s_CurSelectPosition;
324 s_CurSelectPosition = &first_item;
326 unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
327 DoAction(menu, MenuAction_Select, client, item);
328 g_ChatTriggers.SetReplyTo(old_reply);
330 s_CurSelectPosition = old_pos;
333 void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
335 unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
336 DoAction(menu, MenuAction_Cancel, client, (cell_t)reason);
337 g_ChatTriggers.SetReplyTo(old_reply);
340 void CMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
342 DoAction(menu, MenuAction_End, reason, 0);
345 void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
347 g_MenuHelpers.FreeMenuHandler(this);
350 void CMenuHandler::OnMenuVoteStart(IBaseMenu *menu)
352 DoAction(menu, MenuAction_VoteStart, 0, 0);
355 void CMenuHandler::OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason)
357 DoAction(menu, MenuAction_VoteCancel, reason, 0);
360 void CMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
362 if ((m_Flags & (int)MenuAction_DrawItem) == (int)MenuAction_DrawItem)
364 cell_t result = DoAction(menu, MenuAction_DrawItem, client, item, style);
365 style = (unsigned int)result;
369 unsigned int CMenuHandler::OnMenuDisplayItem(IBaseMenu *menu,
373 const ItemDrawInfo &dr)
375 if ((m_Flags & (int)MenuAction_DisplayItem) == (int)MenuAction_DisplayItem)
377 IMenuPanel *oldpanel = s_pCurPanel;
378 unsigned int oldret = s_CurPanelReturn;
379 const ItemDrawInfo *oldinfo = s_CurDrawInfo;
381 s_CurPanelReturn = 0;
384 cell_t res = DoAction(menu, MenuAction_DisplayItem, client, item, 0);
388 res = s_CurPanelReturn;
391 s_pCurPanel = oldpanel;
392 s_CurPanelReturn = oldret;
393 s_CurDrawInfo = oldinfo;
401 cell_t CMenuHandler::DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res)
403 #if defined MENU_DEBUG
404 g_Logger.LogMessage("[SM_MENU] CMenuHandler::DoAction() (menu %p/%08x) (action %d) (param1 %d) (param2 %d)",
411 cell_t res = def_res;
412 m_pBasic->PushCell(menu->GetHandle());
413 m_pBasic->PushCell((cell_t)action);
414 m_pBasic->PushCell(param1);
415 m_pBasic->PushCell(param2);
416 m_pBasic->Execute(&res);
420 void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
424 /* Call MenuAction_VoteEnd instead. See if there are any extra winners. */
425 unsigned int num_items = 1;
426 for (unsigned int i=1; i<results->num_items; i++)
428 if (results->item_list[i].count != results->item_list[0].count)
435 /* See if we need to pick a random winner. */
436 unsigned int winning_item;
441 winning_item = rand() % num_items;
442 winning_item = results->item_list[winning_item].item;
444 /* No, take the first. */
445 winning_item = results->item_list[0].item;
448 unsigned int total_votes = results->num_votes;
449 unsigned int winning_votes = results->item_list[0].count;
451 DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
453 IPluginContext *pContext = m_pVoteResults->GetParentContext();
454 bool no_call = false;
458 cell_t client_array_address = -1;
459 cell_t *client_array_base = NULL;
460 cell_t client_array_size = results->num_clients + (results->num_clients * 2);
461 if (client_array_size)
463 if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
466 g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
469 cell_t target_offs = sizeof(cell_t) * results->num_clients;
470 cell_t *cur_index = client_array_base;
472 for (unsigned int i=0; i<results->num_clients; i++)
474 /* Copy the array index */
475 *cur_index = target_offs;
476 /* Get the current array address */
477 cur_array = (cell_t *)((char *)cur_index + target_offs);
478 /* Store information */
479 cur_array[0] = results->client_list[i].client;
480 cur_array[1] = results->client_list[i].item;
481 /* Adjust for the new target by subtracting one indirection
482 * and adding one array.
484 target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
491 cell_t item_array_address = -1;
492 cell_t *item_array_base = NULL;
493 cell_t item_array_size = results->num_items + (results->num_items * 2);
496 if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
499 g_DbgReporter.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
502 cell_t target_offs = sizeof(cell_t) * results->num_items;
503 cell_t *cur_index = item_array_base;
505 for (unsigned int i=0; i<results->num_items; i++)
507 /* Copy the array index */
508 *cur_index = target_offs;
509 /* Get the current array address */
510 cur_array = (cell_t *)((char *)cur_index + target_offs);
511 /* Store information */
512 cur_array[0] = results->item_list[i].item;
513 cur_array[1] = results->item_list[i].count;
514 /* Adjust for the new target by subtracting one indirection
515 * and adding one array.
517 target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
523 /* Finally, push everything */
526 m_pVoteResults->PushCell(menu->GetHandle());
527 m_pVoteResults->PushCell(results->num_votes);
528 m_pVoteResults->PushCell(results->num_clients);
529 m_pVoteResults->PushCell(client_array_address);
530 m_pVoteResults->PushCell(results->num_items);
531 m_pVoteResults->PushCell(item_array_address);
532 m_pVoteResults->Execute(NULL);
535 /* Free what we allocated, in reverse order as required */
536 if (item_array_address != -1)
538 pContext->HeapPop(item_array_address);
540 if (client_array_address != -1)
542 pContext->HeapPop(client_array_address);
547 bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
549 if (strcmp(option, "set_vote_results_handler") == 0)
551 void **array = (void **)data;
552 m_pVoteResults = (IPluginFunction *)array[0];
553 m_fnVoteResult = *(cell_t *)((cell_t *)array[1]);
561 * INLINE FUNCTIONS FOR NATIVES
564 inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
566 return g_HandleSys.CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
569 inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
572 sec.pIdentity = g_pCoreIdent;
574 return g_HandleSys.ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
577 inline IMenuStyle *GetStyleFromCell(cell_t cell)
581 MenuStyle_Default = 0, /**< The "default" menu style for the mod */
582 MenuStyle_Valve = 1, /**< The Valve provided menu style (Used on HL2DM) */
583 MenuStyle_Radio = 2, /**< The simpler menu style commonly used on CS:S */
586 if (cell == MenuStyle_Valve)
588 return &g_ValveMenuStyle;
589 } else if (cell == MenuStyle_Radio
590 && g_RadioMenuStyle.IsSupported())
592 return &g_RadioMenuStyle;
595 return g_Menus.GetDefaultStyle();
598 /***********************************
599 **** NATIVE DEFINITIONS ***********
600 ***********************************/
602 static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
604 IMenuStyle *style = g_Menus.GetDefaultStyle();
605 IPluginFunction *pFunction;
607 if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
609 return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
612 CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
613 IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
615 Handle_t hndl = menu->GetHandle();
625 static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
627 Handle_t hndl = (Handle_t)params[1];
631 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
633 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
636 return menu->Display(params[2], params[3]) ? 1 : 0;
639 static cell_t DisplayMenuAtItem(IPluginContext *pContext, const cell_t *params)
641 Handle_t hndl = (Handle_t)params[1];
645 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
647 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
650 return menu->DisplayAtItem(params[2], params[4], params[3]) ? 1 : 0;
653 static cell_t VoteMenu(IPluginContext *pContext, const cell_t *params)
655 if (g_Menus.IsVoteInProgress())
657 return pContext->ThrowNativeError("A vote is already in progress");
660 Handle_t hndl = (Handle_t)params[1];
664 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
666 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
670 pContext->LocalToPhysAddr(params[2], &addr);
672 if (!g_Menus.StartVote(menu, params[3], addr, params[4]))
680 static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
682 Handle_t hndl = (Handle_t)params[1];
686 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
688 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
694 pContext->LocalToString(params[2], &info);
695 pContext->LocalToString(params[3], (char **)&dr.display);
696 dr.style = params[4];
698 return menu->AppendItem(info, dr) ? 1 : 0;
701 static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
703 Handle_t hndl = (Handle_t)params[1];
707 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
709 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
715 pContext->LocalToString(params[3], &info);
716 pContext->LocalToString(params[4], (char **)&dr.display);
717 dr.style = params[5];
719 return menu->InsertItem(params[2], info, dr) ? 1 : 0;
722 static cell_t RemoveMenuItem(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);
733 return menu->RemoveItem(params[2]) ? 1 : 0;
736 static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
738 Handle_t hndl = (Handle_t)params[1];
742 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
744 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
747 menu->RemoveAllItems();
752 static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params)
754 Handle_t hndl = (Handle_t)params[1];
758 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
760 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
766 if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
771 pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
772 pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
775 pContext->LocalToPhysAddr(params[5], &addr);
781 static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
783 Handle_t hndl = (Handle_t)params[1];
787 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
789 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
792 return menu->SetPagination(params[2]) ? 1 : 0;
795 static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
797 Handle_t hndl = (Handle_t)params[1];
801 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
803 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
806 return menu->GetPagination();
809 static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
811 Handle_t hndl = (Handle_t)params[1];
815 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
817 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
820 return menu->GetItemCount();
823 static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
825 Handle_t hndl = (Handle_t)params[1];
829 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
831 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
834 g_SourceMod.SetGlobalTarget(LANG_SERVER);
837 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
839 menu->SetDefaultTitle(buffer);
844 static cell_t GetMenuTitle(IPluginContext *pContext, const cell_t *params)
846 Handle_t hndl = (Handle_t)params[1];
850 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
852 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
856 const char *title = menu->GetDefaultTitle();
857 pContext->StringToLocalUTF8(params[2], params[3], title, &written);
859 return (cell_t)written;
863 static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
865 Handle_t hndl = (Handle_t)params[1];
869 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
871 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
874 IMenuPanel *panel = menu->CreatePanel();
875 hndl = MakePanelHandle(panel, pContext);
884 static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
886 Handle_t hndl = (Handle_t)params[1];
890 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
892 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
895 return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
898 static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
900 Handle_t hndl = (Handle_t)params[1];
904 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
906 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
909 return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
912 static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
914 Handle_t hndl = (Handle_t)params[1];
918 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
920 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
923 unsigned int flags = menu->GetMenuOptionFlags();
927 flags |= MENUFLAG_BUTTON_EXIT;
929 flags &= ~MENUFLAG_BUTTON_EXIT;
932 menu->SetMenuOptionFlags(flags);
933 unsigned int new_flags = menu->GetMenuOptionFlags();
935 return (flags == new_flags);
938 static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
940 Handle_t hndl = (Handle_t)params[1];
944 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
946 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
949 unsigned int flags = menu->GetMenuOptionFlags();
953 flags |= MENUFLAG_BUTTON_EXITBACK;
955 flags &= ~MENUFLAG_BUTTON_EXITBACK;
958 menu->SetMenuOptionFlags(flags);
963 static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
965 Handle_t hndl = (Handle_t)params[1];
969 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
971 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
974 g_Menus.CancelMenu(menu);
979 static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
981 return g_Menus.IsVoteInProgress() ? 1 : 0;
984 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
986 Handle_t hndl = (Handle_t)params[1];
990 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
992 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
995 return menu->GetDrawStyle()->GetHandle();
998 static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
1000 IMenuStyle *style = GetStyleFromCell(params[1]);
1006 return style->GetHandle();
1009 static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
1011 Handle_t hndl = (Handle_t)params[1];
1017 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1019 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1022 style = g_Menus.GetDefaultStyle();
1025 IMenuPanel *panel = style->CreatePanel();
1027 hndl = MakePanelHandle(panel, pContext);
1030 panel->DeleteThis();
1037 static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
1039 Handle_t hndl = (Handle_t)params[1];
1045 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1047 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1050 style = g_Menus.GetDefaultStyle();
1053 IPluginFunction *pFunction;
1054 if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
1056 return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
1059 CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
1061 IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
1062 hndl = pMenu->GetHandle();
1072 static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
1074 Handle_t hndl = (Handle_t)params[2];
1080 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1082 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1085 style = g_Menus.GetDefaultStyle();
1088 return style->GetClientMenu(params[1], NULL);
1091 static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
1093 Handle_t hndl = (Handle_t)params[3];
1099 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1101 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1104 style = g_Menus.GetDefaultStyle();
1107 return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
1110 static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
1112 Handle_t hndl = (Handle_t)params[1];
1118 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1120 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1123 style = g_Menus.GetDefaultStyle();
1126 return style->GetMaxPageItems();
1129 static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
1131 Handle_t hndl = (Handle_t)params[1];
1135 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1137 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1140 return panel->GetParentStyle()->GetHandle();
1143 static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
1145 Handle_t hndl = (Handle_t)params[1];
1149 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1151 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1155 pContext->LocalToString(params[2], &text);
1157 panel->DrawTitle(text, params[3] ? true : false);
1162 static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
1164 Handle_t hndl = (Handle_t)params[1];
1168 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1170 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1174 pContext->LocalToString(params[2], (char **)&dr.display);
1175 dr.style = params[3];
1177 return panel->DrawItem(dr);
1180 static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
1182 Handle_t hndl = (Handle_t)params[1];
1186 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1188 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1192 pContext->LocalToString(params[2], &text);
1194 return panel->DrawRawLine(text) ? 1 : 0;
1197 static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
1199 Handle_t hndl = (Handle_t)params[1];
1203 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1205 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1208 return panel->CanDrawItem(params[2]);
1211 static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
1213 Handle_t hndl = (Handle_t)params[1];
1217 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1219 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1222 IPluginFunction *pFunction;
1223 if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
1225 return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
1228 CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
1229 if (!panel->SendDisplay(params[2], handler, params[4]))
1231 g_MenuHelpers.FreePanelHandler(handler);
1237 static cell_t SetPanelKeys(IPluginContext *pContext, const cell_t *params)
1239 Handle_t hndl = (Handle_t)params[1];
1243 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1245 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1248 return panel->SetSelectableKeys(params[2]);
1251 static cell_t GetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
1253 Handle_t hndl = (Handle_t)params[1];
1257 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1259 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1262 return panel->GetCurrentKey();
1265 static cell_t GetPanelTextRemaining(IPluginContext *pContext, const cell_t *params)
1267 Handle_t hndl = (Handle_t)params[1];
1271 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1273 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1276 return panel->GetAmountRemaining();
1279 static cell_t SetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
1281 Handle_t hndl = (Handle_t)params[1];
1285 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1287 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1290 return panel->SetCurrentKey(params[2]) ? 1 : 0;
1293 static cell_t RedrawMenuItem(IPluginContext *pContext, const cell_t *params)
1297 return pContext->ThrowNativeError("You can only call this once from a MenuAction_DisplayItem callback");
1301 pContext->LocalToString(params[1], &str);
1303 ItemDrawInfo dr = *s_CurDrawInfo;
1306 if ((s_CurPanelReturn = s_pCurPanel->DrawItem(dr)) != 0)
1311 return s_CurPanelReturn;
1314 static cell_t GetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
1316 Handle_t hndl = (Handle_t)params[1];
1320 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1322 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1325 return menu->GetMenuOptionFlags();
1328 static cell_t SetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
1330 Handle_t hndl = (Handle_t)params[1];
1334 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1336 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1339 menu->SetMenuOptionFlags(params[2]);
1344 static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
1346 if (!g_Menus.IsVoteInProgress())
1348 return pContxt->ThrowNativeError("No vote is in progress");
1351 g_Menus.CancelVoting();
1356 static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
1358 Handle_t hndl = (Handle_t)params[1];
1362 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1364 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1367 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
1370 return pContext->ThrowNativeError("Invalid function %x", params[2]);
1374 array[0] = pFunction;
1375 array[1] = (void *)¶ms[2];
1377 IMenuHandler *pHandler = menu->GetHandler();
1378 if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
1380 return pContext->ThrowNativeError("The given menu does not support this option");
1386 static cell_t CheckVoteDelay(IPluginContext *pContext, const cell_t *params)
1388 return g_Menus.GetRemainingVoteDelay();
1391 static cell_t GetMenuSelectionPosition(IPluginContext *pContext, const cell_t *params)
1393 if (!s_CurSelectPosition)
1395 return pContext->ThrowNativeError("Can only be called from inside a MenuAction_Select callback");
1398 return *s_CurSelectPosition;
1401 REGISTER_NATIVES(menuNatives)
1403 {"AddMenuItem", AddMenuItem},
1404 {"CanPanelDrawFlags", CanPanelDrawFlags},
1405 {"CancelClientMenu", CancelClientMenu},
1406 {"CancelMenu", CancelMenu},
1407 {"CancelVote", CancelVote},
1408 {"CheckVoteDelay", CheckVoteDelay},
1409 {"CreateMenu", CreateMenu},
1410 {"CreateMenuEx", CreateMenuEx},
1411 {"CreatePanel", CreatePanel},
1412 {"CreatePanelFromMenu", CreatePanelFromMenu},
1413 {"DisplayMenu", DisplayMenu},
1414 {"DisplayMenuAtItem", DisplayMenuAtItem},
1415 {"DrawPanelItem", DrawPanelItem},
1416 {"DrawPanelText", DrawPanelText},
1417 {"GetClientMenu", GetClientMenu},
1418 {"GetMaxPageItems", GetMaxPageItems},
1419 {"GetMenuExitBackButton", GetMenuExitBackButton},
1420 {"GetMenuExitButton", GetMenuExitButton},
1421 {"GetMenuItem", GetMenuItem},
1422 {"GetMenuItemCount", GetMenuItemCount},
1423 {"GetMenuOptionFlags", GetMenuOptionFlags},
1424 {"GetMenuPagination", GetMenuPagination},
1425 {"GetMenuSelectionPosition",GetMenuSelectionPosition},
1426 {"GetMenuStyle", GetMenuStyle},
1427 {"GetMenuStyleHandle", GetMenuStyleHandle},
1428 {"GetMenuTitle", GetMenuTitle},
1429 {"GetPanelTextRemaining", GetPanelTextRemaining},
1430 {"GetPanelCurrentKey", GetPanelCurrentKey},
1431 {"GetPanelStyle", GetPanelStyle},
1432 {"InsertMenuItem", InsertMenuItem},
1433 {"IsVoteInProgress", IsVoteInProgress},
1434 {"RedrawMenuItem", RedrawMenuItem},
1435 {"RemoveAllMenuItems", RemoveAllMenuItems},
1436 {"RemoveMenuItem", RemoveMenuItem},
1437 {"SendPanelToClient", SendPanelToClient},
1438 {"SetMenuExitBackButton", SetMenuExitBackButton},
1439 {"SetMenuExitButton", SetMenuExitButton},
1440 {"SetMenuOptionFlags", SetMenuOptionFlags},
1441 {"SetMenuPagination", SetMenuPagination},
1442 {"SetMenuTitle", SetMenuTitle},
1443 {"SetPanelCurrentKey", SetPanelCurrentKey},
1444 {"SetPanelTitle", SetPanelTitle},
1445 {"SetPanelKeys", SetPanelKeys},
1446 {"SetVoteResultCallback", SetVoteResultCallback},
1447 {"VoteMenu", VoteMenu},