3 * =============================================================================
5 * Copyright (C) 2004-2008 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 "MenuManager.h"
35 #include "MenuStyle_Valve.h"
36 #include "MenuStyle_Radio.h"
37 #include "PlayerManager.h"
38 #include "sm_stringutil.h"
39 #include "sourcemm_api.h"
40 #if defined MENU_DEBUG
43 #include "ChatTriggers.h"
44 #include "logic_bridge.h"
45 #include "sourcemod.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 = handlesys->CreateType("IMenuPanel", this, 0, NULL, NULL, g_pCoreIdent, NULL);
129 m_TempPanelType = handlesys->CreateType("TempIMenuPanel", this, m_PanelType, NULL, NULL, g_pCoreIdent, NULL);
130 scripts->AddPluginsListener(this);
133 virtual void OnSourceModShutdown()
135 scripts->RemovePluginsListener(this);
136 handlesys->RemoveType(m_TempPanelType, g_pCoreIdent);
137 handlesys->RemoveType(m_PanelType, g_pCoreIdent);
139 while (!m_FreePanelHandlers.empty())
141 delete m_FreePanelHandlers.front();
142 m_FreePanelHandlers.pop();
145 while (!m_FreeMenuHandlers.empty())
147 delete m_FreeMenuHandlers.front();
148 m_FreeMenuHandlers.pop();
152 virtual void OnHandleDestroy(HandleType_t type, void *object)
154 if (type == m_TempPanelType)
159 IMenuPanel *panel = (IMenuPanel *)object;
163 virtual bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
165 *pSize = ((IMenuPanel *)object)->GetApproxMemUsage();
170 * It is extremely important that unloaded plugins don't crash.
171 * Thus, if a plugin unloads, we run through every handler we have.
172 * This means we do almost no runtime work for keeping track of
173 * our panel handlers (we don't have to store a list of the running
174 * ones), but when push comes to shove, we have to scan them all
175 * in case any of them are active.
177 virtual void OnPluginUnloaded(IPlugin *plugin)
179 for (size_t i = 0; i < m_PanelHandlers.size(); i++)
181 if (m_PanelHandlers[i]->m_pPlugin == plugin)
183 m_PanelHandlers[i]->m_pPlugin = NULL;
184 m_PanelHandlers[i]->m_pFunc = NULL;
189 inline HandleType_t GetPanelType()
194 inline HandleType_t GetTempPanelType()
196 return m_TempPanelType;
199 CPanelHandler *GetPanelHandler(IPluginFunction *pFunction)
201 CPanelHandler *handler;
202 if (m_FreePanelHandlers.empty())
204 handler = new CPanelHandler;
205 m_PanelHandlers.push_back(handler);
207 handler = m_FreePanelHandlers.front();
208 m_FreePanelHandlers.pop();
210 handler->m_pFunc = pFunction;
211 handler->m_pPlugin = scripts->FindPluginByContext(pFunction->GetParentContext()->GetContext());
215 void FreePanelHandler(CPanelHandler *handler)
217 handler->m_pFunc = NULL;
218 handler->m_pPlugin = NULL;
219 m_FreePanelHandlers.push(handler);
222 CMenuHandler *GetMenuHandler(IPluginFunction *pFunction, int flags)
224 CMenuHandler *handler;
225 if (m_FreeMenuHandlers.empty())
227 handler = new CMenuHandler(pFunction, flags);
229 handler = m_FreeMenuHandlers.front();
230 m_FreeMenuHandlers.pop();
231 handler->m_pBasic = pFunction;
232 handler->m_Flags = flags;
233 handler->m_pVoteResults = NULL;
238 void FreeMenuHandler(CMenuHandler *handler)
240 m_FreeMenuHandlers.push(handler);
244 HandleType_t m_PanelType;
245 HandleType_t m_TempPanelType;
246 CStack<CPanelHandler *> m_FreePanelHandlers;
247 CStack<CMenuHandler *> m_FreeMenuHandlers;
248 CVector<CPanelHandler *> m_PanelHandlers;
252 * BASIC PANEL HANDLER WRAPPER
255 void CPanelHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
259 m_pFunc->PushCell(BAD_HANDLE);
260 m_pFunc->PushCell(MenuAction_Cancel);
261 m_pFunc->PushCell(client);
262 m_pFunc->PushCell(reason);
263 m_pFunc->Execute(NULL);
265 g_MenuHelpers.FreePanelHandler(this);
268 void CPanelHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item)
272 unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
273 m_pFunc->PushCell(BAD_HANDLE);
274 m_pFunc->PushCell(MenuAction_Select);
275 m_pFunc->PushCell(client);
276 m_pFunc->PushCell(item);
277 m_pFunc->Execute(NULL);
278 g_ChatTriggers.SetReplyTo(old_reply);
280 g_MenuHelpers.FreePanelHandler(this);
283 static IMenuPanel *s_pCurPanel = NULL;
284 static unsigned int s_CurPanelReturn = 0;
285 static const ItemDrawInfo *s_CurDrawInfo = NULL;
286 static unsigned int *s_CurSelectPosition = NULL;
289 * MENU HANDLER WRAPPER
291 CMenuHandler::CMenuHandler(IPluginFunction *pBasic, int flags) :
292 m_pBasic(pBasic), m_Flags(flags), m_pVoteResults(NULL)
294 /* :TODO: We can probably cache the handle ahead of time */
297 void CMenuHandler::OnMenuStart(IBaseMenu *menu)
299 if ((m_Flags & (int)MenuAction_Start) == (int)MenuAction_Start)
301 DoAction(menu, MenuAction_Start, 0, 0);
305 void CMenuHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuPanel *panel)
307 if ((m_Flags & (int)MenuAction_Display) == (int)MenuAction_Display)
310 sec.pIdentity = g_pCoreIdent;
311 sec.pOwner = m_pBasic->GetParentContext()->GetIdentity();
314 handlesys->InitAccessDefaults(NULL, &access);
315 access.access[HandleAccess_Delete] = HANDLE_RESTRICT_IDENTITY|HANDLE_RESTRICT_OWNER;
317 Handle_t hndl = handlesys->CreateHandleEx(g_MenuHelpers.GetTempPanelType(), panel, &sec, &access, NULL);
319 DoAction(menu, MenuAction_Display, client, hndl);
321 handlesys->FreeHandle(hndl, &sec);
325 void CMenuHandler::OnMenuSelect2(IBaseMenu *menu, int client, unsigned int item, unsigned int item_on_page)
327 /* Save old position first. */
328 unsigned int first_item = item_on_page;
329 unsigned int *old_pos = s_CurSelectPosition;
331 s_CurSelectPosition = &first_item;
333 unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
334 DoAction(menu, MenuAction_Select, client, item);
335 g_ChatTriggers.SetReplyTo(old_reply);
337 s_CurSelectPosition = old_pos;
340 void CMenuHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason)
342 unsigned int old_reply = g_ChatTriggers.SetReplyTo(SM_REPLY_CHAT);
343 DoAction(menu, MenuAction_Cancel, client, (cell_t)reason);
344 g_ChatTriggers.SetReplyTo(old_reply);
347 void CMenuHandler::OnMenuEnd(IBaseMenu *menu, MenuEndReason reason)
349 DoAction(menu, MenuAction_End, reason, 0);
352 void CMenuHandler::OnMenuDestroy(IBaseMenu *menu)
354 g_MenuHelpers.FreeMenuHandler(this);
357 void CMenuHandler::OnMenuVoteStart(IBaseMenu *menu)
359 DoAction(menu, MenuAction_VoteStart, 0, 0);
362 void CMenuHandler::OnMenuVoteCancel(IBaseMenu *menu, VoteCancelReason reason)
364 DoAction(menu, MenuAction_VoteCancel, reason, 0);
367 void CMenuHandler::OnMenuDrawItem(IBaseMenu *menu, int client, unsigned int item, unsigned int &style)
369 if ((m_Flags & (int)MenuAction_DrawItem) == (int)MenuAction_DrawItem)
371 cell_t result = DoAction(menu, MenuAction_DrawItem, client, item, style);
372 style = (unsigned int)result;
376 unsigned int CMenuHandler::OnMenuDisplayItem(IBaseMenu *menu,
380 const ItemDrawInfo &dr)
382 if ((m_Flags & (int)MenuAction_DisplayItem) == (int)MenuAction_DisplayItem)
384 IMenuPanel *oldpanel = s_pCurPanel;
385 unsigned int oldret = s_CurPanelReturn;
386 const ItemDrawInfo *oldinfo = s_CurDrawInfo;
388 s_CurPanelReturn = 0;
391 cell_t res = DoAction(menu, MenuAction_DisplayItem, client, item, 0);
395 res = s_CurPanelReturn;
398 s_pCurPanel = oldpanel;
399 s_CurPanelReturn = oldret;
400 s_CurDrawInfo = oldinfo;
408 cell_t CMenuHandler::DoAction(IBaseMenu *menu, MenuAction action, cell_t param1, cell_t param2, cell_t def_res)
410 #if defined MENU_DEBUG
411 g_Logger.LogMessage("[SM_MENU] CMenuHandler::DoAction() (menu %p/%08x) (action %d) (param1 %d) (param2 %d)",
418 cell_t res = def_res;
419 m_pBasic->PushCell(menu->GetHandle());
420 m_pBasic->PushCell((cell_t)action);
421 m_pBasic->PushCell(param1);
422 m_pBasic->PushCell(param2);
423 m_pBasic->Execute(&res);
427 void CMenuHandler::OnMenuVoteResults(IBaseMenu *menu, const menu_vote_result_t *results)
431 /* Call MenuAction_VoteEnd instead. See if there are any extra winners. */
432 unsigned int num_items = 1;
433 for (unsigned int i=1; i<results->num_items; i++)
435 if (results->item_list[i].count != results->item_list[0].count)
442 /* See if we need to pick a random winner. */
443 unsigned int winning_item;
448 winning_item = rand() % num_items;
449 winning_item = results->item_list[winning_item].item;
451 /* No, take the first. */
452 winning_item = results->item_list[0].item;
455 unsigned int total_votes = results->num_votes;
456 unsigned int winning_votes = results->item_list[0].count;
458 DoAction(menu, MenuAction_VoteEnd, winning_item, (total_votes << 16) | (winning_votes & 0xFFFF));
460 IPluginContext *pContext = m_pVoteResults->GetParentContext();
461 bool no_call = false;
465 cell_t client_array_address = -1;
466 cell_t *client_array_base = NULL;
467 cell_t client_array_size = results->num_clients + (results->num_clients * 2);
468 if (client_array_size)
470 if ((err = pContext->HeapAlloc(client_array_size, &client_array_address, &client_array_base))
473 logicore.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for client list.", client_array_size * sizeof(cell_t));
476 cell_t target_offs = sizeof(cell_t) * results->num_clients;
477 cell_t *cur_index = client_array_base;
479 for (unsigned int i=0; i<results->num_clients; i++)
481 /* Copy the array index */
482 *cur_index = target_offs;
483 /* Get the current array address */
484 cur_array = (cell_t *)((char *)cur_index + target_offs);
485 /* Store information */
486 cur_array[0] = results->client_list[i].client;
487 cur_array[1] = results->client_list[i].item;
488 /* Adjust for the new target by subtracting one indirection
489 * and adding one array.
491 target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
498 cell_t item_array_address = -1;
499 cell_t *item_array_base = NULL;
500 cell_t item_array_size = results->num_items + (results->num_items * 2);
503 if ((err = pContext->HeapAlloc(item_array_size, &item_array_address, &item_array_base))
506 logicore.GenerateError(pContext, m_fnVoteResult, err, "Menu callback could not allocate %d bytes for item list.", item_array_size);
509 cell_t target_offs = sizeof(cell_t) * results->num_items;
510 cell_t *cur_index = item_array_base;
512 for (unsigned int i=0; i<results->num_items; i++)
514 /* Copy the array index */
515 *cur_index = target_offs;
516 /* Get the current array address */
517 cur_array = (cell_t *)((char *)cur_index + target_offs);
518 /* Store information */
519 cur_array[0] = results->item_list[i].item;
520 cur_array[1] = results->item_list[i].count;
521 /* Adjust for the new target by subtracting one indirection
522 * and adding one array.
524 target_offs += (sizeof(cell_t) * 2) - sizeof(cell_t);
530 /* Finally, push everything */
533 m_pVoteResults->PushCell(menu->GetHandle());
534 m_pVoteResults->PushCell(results->num_votes);
535 m_pVoteResults->PushCell(results->num_clients);
536 m_pVoteResults->PushCell(client_array_address);
537 m_pVoteResults->PushCell(results->num_items);
538 m_pVoteResults->PushCell(item_array_address);
539 m_pVoteResults->Execute(NULL);
542 /* Free what we allocated, in reverse order as required */
543 if (item_array_address != -1)
545 pContext->HeapPop(item_array_address);
547 if (client_array_address != -1)
549 pContext->HeapPop(client_array_address);
554 bool CMenuHandler::OnSetHandlerOption(const char *option, const void *data)
556 if (strcmp(option, "set_vote_results_handler") == 0)
558 void **array = (void **)data;
559 m_pVoteResults = (IPluginFunction *)array[0];
560 m_fnVoteResult = *(cell_t *)((cell_t *)array[1]);
568 * INLINE FUNCTIONS FOR NATIVES
571 inline Handle_t MakePanelHandle(IMenuPanel *panel, IPluginContext *pContext)
573 return handlesys->CreateHandle(g_MenuHelpers.GetPanelType(), panel, pContext->GetIdentity(), g_pCoreIdent, NULL);
576 inline HandleError ReadPanelHandle(Handle_t hndl, IMenuPanel **panel)
579 sec.pIdentity = g_pCoreIdent;
581 return handlesys->ReadHandle(hndl, g_MenuHelpers.GetPanelType(), &sec, (void **)panel);
584 inline IMenuStyle *GetStyleFromCell(cell_t cell)
588 MenuStyle_Default = 0, /**< The "default" menu style for the mod */
589 MenuStyle_Valve = 1, /**< The Valve provided menu style (Used on HL2DM) */
590 MenuStyle_Radio = 2, /**< The simpler menu style commonly used on CS:S */
593 if (cell == MenuStyle_Valve)
595 return &g_ValveMenuStyle;
596 } else if (cell == MenuStyle_Radio
597 && g_RadioMenuStyle.IsSupported())
599 return &g_RadioMenuStyle;
602 return g_Menus.GetDefaultStyle();
605 /***********************************
606 **** NATIVE DEFINITIONS ***********
607 ***********************************/
609 static cell_t CreateMenu(IPluginContext *pContext, const cell_t *params)
611 IMenuStyle *style = g_Menus.GetDefaultStyle();
612 IPluginFunction *pFunction;
614 if ((pFunction=pContext->GetFunctionById(params[1])) == NULL)
616 return pContext->ThrowNativeError("Function id %x is invalid", params[1]);
619 CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[2]);
620 IBaseMenu *menu = style->CreateMenu(handler, pContext->GetIdentity());
622 Handle_t hndl = menu->GetHandle();
632 static cell_t DisplayMenu(IPluginContext *pContext, const cell_t *params)
634 Handle_t hndl = (Handle_t)params[1];
638 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
640 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
643 return menu->Display(params[2], params[3]) ? 1 : 0;
646 static cell_t DisplayMenuAtItem(IPluginContext *pContext, const cell_t *params)
648 Handle_t hndl = (Handle_t)params[1];
652 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
654 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
657 return menu->DisplayAtItem(params[2], params[4], params[3]) ? 1 : 0;
660 static cell_t VoteMenu(IPluginContext *pContext, const cell_t *params)
662 if (g_Menus.IsVoteInProgress())
664 return pContext->ThrowNativeError("A vote is already in progress");
667 Handle_t hndl = (Handle_t)params[1];
671 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
673 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
677 pContext->LocalToPhysAddr(params[2], &addr);
685 if (!g_Menus.StartVote(menu, params[3], addr, params[4], flags))
693 static cell_t AddMenuItem(IPluginContext *pContext, const cell_t *params)
695 Handle_t hndl = (Handle_t)params[1];
699 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
701 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
707 pContext->LocalToString(params[2], &info);
708 pContext->LocalToString(params[3], (char **)&dr.display);
709 dr.style = params[4];
711 return menu->AppendItem(info, dr) ? 1 : 0;
714 static cell_t InsertMenuItem(IPluginContext *pContext, const cell_t *params)
716 Handle_t hndl = (Handle_t)params[1];
720 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
722 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
728 pContext->LocalToString(params[3], &info);
729 pContext->LocalToString(params[4], (char **)&dr.display);
730 dr.style = params[5];
732 return menu->InsertItem(params[2], info, dr) ? 1 : 0;
735 static cell_t RemoveMenuItem(IPluginContext *pContext, const cell_t *params)
737 Handle_t hndl = (Handle_t)params[1];
741 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
743 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
746 return menu->RemoveItem(params[2]) ? 1 : 0;
749 static cell_t RemoveAllMenuItems(IPluginContext *pContext, const cell_t *params)
751 Handle_t hndl = (Handle_t)params[1];
755 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
757 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
760 menu->RemoveAllItems();
765 static cell_t GetMenuItem(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);
779 if ((info=menu->GetItemInfo(params[2], &dr)) == NULL)
784 pContext->StringToLocalUTF8(params[3], params[4], info, NULL);
785 pContext->StringToLocalUTF8(params[6], params[7], dr.display ? dr.display : "", NULL);
788 pContext->LocalToPhysAddr(params[5], &addr);
794 static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params)
796 Handle_t hndl = (Handle_t)params[1];
800 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
802 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
805 return menu->SetPagination(params[2]) ? 1 : 0;
808 static cell_t GetMenuPagination(IPluginContext *pContext, const cell_t *params)
810 Handle_t hndl = (Handle_t)params[1];
814 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
816 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
819 return menu->GetPagination();
822 static cell_t GetMenuItemCount(IPluginContext *pContext, const cell_t *params)
824 Handle_t hndl = (Handle_t)params[1];
828 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
830 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
833 return menu->GetItemCount();
836 static cell_t SetMenuTitle(IPluginContext *pContext, const cell_t *params)
838 Handle_t hndl = (Handle_t)params[1];
842 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
844 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
847 g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
850 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
852 menu->SetDefaultTitle(buffer);
857 static cell_t GetMenuTitle(IPluginContext *pContext, const cell_t *params)
859 Handle_t hndl = (Handle_t)params[1];
863 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
865 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
869 const char *title = menu->GetDefaultTitle();
870 pContext->StringToLocalUTF8(params[2], params[3], title, &written);
872 return (cell_t)written;
876 static cell_t CreatePanelFromMenu(IPluginContext *pContext, const cell_t *params)
878 Handle_t hndl = (Handle_t)params[1];
882 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
884 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
887 IMenuPanel *panel = menu->CreatePanel();
888 hndl = MakePanelHandle(panel, pContext);
897 static cell_t GetMenuExitButton(IPluginContext *pContext, const cell_t *params)
899 Handle_t hndl = (Handle_t)params[1];
903 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
905 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
908 return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXIT) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
911 static cell_t GetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
913 Handle_t hndl = (Handle_t)params[1];
917 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
919 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
922 return ((menu->GetMenuOptionFlags() & MENUFLAG_BUTTON_EXITBACK) == MENUFLAG_BUTTON_EXIT) ? 1 : 0;
925 static cell_t SetMenuExitButton(IPluginContext *pContext, const cell_t *params)
927 Handle_t hndl = (Handle_t)params[1];
931 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
933 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
936 unsigned int flags = menu->GetMenuOptionFlags();
940 flags |= MENUFLAG_BUTTON_EXIT;
942 flags &= ~MENUFLAG_BUTTON_EXIT;
945 menu->SetMenuOptionFlags(flags);
946 unsigned int new_flags = menu->GetMenuOptionFlags();
948 return (flags == new_flags);
951 static cell_t SetMenuNoVoteButton(IPluginContext *pContext, const cell_t *params)
953 Handle_t hndl = (Handle_t)params[1];
957 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
959 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
962 unsigned int flags = menu->GetMenuOptionFlags();
966 flags |= MENUFLAG_BUTTON_NOVOTE;
968 flags &= ~MENUFLAG_BUTTON_NOVOTE;
971 menu->SetMenuOptionFlags(flags);
972 unsigned int new_flags = menu->GetMenuOptionFlags();
974 return (flags == new_flags);
977 static cell_t SetMenuExitBackButton(IPluginContext *pContext, const cell_t *params)
979 Handle_t hndl = (Handle_t)params[1];
983 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
985 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
988 unsigned int flags = menu->GetMenuOptionFlags();
992 flags |= MENUFLAG_BUTTON_EXITBACK;
994 flags &= ~MENUFLAG_BUTTON_EXITBACK;
997 menu->SetMenuOptionFlags(flags);
1002 static cell_t CancelMenu(IPluginContext *pContext, const cell_t *params)
1004 Handle_t hndl = (Handle_t)params[1];
1008 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1010 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1013 g_Menus.CancelMenu(menu);
1018 static cell_t IsVoteInProgress(IPluginContext *pContext, const cell_t *params)
1020 return g_Menus.IsVoteInProgress() ? 1 : 0;
1023 static cell_t GetMenuStyle(IPluginContext *pContext, const cell_t *params)
1025 Handle_t hndl = (Handle_t)params[1];
1029 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1031 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1034 return menu->GetDrawStyle()->GetHandle();
1037 static cell_t GetMenuStyleHandle(IPluginContext *pContext, const cell_t *params)
1039 IMenuStyle *style = GetStyleFromCell(params[1]);
1045 return style->GetHandle();
1048 static cell_t CreatePanel(IPluginContext *pContext, const cell_t *params)
1050 Handle_t hndl = (Handle_t)params[1];
1056 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1058 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1061 style = g_Menus.GetDefaultStyle();
1064 IMenuPanel *panel = style->CreatePanel();
1066 hndl = MakePanelHandle(panel, pContext);
1069 panel->DeleteThis();
1076 static cell_t CreateMenuEx(IPluginContext *pContext, const cell_t *params)
1078 Handle_t hndl = (Handle_t)params[1];
1084 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1086 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1089 style = g_Menus.GetDefaultStyle();
1092 IPluginFunction *pFunction;
1093 if ((pFunction=pContext->GetFunctionById(params[2])) == NULL)
1095 return pContext->ThrowNativeError("Function id %x is invalid", params[2]);
1098 CMenuHandler *handler = g_MenuHelpers.GetMenuHandler(pFunction, params[3]);
1100 IBaseMenu *pMenu = style->CreateMenu(handler, pContext->GetIdentity());
1101 hndl = pMenu->GetHandle();
1111 static cell_t GetClientMenu(IPluginContext *pContext, const cell_t *params)
1113 Handle_t hndl = (Handle_t)params[2];
1119 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1121 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1124 style = g_Menus.GetDefaultStyle();
1127 return style->GetClientMenu(params[1], NULL);
1130 static cell_t CancelClientMenu(IPluginContext *pContext, const cell_t *params)
1132 Handle_t hndl = (Handle_t)params[3];
1138 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1140 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1143 style = g_Menus.GetDefaultStyle();
1146 return style->CancelClientMenu(params[1], params[2] ? true : false) ? 1 : 0;
1149 static cell_t GetMaxPageItems(IPluginContext *pContext, const cell_t *params)
1151 Handle_t hndl = (Handle_t)params[1];
1157 if ((err=g_Menus.ReadStyleHandle(params[1], &style)) != HandleError_None)
1159 return pContext->ThrowNativeError("MenuStyle handle %x is invalid (error %d)", hndl, err);
1162 style = g_Menus.GetDefaultStyle();
1165 return style->GetMaxPageItems();
1168 static cell_t GetPanelStyle(IPluginContext *pContext, const cell_t *params)
1170 Handle_t hndl = (Handle_t)params[1];
1174 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1176 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1179 return panel->GetParentStyle()->GetHandle();
1182 static cell_t SetPanelTitle(IPluginContext *pContext, const cell_t *params)
1184 Handle_t hndl = (Handle_t)params[1];
1188 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1190 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1194 pContext->LocalToString(params[2], &text);
1196 panel->DrawTitle(text, params[3] ? true : false);
1201 static cell_t DrawPanelItem(IPluginContext *pContext, const cell_t *params)
1203 Handle_t hndl = (Handle_t)params[1];
1207 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1209 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1213 pContext->LocalToString(params[2], (char **)&dr.display);
1214 dr.style = params[3];
1216 return panel->DrawItem(dr);
1219 static cell_t DrawPanelText(IPluginContext *pContext, const cell_t *params)
1221 Handle_t hndl = (Handle_t)params[1];
1225 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1227 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1231 pContext->LocalToString(params[2], &text);
1233 return panel->DrawRawLine(text) ? 1 : 0;
1236 static cell_t CanPanelDrawFlags(IPluginContext *pContext, const cell_t *params)
1238 Handle_t hndl = (Handle_t)params[1];
1242 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1244 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1247 return panel->CanDrawItem(params[2]);
1250 static cell_t SendPanelToClient(IPluginContext *pContext, const cell_t *params)
1252 Handle_t hndl = (Handle_t)params[1];
1256 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1258 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1261 IPluginFunction *pFunction;
1262 if ((pFunction=pContext->GetFunctionById(params[3])) == NULL)
1264 return pContext->ThrowNativeError("Function id %x is invalid", params[3]);
1267 CPanelHandler *handler = g_MenuHelpers.GetPanelHandler(pFunction);
1268 if (!panel->SendDisplay(params[2], handler, params[4]))
1270 g_MenuHelpers.FreePanelHandler(handler);
1276 static cell_t SetPanelKeys(IPluginContext *pContext, const cell_t *params)
1278 Handle_t hndl = (Handle_t)params[1];
1282 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1284 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1287 return panel->SetSelectableKeys(params[2]);
1290 static cell_t GetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
1292 Handle_t hndl = (Handle_t)params[1];
1296 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1298 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1301 return panel->GetCurrentKey();
1304 static cell_t GetPanelTextRemaining(IPluginContext *pContext, const cell_t *params)
1306 Handle_t hndl = (Handle_t)params[1];
1310 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1312 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1315 return panel->GetAmountRemaining();
1318 static cell_t SetPanelCurrentKey(IPluginContext *pContext, const cell_t *params)
1320 Handle_t hndl = (Handle_t)params[1];
1324 if ((err=ReadPanelHandle(hndl, &panel)) != HandleError_None)
1326 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1329 return panel->SetCurrentKey(params[2]) ? 1 : 0;
1332 static cell_t RedrawMenuItem(IPluginContext *pContext, const cell_t *params)
1336 return pContext->ThrowNativeError("You can only call this once from a MenuAction_DisplayItem callback");
1340 pContext->LocalToString(params[1], &str);
1342 ItemDrawInfo dr = *s_CurDrawInfo;
1345 if ((s_CurPanelReturn = s_pCurPanel->DrawItem(dr)) != 0)
1350 return s_CurPanelReturn;
1353 static cell_t GetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
1355 Handle_t hndl = (Handle_t)params[1];
1359 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1361 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1364 return menu->GetMenuOptionFlags();
1367 static cell_t SetMenuOptionFlags(IPluginContext *pContext, const cell_t *params)
1369 Handle_t hndl = (Handle_t)params[1];
1373 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1375 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1378 menu->SetMenuOptionFlags(params[2]);
1383 static cell_t CancelVote(IPluginContext *pContxt, const cell_t *params)
1385 if (!g_Menus.IsVoteInProgress())
1387 return pContxt->ThrowNativeError("No vote is in progress");
1390 g_Menus.CancelVoting();
1395 static cell_t SetVoteResultCallback(IPluginContext *pContext, const cell_t *params)
1397 Handle_t hndl = (Handle_t)params[1];
1401 if ((err=g_Menus.ReadMenuHandle(params[1], &menu)) != HandleError_None)
1403 return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err);
1406 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
1409 return pContext->ThrowNativeError("Invalid function %x", params[2]);
1413 array[0] = pFunction;
1414 array[1] = (void *)¶ms[2];
1416 IMenuHandler *pHandler = menu->GetHandler();
1417 if (!pHandler->OnSetHandlerOption("set_vote_results_handler", (const void *)array))
1419 return pContext->ThrowNativeError("The given menu does not support this option");
1425 static cell_t CheckVoteDelay(IPluginContext *pContext, const cell_t *params)
1427 return g_Menus.GetRemainingVoteDelay();
1430 static cell_t GetMenuSelectionPosition(IPluginContext *pContext, const cell_t *params)
1432 if (!s_CurSelectPosition)
1434 return pContext->ThrowNativeError("Can only be called from inside a MenuAction_Select callback");
1437 return *s_CurSelectPosition;
1440 static cell_t IsClientInVotePool(IPluginContext *pContext, const cell_t *params)
1443 IGamePlayer *pPlayer;
1446 if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
1448 return pContext->ThrowNativeError("Invalid client index %d", client);
1451 if (!g_Menus.IsVoteInProgress())
1453 return pContext->ThrowNativeError("No vote is in progress");
1456 return g_Menus.IsClientInVotePool(client) ? 1 : 0;
1459 static cell_t RedrawClientVoteMenu(IPluginContext *pContext, const cell_t *params)
1462 IGamePlayer *pPlayer;
1465 if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL)
1467 return pContext->ThrowNativeError("Invalid client index %d", client);
1470 if (!g_Menus.IsVoteInProgress())
1472 return pContext->ThrowNativeError("No vote is in progress");
1475 if (!g_Menus.IsClientInVotePool(client))
1477 return pContext->ThrowNativeError("Client is not in the voting pool");
1481 if (params[0] >= 2 && !params[2])
1486 return g_Menus.RedrawClientVoteMenu2(client, revote) ? 1 : 0;
1489 class EmptyMenuHandler : public IMenuHandler
1492 } s_EmptyMenuHandler;
1494 static cell_t InternalShowMenu(IPluginContext *pContext, const cell_t *params)
1496 int client = params[1];
1497 CPlayer *pPlayer = g_Players.GetPlayerByIndex(client);
1499 if (pPlayer == NULL)
1501 return pContext->ThrowNativeError("Invalid client index %d", client);
1503 else if (!pPlayer->IsInGame())
1505 return pContext->ThrowNativeError("Client %d is not in game", client);
1508 if (!g_RadioMenuStyle.IsSupported())
1510 return pContext->ThrowNativeError("Radio menus are not supported on this mod");
1514 pContext->LocalToString(params[2], &str);
1516 IMenuPanel *pPanel = g_RadioMenuStyle.MakeRadioDisplay(str, params[4]);
1523 IMenuHandler *pHandler;
1524 CPanelHandler *pActualHandler = NULL;
1525 if (params[5] != -1)
1527 IPluginFunction *pFunction = pContext->GetFunctionById(params[5]);
1528 if (pFunction == NULL)
1530 return pContext->ThrowNativeError("Invalid function index %x", params[5]);
1532 pActualHandler = g_MenuHelpers.GetPanelHandler(pFunction);
1535 if (pActualHandler == NULL)
1537 pHandler = &s_EmptyMenuHandler;
1541 pHandler = pActualHandler;
1544 bool bSuccess = pPanel->SendDisplay(client, pHandler, params[3]);
1546 pPanel->DeleteThis();
1548 if (!bSuccess && pActualHandler != NULL)
1550 g_MenuHelpers.FreePanelHandler(pActualHandler);
1553 return bSuccess ? 1 : 0;
1556 REGISTER_NATIVES(menuNatives)
1558 {"AddMenuItem", AddMenuItem},
1559 {"CanPanelDrawFlags", CanPanelDrawFlags},
1560 {"CancelClientMenu", CancelClientMenu},
1561 {"CancelMenu", CancelMenu},
1562 {"CancelVote", CancelVote},
1563 {"CheckVoteDelay", CheckVoteDelay},
1564 {"CreateMenu", CreateMenu},
1565 {"CreateMenuEx", CreateMenuEx},
1566 {"CreatePanel", CreatePanel},
1567 {"CreatePanelFromMenu", CreatePanelFromMenu},
1568 {"DisplayMenu", DisplayMenu},
1569 {"DisplayMenuAtItem", DisplayMenuAtItem},
1570 {"DrawPanelItem", DrawPanelItem},
1571 {"DrawPanelText", DrawPanelText},
1572 {"GetClientMenu", GetClientMenu},
1573 {"GetMaxPageItems", GetMaxPageItems},
1574 {"GetMenuExitBackButton", GetMenuExitBackButton},
1575 {"GetMenuExitButton", GetMenuExitButton},
1576 {"GetMenuItem", GetMenuItem},
1577 {"GetMenuItemCount", GetMenuItemCount},
1578 {"GetMenuOptionFlags", GetMenuOptionFlags},
1579 {"GetMenuPagination", GetMenuPagination},
1580 {"GetMenuSelectionPosition",GetMenuSelectionPosition},
1581 {"GetMenuStyle", GetMenuStyle},
1582 {"GetMenuStyleHandle", GetMenuStyleHandle},
1583 {"GetMenuTitle", GetMenuTitle},
1584 {"GetPanelTextRemaining", GetPanelTextRemaining},
1585 {"GetPanelCurrentKey", GetPanelCurrentKey},
1586 {"GetPanelStyle", GetPanelStyle},
1587 {"InsertMenuItem", InsertMenuItem},
1588 {"InternalShowMenu", InternalShowMenu},
1589 {"IsClientInVotePool", IsClientInVotePool},
1590 {"IsVoteInProgress", IsVoteInProgress},
1591 {"RedrawClientVoteMenu", RedrawClientVoteMenu},
1592 {"RedrawMenuItem", RedrawMenuItem},
1593 {"RemoveAllMenuItems", RemoveAllMenuItems},
1594 {"RemoveMenuItem", RemoveMenuItem},
1595 {"SendPanelToClient", SendPanelToClient},
1596 {"SetMenuExitBackButton", SetMenuExitBackButton},
1597 {"SetMenuExitButton", SetMenuExitButton},
1598 {"SetMenuOptionFlags", SetMenuOptionFlags},
1599 {"SetMenuPagination", SetMenuPagination},
1600 {"SetMenuTitle", SetMenuTitle},
1601 {"SetPanelCurrentKey", SetPanelCurrentKey},
1602 {"SetPanelTitle", SetPanelTitle},
1603 {"SetPanelKeys", SetPanelKeys},
1604 {"SetVoteResultCallback", SetVoteResultCallback},
1605 {"VoteMenu", VoteMenu},
1606 {"SetMenuNoVoteButton", SetMenuNoVoteButton},