diff --git a/core/ConCmdManager.cpp b/core/ConCmdManager.cpp index 77d43e9b..6bf92178 100644 --- a/core/ConCmdManager.cpp +++ b/core/ConCmdManager.cpp @@ -42,6 +42,7 @@ ConCmdManager::ConCmdManager() : m_Strings(1024) m_pExecCmd = NULL; m_pServerCfgFile = NULL; m_pServerCfgFwd = NULL; + m_CmdClient = 0; } ConCmdManager::~ConCmdManager() diff --git a/core/ConCmdManager.h b/core/ConCmdManager.h index bc6ccff4..dbfcd0d5 100644 --- a/core/ConCmdManager.h +++ b/core/ConCmdManager.h @@ -111,6 +111,11 @@ private: void RemoveConCmds(List &cmdlist, IPluginContext *pContext); bool CheckAccess(int client, const char *cmd, AdminCmdInfo *pAdmin); void OnExecCmd(); +public: + inline int GetCommandClient() + { + return m_CmdClient; + } private: Trie *m_pCmds; /* command lookup */ Trie *m_pCmdGrps; /* command group lookup */ diff --git a/core/MenuManager.cpp b/core/MenuManager.cpp new file mode 100644 index 00000000..9a2f4468 --- /dev/null +++ b/core/MenuManager.cpp @@ -0,0 +1,676 @@ +#include +#include +#include "MenuManager.h" +#include "sm_stringutil.h" +#include "sourcemm_api.h" +#include "PlayerManager.h" +#include "MenuStyle_Valve.h" + +MenuManager g_Menus; + +/************************************* + ************************************* + **** BROADCAST HANDLING WRAPPERS **** + ************************************* + *************************************/ + +BroadcastHandler::BroadcastHandler(IMenuHandler *handler) : m_pHandler(handler), numClients(0) +{ +} + +unsigned int BroadcastHandler::GetMenuAPIVersion2() +{ + return m_pHandler->GetMenuAPIVersion2(); +} + +void BroadcastHandler::OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason) +{ + m_pHandler->OnMenuCancel(menu, client, reason); +} + +void BroadcastHandler::OnMenuDisplay(IBaseMenu *menu, int client, IMenuDisplay *display) +{ + numClients++; + m_pHandler->OnMenuDisplay(menu, client, display); +} + +void BroadcastHandler::OnBroadcastEnd(IBaseMenu *menu) +{ + g_Menus.FreeBroadcastHandler(this); +} + +void BroadcastHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int item) +{ + m_pHandler->OnMenuSelect(menu, client, item); +} + +void BroadcastHandler::OnMenuEnd(IBaseMenu *menu) +{ + assert(numClients > 0); + + /* Only fire if all clients have gotten a menu end */ + if (--numClients == 0) + { + IMenuHandler *pHandler = m_pHandler; + OnBroadcastEnd(menu); + pHandler->OnMenuEnd(menu); + } +} + +VoteHandler::VoteHandler(IMenuVoteHandler *handler) +: BroadcastHandler(handler), m_pVoteHandler(handler) +{ +} + +void VoteHandler::Initialize(IBaseMenu *menu) +{ + unsigned int numItems = menu->GetItemCount(); + + if (m_counts.size() >= numItems) + { + for (size_t i=0; i m_counts[highest]) + { + m_ties.clear(); + highest = i; + } else if (m_counts[i] == m_counts[highest]) { + m_ties.push_back(i); + } + } + + if (m_ties.size()) + { + m_ties.push_back(highest); + srand(static_cast(time(NULL))); + highest = m_ties[rand() % m_ties.size()]; + } + + m_pVoteHandler->OnMenuVoteEnd(menu, highest); + + g_Menus.FreeVoteHandler(this); +} + +/******************************* + ******************************* + ******** MENU MANAGER ********* + ******************************* + *******************************/ + +MenuManager::MenuManager() +{ + m_ShowMenu = -1; + m_pDefaultStyle = NULL; +} + +void MenuManager::OnSourceModAllInitialized() +{ + int num = g_SMAPI->GetUserMessageCount(); + if (num >= 1) + { + for (int i=0; iGetUserMessage(i, NULL), "ShowMenu") == 0) + { + m_ShowMenu = i; + break; + } + } + } + + /* :TODO: styles */ + m_Styles.push_back(&g_ValveMenuStyle); + SetDefaultStyle(&g_ValveMenuStyle); +} + +void MenuManager::OnSourceModAllShutdown() +{ + while (!m_BroadcastHandlers.empty()) + { + delete m_BroadcastHandlers.front(); + m_BroadcastHandlers.pop(); + } + + while (!m_VoteHandlers.empty()) + { + delete m_VoteHandlers.front(); + m_VoteHandlers.pop(); + } +} + +bool MenuManager::SetDefaultStyle(IMenuStyle *style) +{ + if (!style) + { + return false; + } + + m_pDefaultStyle = style; + + return true; +} + +IMenuStyle *MenuManager::GetStyle(unsigned int index) +{ + if (index >= GetStyleCount()) + { + return NULL; + } + + return m_Styles[index]; +} + +unsigned int MenuManager::GetStyleCount() +{ + return (unsigned int)m_Styles.size(); +} + +IMenuStyle *MenuManager::FindStyleByName(const char *name) +{ + unsigned int count = GetStyleCount(); + for (unsigned int i=0; iGetStyleName(), name) == 0) + { + return ptr; + } + } + + return NULL; +} + +inline bool IsSlotItem(IMenuDisplay *display, + unsigned int style) +{ + if (!display->CanDrawItem(style)) + { + return false; + } + if ((style & ITEMDRAW_IGNORE) == ITEMDRAW_IGNORE) + { + return false; + } + if (style & ITEMDRAW_RAWLINE) + { + return false; + } + + return true; +} + +IMenuDisplay *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder order) +{ + IBaseMenu *menu = md.menu; + + if (!menu) + { + return NULL; + } + + struct + { + unsigned int position; + ItemDrawInfo draw; + } drawItems[10]; + + /* Figure out how many items to draw */ + IMenuStyle *style = menu->GetDrawStyle(); + unsigned int pgn = menu->GetPagination(); + unsigned int maxItems = style->GetMaxPageItems(); + if (pgn != MENU_NO_PAGINATION) + { + maxItems = pgn; + } + + unsigned int totalItems = menu->GetItemCount(); + unsigned int startItem = 0; + + /* For pagination, find the starting point. */ + if (pgn != MENU_NO_PAGINATION) + { + if (order == ItemOrder_Ascending) + { + startItem = md.lastItem; + /* This shouldn't happen with well-coded menus. + * If the item is out of bounds, switch the order to + * Items_Descending and make us start from the top. + */ + if (startItem >= totalItems) + { + startItem = totalItems - 1; + order = ItemOrder_Descending; + } + } else if (order == ItemOrder_Descending) { + startItem = md.firstItem; + /* This shouldn't happen with well-coded menus. + * If searching backwards doesn't give us enough room, + * start from the beginning and change to ascending. + */ + if (startItem <= maxItems) + { + startItem = 0; + order = ItemOrder_Ascending; + } + } + } + + /* Get our Display pointer and initialize some crap */ + IMenuDisplay *display = menu->CreateDisplay(); + IMenuHandler *mh = md.mh; + bool foundExtra = false; + unsigned int extraItem = 0; + + /** + * We keep searching until: + * 1) There are no more items + * 2) We reach one OVER the maximum number of slot items + * 3) We have reached maxItems and pagination is MENU_NO_PAGINATION + */ + unsigned int i = startItem; + unsigned int foundItems = 0; + while (true) + { + ItemDrawInfo &dr = drawItems[foundItems].draw; + /* Is the item valid? */ + if (menu->GetItemInfo(i, &dr) != NULL) + { + /* Ask the user to change the style, if necessary */ + mh->OnMenuDrawItem(menu, client, i, dr.style); + /* Check if it's renderable */ + if (IsSlotItem(display, dr.style)) + { + /* If we've already found the max number of items, + * This means we should just cancel out and log our + * "last item." + */ + if (foundItems >= maxItems) + { + foundExtra = true; + extraItem = i; + break; + } + drawItems[foundItems++].position = i; + } + } + /* If there's no pagination, stop once the menu is full. */ + if (pgn == MENU_NO_PAGINATION) + { + /* If we've filled up, then stop */ + if (foundItems >= maxItems) + { + break; + } + } + /* If we're descending and this is the first item, stop */ + if (order == ItemOrder_Descending) + { + if (i == 0) + { + break; + } + i--; + } + /* If we're ascending and this is the last item, stop */ + else if (order == ItemOrder_Ascending) + { + if (i >= totalItems - 1) + { + break; + } + i++; + } + } + + /* There were no items to draw! */ + if (!foundItems) + { + delete display; + return NULL; + } + + /* Check initial buttons */ + bool displayPrev = false; + bool displayNext = false; + if (foundExtra) + { + if (order == ItemOrder_Descending) + { + displayPrev = true; + md.firstItem = extraItem; + } else if (order == ItemOrder_Ascending) { + displayNext = true; + md.lastItem = extraItem; + } + } + + /** + * If we're paginated, we have to find if there is another page. + * Sadly, the only way to do this is to try drawing more items! + */ + if (pgn != MENU_NO_PAGINATION) + { + unsigned int lastItem = 0; + ItemDrawInfo dr; + /* Find the last feasible item to search from. */ + if (order == ItemOrder_Descending) + { + lastItem = drawItems[0].position; + if (lastItem >= totalItems - 1) + { + goto skip_search; + } + while (++lastItem < totalItems) + { + if (menu->GetItemInfo(lastItem, &dr) != NULL) + { + mh->OnMenuDrawItem(menu, client, lastItem, dr.style); + if (IsSlotItem(display, dr.style)) + { + displayNext = true; + md.lastItem = lastItem; + break; + } + } + } + } else if (order == ItemOrder_Ascending) { + lastItem = drawItems[0].position; + if (lastItem == 0) + { + goto skip_search; + } + lastItem--; + while (lastItem-- != 0) + { + if (menu->GetItemInfo(lastItem, &dr) != NULL) + { + mh->OnMenuDrawItem(menu, client, lastItem, dr.style); + if (IsSlotItem(display, dr.style)) + { + displayPrev = true; + md.firstItem = lastItem; + break; + } + } + } + } + } +skip_search: + + /* Draw the item according to the order */ + menu_slots_t *slots = md.slots; + unsigned int position = 0; /* Keep track of the last position */ + if (order == ItemOrder_Ascending) + { + for (unsigned int i=0; iGetItemInfo(drawItems[i].position, &dr); + mh->OnMenuDisplayItem(menu, client, drawItems[i].position, &(dr.display)); + if ((position = display->DrawItem(dr)) != 0) + { + slots[position].item = drawItems[i].position; + slots[position].type = ItemSel_Item; + } + } + } else if (order == ItemOrder_Descending) { + unsigned int i = foundItems; + /* NOTE: There will always be at least one item because + * of the check earlier. + */ + while (i--) + { + ItemDrawInfo &dr = drawItems[i].draw; + menu->GetItemInfo(drawItems[i].position, &dr); + mh->OnMenuDisplayItem(menu, client, drawItems[i].position, &(dr.display)); + if ((position = display->DrawItem(dr)) != 0) + { + slots[position].item = drawItems[i].position; + slots[position].type = ItemSel_Item; + } + } + } + + /* Now, we need to check if we need to add anything extra */ + if (pgn != MENU_NO_PAGINATION) + { + bool canDrawDisabled = display->CanDrawItem(ITEMDRAW_DISABLED); + bool exitButton = menu->GetExitButton(); + char text[50]; + + /* Calculate how many items we are allowed for control stuff */ + unsigned int padding = style->GetMaxPageItems() - maxItems; + + /* Add the number of available slots */ + padding += (maxItems - foundItems); + + /* Someday, if we are able to re-enable this, we will be very lucky men. */ +#if 0 + if (!style->FeatureExists(MenuStyleFeature_ImplicitExit)) + { +#endif + /* Even if we don't draw an exit button, we invalidate the slot. */ + padding--; +#if 0 + } else { + /* Otherwise, we don't draw anything and leave the slot available */ + exitButton = false; + } +#endif + + /* Subtract two slots for the displayNext/displayPrev padding */ + padding -= 2; + + /** + * We allow next/prev to be undrawn if neither exists. + * Thus, we only need padding if one of them will be drawn, + * or the exit button will be drawn. + */ + ItemDrawInfo padItem(NULL, ITEMDRAW_SPACER); + if (exitButton || (displayNext || displayPrev)) + { + /* Add spacers so we can pad to the end */ + unsigned int null_pos = 0; + for (unsigned int i=0; iDrawItem(padItem); + slots[position].type = ItemSel_None; + } + } + ItemDrawInfo dr(text, 0); + + /* PREVIOUS */ + if (displayPrev || canDrawDisabled) + { + CorePlayerTranslate(client, text, sizeof(text), "Back", NULL); + dr.style = displayPrev ? 0 : ITEMDRAW_DISABLED; + position = display->DrawItem(dr); + slots[position].type = ItemSel_Back; + } else if ((displayNext || canDrawDisabled) || exitButton) { + /* If we can't display this, + * but there is a "next" or "exit" button, we need to pad! + */ + position = display->DrawItem(padItem); + slots[position].type = ItemSel_None; + } + + /* NEXT */ + if (displayNext || canDrawDisabled) + { + CorePlayerTranslate(client, text, sizeof(text), "Next", NULL); + dr.style = displayNext ? 0 : ITEMDRAW_DISABLED; + position = display->DrawItem(dr); + slots[position].type = ItemSel_Next; + } else if (exitButton) { + /* If we can't display this, + * but there is an exit button, we need to pad! + */ + position = display->DrawItem(padItem); + slots[position].type = ItemSel_None; + } + + /* EXIT */ + if (exitButton) + { + CorePlayerTranslate(client, text, sizeof(text), "Exit", NULL); + dr.style = 0; + position = display->DrawItem(dr); + slots[position].type = ItemSel_Exit; + } + } + + /* Lastly, fill in any slots we could have missed */ + for (unsigned int i = position + 1; i < 10; i++) + { + slots[i].type = ItemSel_None; + } + + /* Do title stuff */ + mh->OnMenuDisplay(menu, client, display); + display->DrawTitle(menu->GetDefaultTitle(), true); + + return display; +} + +unsigned int MenuManager::BroadcastMenu(IBaseMenu *menu, + IMenuHandler *handler, + int clients[], + unsigned int numClients, + unsigned int time) +{ + BroadcastHandler *bh; + + if (m_BroadcastHandlers.empty()) + { + bh = new BroadcastHandler(handler); + } else { + bh = m_BroadcastHandlers.front(); + m_BroadcastHandlers.pop(); + bh->m_pHandler = handler; + bh->numClients = 0; + } + + handler->OnMenuStart(menu); + + unsigned int total = 0; + for (unsigned int i=0; iDisplay(clients[i], bh, time)) + { + continue; + } + + /* :TODO: Allow sourcetv only, not all bots */ + CPlayer *player = g_Players.GetPlayerByIndex(clients[i]); + if (player->IsFakeClient()) + { + continue; + } + + total++; + } + + if (!total) + { + /* End the broadcast here */ + handler->OnMenuEnd(menu); + FreeBroadcastHandler(bh); + } + + return total; +} + +unsigned int MenuManager::VoteMenu(IBaseMenu *menu, + IMenuVoteHandler *handler, + int clients[], + unsigned int numClients, + unsigned int time) +{ + VoteHandler *vh; + + if (m_VoteHandlers.empty()) + { + vh = new VoteHandler(handler); + } else { + vh = m_VoteHandlers.front(); + m_VoteHandlers.pop(); + vh->m_pHandler = handler; + vh->numClients = 0; + } + + vh->Initialize(menu); + handler->OnMenuStart(menu); + + unsigned int total = 0; + for (unsigned int i=0; iDisplay(clients[i], vh, time)) + { + continue; + } + + /* :TODO: Allow sourcetv only, not all bots */ + CPlayer *player = g_Players.GetPlayerByIndex(clients[i]); + if (player->IsFakeClient()) + { + continue; + } + + total++; + } + + if (!total) + { + /* End the broadcast here */ + handler->OnMenuEnd(menu); + FreeVoteHandler(vh); + } + + return total; +} + +void MenuManager::FreeBroadcastHandler(BroadcastHandler *bh) +{ + m_BroadcastHandlers.push(bh); +} + +void MenuManager::FreeVoteHandler(VoteHandler *vh) +{ + m_VoteHandlers.push(vh); +} + +IMenuStyle *MenuManager::GetDefaultStyle() +{ + return m_pDefaultStyle; +} + diff --git a/core/MenuManager.h b/core/MenuManager.h new file mode 100644 index 00000000..ebe70320 --- /dev/null +++ b/core/MenuManager.h @@ -0,0 +1,110 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_SOURCEMOD_MENUMANAGER_H_ +#define _INCLUDE_SOURCEMOD_MENUMANAGER_H_ + +#include +#include +#include +#include +#include "sm_memtable.h" +#include "sm_globals.h" + +using namespace SourceMod; +using namespace SourceHook; + +class BroadcastHandler : public IMenuHandler +{ +public: + BroadcastHandler(IMenuHandler *handler); +public: //IMenuHandler + void OnMenuDisplay(IBaseMenu *menu, int client, IMenuDisplay *display); + void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item); + void OnMenuEnd(IBaseMenu *menu); + void OnMenuCancel(IBaseMenu *menu, int client, MenuCancelReason reason); + unsigned int GetMenuAPIVersion2(); +public: + virtual void OnBroadcastEnd(IBaseMenu *menu); +public: + IMenuHandler *m_pHandler; + unsigned int numClients; +}; + +class VoteHandler : public BroadcastHandler +{ +public: + VoteHandler(IMenuVoteHandler *handler); +public: + void Initialize(IBaseMenu *menu); + void OnMenuSelect(IBaseMenu *menu, int client, unsigned int item); + void OnBroadcastEnd(IBaseMenu *menu); +private: + CVector m_counts; + CVector m_ties; + unsigned int numItems; + IMenuVoteHandler *m_pVoteHandler; +}; + +class MenuManager : + public IMenuManager, + public SMGlobalClass +{ + friend class BroadcastHandler; + friend class VoteHandler; +public: + MenuManager(); +public: //SMGlobalClass + void OnSourceModAllInitialized(); + void OnSourceModAllShutdown(); +public: //IMenuManager + virtual const char *GetInterfaceName() + { + return SMINTERFACE_MENUMANAGER_NAME; + } + virtual unsigned int GetInterfaceVersion() + { + return SMINTERFACE_MENUMANAGER_VERSION; + } +public: + unsigned int GetStyleCount(); + IMenuStyle *GetStyle(unsigned int index); + IMenuStyle *FindStyleByName(const char *name); + unsigned int BroadcastMenu(IBaseMenu *menu, + IMenuHandler *handler, + int clients[], + unsigned int numClients, + unsigned int time); + unsigned int VoteMenu(IBaseMenu *menu, + IMenuVoteHandler *handler, + int clients[], + unsigned int numClients, + unsigned int time); + IMenuStyle *GetDefaultStyle(); + bool SetDefaultStyle(IMenuStyle *style); + IMenuDisplay *RenderMenu(int client, menu_states_t &states, ItemOrder order); +protected: + void FreeBroadcastHandler(BroadcastHandler *bh); + void FreeVoteHandler(VoteHandler *vh); +private: + int m_ShowMenu; + IMenuStyle *m_pDefaultStyle; + CStack m_BroadcastHandlers; + CStack m_VoteHandlers; + CVector m_Styles; +}; + +extern MenuManager g_Menus; + +#endif //_INCLUDE_SOURCEMOD_MENUMANAGER_H_ diff --git a/core/MenuStyle_Base.cpp b/core/MenuStyle_Base.cpp new file mode 100644 index 00000000..79023589 --- /dev/null +++ b/core/MenuStyle_Base.cpp @@ -0,0 +1,162 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ +#include +#include "sm_stringutil.h" +#include "MenuStyle_Base.h" + +CBaseMenu::CBaseMenu(IMenuStyle *pStyle) : +m_pStyle(pStyle), m_Strings(512), m_Pagination(7), m_ExitButton(true) +{ +} + +CBaseMenu::~CBaseMenu() +{ +} + +bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw) +{ + if (m_Pagination == MENU_NO_PAGINATION + && m_items.size() >= m_pStyle->GetMaxPageItems()) + { + return false; + } + + CItem item; + + item.infoString = m_Strings.AddString(info); + if (draw.display) + { + item.displayString = m_Strings.AddString(draw.display); + } + item.style = draw.style; + + + m_items.push_back(item); + + return true; +} + +bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw) +{ + if (m_Pagination == MENU_NO_PAGINATION + && m_items.size() >= m_pStyle->GetMaxPageItems()) + { + return false; + } + + if (position >= m_items.size()) + { + return false; + } + + CItem item; + item.infoString = m_Strings.AddString(info); + if (draw.display) + { + item.displayString = m_Strings.AddString(draw.display); + } + item.style = draw.style; + + CVector::iterator iter = m_items.iterAt(position); + m_items.insert(iter, item); + + return true; +} + +bool CBaseMenu::RemoveItem(unsigned int position) +{ + if (position >= m_items.size()) + { + return false; + } + + m_items.erase(m_items.iterAt(position)); + + if (m_items.size() == 0) + { + m_Strings.Reset(); + } + + return true; +} + +void CBaseMenu::RemoveAllItems() +{ + m_items.clear(); + m_Strings.Reset(); +} + +const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */) +{ + if (position >= m_items.size()) + { + return NULL; + } + + if (draw) + { + draw->display = m_Strings.GetString(m_items[position].displayString); + draw->style = m_items[position].style; + } + + return m_Strings.GetString(m_items[position].infoString); +} + +unsigned int CBaseMenu::GetItemCount() +{ + return m_items.size(); +} + +bool CBaseMenu::SetPagination(unsigned int itemsPerPage) +{ + if (itemsPerPage > 7) + { + return false; + } + + m_Pagination = itemsPerPage; + + return true; +} + +unsigned int CBaseMenu::GetPagination() +{ + return m_Pagination; +} + +IMenuStyle *CBaseMenu::GetDrawStyle() +{ + return m_pStyle; +} + +void CBaseMenu::SetDefaultTitle(const char *message) +{ + m_Title.assign(message); +} + +const char *CBaseMenu::GetDefaultTitle() +{ + return m_Title.c_str(); +} + +bool CBaseMenu::GetExitButton() +{ + return m_ExitButton; +} + +bool CBaseMenu::SetExitButton(bool set) +{ + m_ExitButton = set; + return true; +} diff --git a/core/MenuStyle_Base.h b/core/MenuStyle_Base.h new file mode 100644 index 00000000..fdfa8c29 --- /dev/null +++ b/core/MenuStyle_Base.h @@ -0,0 +1,69 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_MENUSTYLE_BASE_H +#define _INCLUDE_MENUSTYLE_BASE_H + +#include +#include +#include +#include "sm_memtable.h" + +using namespace SourceMod; +using namespace SourceHook; + +class CItem +{ +public: + CItem() + { + infoString = -1; + displayString = -1; + style = 0; + } +public: + int infoString; + int displayString; + unsigned int style; +}; + +class CBaseMenu : public IBaseMenu +{ +public: + CBaseMenu(IMenuStyle *pStyle); + virtual ~CBaseMenu(); +public: + virtual bool AppendItem(const char *info, const ItemDrawInfo &draw); + virtual bool InsertItem(unsigned int position, const char *info, const ItemDrawInfo &draw); + virtual bool RemoveItem(unsigned int position); + virtual void RemoveAllItems(); + virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw=NULL); + virtual unsigned int GetItemCount(); + virtual bool SetPagination(unsigned int itemsPerPage); + virtual unsigned int GetPagination(); + virtual IMenuStyle *GetDrawStyle(); + virtual void SetDefaultTitle(const char *message); + virtual const char *GetDefaultTitle(); + virtual bool GetExitButton(); + virtual bool SetExitButton(bool set); +protected: + String m_Title; + IMenuStyle *m_pStyle; + unsigned int m_Pagination; + CVector m_items; + BaseStringTable m_Strings; + bool m_ExitButton; +}; + +#endif //_INCLUDE_MENUSTYLE_BASE_H diff --git a/core/MenuStyle_Valve.cpp b/core/MenuStyle_Valve.cpp new file mode 100644 index 00000000..c507f39f --- /dev/null +++ b/core/MenuStyle_Valve.cpp @@ -0,0 +1,796 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#include "sm_stringutil.h" +#include "PlayerManager.h" +#include "MenuStyle_Valve.h" +#include "Translator.h" +#include "PlayerManager.h" +#include "ConCmdManager.h" + +SH_DECL_HOOK4_void(IServerPluginHelpers, CreateMessage, SH_NOATTRIB, false, edict_t *, DIALOG_TYPE, KeyValues *, IServerPluginCallbacks *); + +ValveMenuStyle g_ValveMenuStyle; +const char *g_OptionNumTable[]; +const char *g_OptionCmdTable[]; +IServerPluginCallbacks *g_pVSPHandle = NULL; +CallClass *g_pSPHCC = NULL; + +class TestHandler : public IMenuHandler +{ +public: + virtual void OnMenuEnd(IBaseMenu *menu) + { + menu->Destroy(); + } +}; + +ValveMenuStyle::ValveMenuStyle() : m_players(new CValveMenuPlayer[256+1]), m_WatchList(256) +{ +} + +bool ValveMenuStyle::OnClientCommand(int client) +{ + const char *cmd = engine->Cmd_Argv(0); + + if (strcmp(cmd, "sm_vmenuselect") == 0) + { + int key_press = atoi(engine->Cmd_Argv(1)); + g_ValveMenuStyle.ClientPressedKey(client, key_press); + return true; + } + + if (strcmp(cmd, "sm_test") == 0) + { + IBaseMenu *menu = g_ValveMenuStyle.CreateMenu(); + menu->AppendItem("test1", ItemDrawInfo("Test #1", 0)); + menu->AppendItem("test2", ItemDrawInfo("Test #2", 0)); + menu->AppendItem("test3", ItemDrawInfo("Test #3", 0)); + menu->AppendItem("test4", ItemDrawInfo("Test #4", 0)); + menu->AppendItem("test5", ItemDrawInfo("Test #5", 0)); + menu->AppendItem("test6", ItemDrawInfo("Test #6", 0)); + menu->AppendItem("test7", ItemDrawInfo("Test #7", 0)); + menu->AppendItem("test8", ItemDrawInfo("Test #8", 0)); + menu->AppendItem("test9", ItemDrawInfo("Test #9", 0)); + menu->AppendItem("test10", ItemDrawInfo("Test #10", 0)); + menu->AppendItem("test11", ItemDrawInfo("Test #11", 0)); + menu->AppendItem("test12", ItemDrawInfo("Test #12", 0)); + menu->AppendItem("test13", ItemDrawInfo("Test #13", 0)); + menu->AppendItem("test14", ItemDrawInfo("Test #14", 0)); + menu->AppendItem("test15", ItemDrawInfo("Test #15", 0)); + menu->AppendItem("test16", ItemDrawInfo("Test #16", 0)); + menu->AppendItem("test17", ItemDrawInfo("Test #17", 0)); + menu->AppendItem("test18", ItemDrawInfo("Test #18", 0)); + menu->AppendItem("test19", ItemDrawInfo("Test #19", 0)); + menu->AppendItem("test20", ItemDrawInfo("Test #20", 0)); + + menu->Display(client, new TestHandler, 20); + return true; + } else if (strcmp(cmd, "gaben") == 0) { + KeyValues *kv = new KeyValues("menu"); + kv->SetString("msg", "hi"); + serverpluginhelpers->CreateMessage(engine->PEntityOfEntIndex(client), DIALOG_MENU, kv, g_pVSPHandle); + kv->deleteThis(); + } + + return false; +} + +void ValveMenuStyle::OnSourceModAllInitialized() +{ + g_Players.AddClientListener(this); + SH_ADD_HOOK_MEMFUNC(IServerPluginHelpers, CreateMessage, serverpluginhelpers, this, &ValveMenuStyle::HookCreateMessage, false); + g_pSPHCC = SH_GET_CALLCLASS(serverpluginhelpers); +} + +void ValveMenuStyle::OnSourceModShutdown() +{ + SH_RELEASE_CALLCLASS(g_pSPHCC); + SH_REMOVE_HOOK_MEMFUNC(IServerPluginHelpers, CreateMessage, serverpluginhelpers, this, &ValveMenuStyle::HookCreateMessage, false); + g_Players.RemoveClientListener(this); +} + +void ValveMenuStyle::OnClientDisconnected(int client) +{ + CValveMenuPlayer *player = &m_players[client]; + if (!player->bInMenu) + { + return; + } + + menu_states_t &states = player->states; + states.mh->OnMenuCancel(states.menu, client, MenuCancel_Disconnect); + states.mh->OnMenuEnd(states.menu); + + if (player->menuHoldTime) + { + m_WatchList.remove(client); + } + + player->bInMenu = false; +} + +void ValveMenuStyle::HookCreateMessage(edict_t *pEdict, + DIALOG_TYPE type, + KeyValues *kv, + IServerPluginCallbacks *plugin) +{ + if (type != DIALOG_MENU) + { + return; + } + + int client = engine->IndexOfEdict(pEdict); + if (client < 1 || client > 256) + { + return; + } + + CValveMenuPlayer *player = &m_players[client]; + + /* We don't care if the player is in a menu because, for all intents and purposes, + * the menu is completely private. Instead, we just figure out the level we'll need + * in order to override it. + */ + player->curPrioLevel = kv->GetInt("level", player->curPrioLevel); + + /* Oh no! What happens if we got a menu that overwrites ours?! */ + if (player->bInMenu) + { + /* Okay, let the external menu survive for now. It may live another + * day to avenge its grandfather, killed in the great Menu Interruption + * battle. + */ + _CancelMenu(client, true); + } +} + +void ValveMenuStyle::OnSourceModVSPReceived(IServerPluginCallbacks *iface) +{ + g_pVSPHandle = iface; +} + +IMenuDisplay *ValveMenuStyle::CreateDisplay() +{ + return new CValveMenuDisplay(); +} + +IBaseMenu *ValveMenuStyle::CreateMenu() +{ + return new CValveMenu(); +} + +const char *ValveMenuStyle::GetStyleName() +{ + return "valve"; +} + +unsigned int ValveMenuStyle::GetMaxPageItems() +{ + return 8; +} + +static int do_lookup[256]; +void ValveMenuStyle::ProcessWatchList() +{ + if (!m_WatchList.size()) + { + return; + } + + FastLink::iterator iter; + + unsigned int total = 0; + for (iter=m_WatchList.begin(); iter!=m_WatchList.end(); ++iter) + { + do_lookup[total++] = (*iter); + } + + int client; + CValveMenuPlayer *player; + float curTime = gpGlobals->curtime; + for (unsigned int i=0; ibInMenu || !player->menuHoldTime) + { + m_WatchList.remove(i); + continue; + } + if (curTime > player->menuStartTime + player->menuHoldTime) + { + _CancelMenu(i, false); + } + } +} + +void ValveMenuStyle::_CancelMenu(int client, bool bAutoIgnore) +{ + CValveMenuPlayer *player = &m_players[client]; + menu_states_t &states = player->states; + + bool bOldIgnore = player->bAutoIgnore; + if (bAutoIgnore) + { + player->bAutoIgnore = true; + } + + /* Save states */ + IMenuHandler *mh = states.mh; + IBaseMenu *menu = states.menu; + + /* Clear menu */ + player->bInMenu = false; + if (player->menuHoldTime) + { + m_WatchList.remove(client); + } + + /* Fire callbacks */ + mh->OnMenuCancel(menu, client, MenuCancel_Interrupt); + mh->OnMenuEnd(menu); + + if (bAutoIgnore) + { + player->bAutoIgnore = bOldIgnore; + } +} + +void ValveMenuStyle::CancelMenu(CValveMenu *menu) +{ + int maxClients = g_Players.GetMaxClients(); + for (int i=1; i<=maxClients; i++) + { + if (m_players[i].bInMenu) + { + menu_states_t &states = m_players[i].states; + if (states.menu == menu) + { + _CancelMenu(i); + } + } + } +} + +bool ValveMenuStyle::CancelClientMenu(int client, bool autoIgnore) +{ + if (client < 1 || client > 256 || !m_players[client].bInMenu) + { + return false; + } + + _CancelMenu(client, autoIgnore); + + return true; +} + + +void ValveMenuStyle::ClientPressedKey(int client, unsigned int key_press) +{ + CValveMenuPlayer *player = &m_players[client]; + + /* First question: Are we in a menu? */ + if (!player->bInMenu) + { + return; + } + + bool cancel = false; + unsigned int item = 0; + MenuCancelReason reason = MenuCancel_Exit; + menu_states_t &states = player->states; + + assert(states.mh != NULL); + + if (states.menu == NULL) + { + item = key_press; + } else if (key_press < 1 || key_press > 8) { + cancel = true; + } else { + ItemSelection type = states.slots[key_press].type; + + /* For navigational items, we're going to redisplay */ + if (type == ItemSel_Back) + { + if (!RedoClientMenu(client, ItemOrder_Descending)) + { + cancel = true; + reason = MenuCancel_NoDisplay; + } else { + return; + } + } else if (type == ItemSel_Next) { + if (!RedoClientMenu(client, ItemOrder_Ascending)) + { + cancel = true; /* I like Saltines. */ + reason = MenuCancel_NoDisplay; + } else { + return; + } + } else if (type == ItemSel_Exit || type == ItemSel_None) { + cancel = true; + } else { + item = states.slots[key_press].item; + } + } + + /* Save variables */ + IMenuHandler *mh = states.mh; + IBaseMenu *menu = states.menu; + + /* Clear states */ + player->bInMenu = false; + if (player->menuHoldTime) + { + m_WatchList.remove(client); + } + + if (cancel) + { + mh->OnMenuCancel(menu, client, reason); + } else { + mh->OnMenuSelect(menu, client, item); + } + + mh->OnMenuEnd(menu); +} + +bool ValveMenuStyle::DoClientMenu(int client, CValveMenuDisplay *menu, IMenuHandler *mh, unsigned int time) +{ + if (!g_pVSPHandle || !mh) + { + return false; + } + + CPlayer *pPlayer = g_Players.GetPlayerByIndex(client); + if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame()) + { + return false; + } + + CValveMenuPlayer *player = &m_players[client]; + if (player->bAutoIgnore) + { + return false; + } + + /* For the duration of this, we are going to totally ignore whether + * the player is already in a menu or not (except to cancel the old one). + * Instead, we are simply going to ignore any further menu displays, so + * this display can't be interrupted. + */ + player->bAutoIgnore = true; + + /* Cancel any old menus */ + menu_states_t &states = player->states; + if (player->bInMenu) + { + /* We need to cancel the old menu */ + if (player->menuHoldTime) + { + m_WatchList.remove(client); + } + states.mh->OnMenuCancel(states.menu, client, MenuCancel_Interrupt); + states.mh->OnMenuEnd(states.menu); + } + + states.firstItem = 0; + states.lastItem = 0; + states.menu = NULL; + states.mh = mh; + states.apiVers = SMINTERFACE_MENUMANAGER_VERSION; + player->curPrioLevel--; + player->bInMenu = true; + player->menuStartTime = gpGlobals->curtime; + player->menuHoldTime = time; + + if (time) + { + m_WatchList.push_back(client); + } + + /* Draw the display */ + menu->SendRawDisplay(client, player->curPrioLevel, time); + + /* We can be interrupted again! */ + player->bAutoIgnore = false; + + return true; +} + +bool ValveMenuStyle::DoClientMenu(int client, CValveMenu *menu, IMenuHandler *mh, unsigned int time) +{ + mh->OnMenuStart(menu); + + if (!g_pVSPHandle || !mh) + { + mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay); + mh->OnMenuEnd(menu); + return false; + } + + CPlayer *pPlayer = g_Players.GetPlayerByIndex(client); + if (!pPlayer || pPlayer->IsFakeClient() || !pPlayer->IsInGame()) + { + mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay); + mh->OnMenuEnd(menu); + return false; + } + + CValveMenuPlayer *player = &m_players[client]; + if (player->bAutoIgnore) + { + mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay); + mh->OnMenuEnd(menu); + return false; + } + + /* For the duration of this, we are going to totally ignore whether + * the player is already in a menu or not (except to cancel the old one). + * Instead, we are simply going to ignore any further menu displays, so + * this display can't be interrupted. + */ + player->bAutoIgnore = true; + + /* Cancel any old menus */ + menu_states_t &states = player->states; + if (player->bInMenu) + { + /* We need to cancel the old menu */ + if (player->menuHoldTime) + { + m_WatchList.remove(client); + } + states.mh->OnMenuCancel(states.menu, client, MenuCancel_Interrupt); + states.mh->OnMenuEnd(states.menu); + } + + states.firstItem = 0; + states.lastItem = 0; + states.menu = menu; + states.mh = mh; + states.apiVers = SMINTERFACE_MENUMANAGER_VERSION; + + IMenuDisplay *display = g_Menus.RenderMenu(client, states, ItemOrder_Ascending); + if (!display) + { + player->bAutoIgnore = false; + player->bInMenu = false; + mh->OnMenuCancel(menu, client, MenuCancel_NoDisplay); + mh->OnMenuEnd(menu); + return false; + } + + /* Finally, set our states */ + player->curPrioLevel--; + player->bInMenu = true; + player->menuStartTime = gpGlobals->curtime; + player->menuHoldTime = time; + + if (time) + { + m_WatchList.push_back(client); + } + + /* Draw the display */ + CValveMenuDisplay *vDisplay = (CValveMenuDisplay *)display; + vDisplay->SendRawDisplay(client, player->curPrioLevel, time); + + /* Free the display pointer */ + delete display; + + /* We can be interrupted again! */ + player->bAutoIgnore = false; + + return true; +} + +bool ValveMenuStyle::RedoClientMenu(int client, ItemOrder order) +{ + CValveMenuPlayer *player = &m_players[client]; + menu_states_t &states = player->states; + + player->bAutoIgnore = true; + IMenuDisplay *display = g_Menus.RenderMenu(client, states, order); + if (!display) + { + if (player->menuHoldTime) + { + m_WatchList.remove(client); + } + player->bAutoIgnore = false; + return false; + } + + CValveMenuDisplay *vDisplay = (CValveMenuDisplay *)display; + vDisplay->SendRawDisplay(client, --player->curPrioLevel, player->menuHoldTime); + + delete display; + + player->bAutoIgnore = false; + + return true; +} + +MenuSource ValveMenuStyle::GetClientMenu(int client, void **object) +{ + if (client < 1 || client > 256 || !m_players[client].bInMenu) + { + return MenuSource_None; + } + + IBaseMenu *menu; + if ((menu=m_players[client].states.menu) != NULL) + { + if (object) + { + *object = menu; + } + return MenuSource_BaseMenu; + } + + return MenuSource_Display; +} + +CValveMenuDisplay::CValveMenuDisplay() +{ + m_pKv = NULL; + Reset(); +} + +CValveMenuDisplay::CValveMenuDisplay(CValveMenu *pMenu) +{ + m_pKv = NULL; + Reset(); + m_pKv->SetColor("color", pMenu->m_IntroColor); + m_pKv->SetString("title", pMenu->m_IntroMsg); +} + +CValveMenuDisplay::~CValveMenuDisplay() +{ + m_pKv->deleteThis(); +} + +IMenuStyle *CValveMenuDisplay::GetParentStyle() +{ + return &g_ValveMenuStyle; +} + +void CValveMenuDisplay::Reset() +{ + if (m_pKv) + { + m_pKv->deleteThis(); + } + m_pKv = new KeyValues("menu"); + m_NextPos = 1; + m_TitleDrawn = false; +} + +bool CValveMenuDisplay::SetExtOption(MenuOption option, const void *valuePtr) +{ + if (option == MenuOption_IntroMessage) + { + m_pKv->SetString("title", (const char *)valuePtr); + return true; + } else if (option == MenuOption_IntroColor) { + int *array = (int *)valuePtr; + m_pKv->SetColor("color", Color(array[0], array[1], array[2], array[3])); + return true; + } + + return false; +} + +bool CValveMenuDisplay::CanDrawItem(unsigned int drawFlags) +{ + /** + * ITEMDRAW_RAWLINE - We can't draw random text, and this doesn't add a slot, + * so it's completely safe to ignore it. + * ----------------------------------------- + */ + if (drawFlags & ITEMDRAW_RAWLINE) + { + return false; + } + + /** + * Special cases, explained in DrawItem() + */ + if ((drawFlags & ITEMDRAW_NOTEXT) + || (drawFlags & ITEMDRAW_SPACER)) + { + return true; + } + + /** + * We can't draw disabled text. We could bump the position, but we + * want DirectDraw() to find some actual items to display. + */ + if (drawFlags & ITEMDRAW_DISABLED) + { + return false; + } + + return true; +} + +unsigned int CValveMenuDisplay::DrawItem(const ItemDrawInfo &item) +{ + if (m_NextPos > 9 || !CanDrawItem(item.style)) + { + return 0; + } + + /** + * For these cases we can't draw anything at all, but + * we can at least bump the position since we were explicitly asked to. + */ + if ((item.style & ITEMDRAW_NOTEXT) + || (item.style & ITEMDRAW_SPACER)) + { + return m_NextPos++; + } + + char buffer[255]; + UTIL_Format(buffer, sizeof(buffer), "%d. %s", m_NextPos, item.display); + + KeyValues *ki = m_pKv->FindKey(g_OptionNumTable[m_NextPos], true); + ki->SetString("command", g_OptionCmdTable[m_NextPos]); + ki->SetString("msg", buffer); + + return m_NextPos++; +} + +void CValveMenuDisplay::DrawTitle(const char *text, bool onlyIfEmpty/* =false */) +{ + if (onlyIfEmpty && m_TitleDrawn) + { + return; + } + + m_pKv->SetString("msg", text); + m_TitleDrawn = true; +} + +bool CValveMenuDisplay::DrawRawLine(const char *rawline) +{ + return false; +} + +void CValveMenuDisplay::SendRawDisplay(int client, int priority, unsigned int time) +{ + m_pKv->SetInt("level", priority); + m_pKv->SetInt("time", time); + + SH_CALL(g_pSPHCC, &IServerPluginHelpers::CreateMessage)( + engine->PEntityOfEntIndex(client), + DIALOG_MENU, + m_pKv, + g_pVSPHandle); +} + +bool CValveMenuDisplay::SendDisplay(int client, IMenuHandler *handler, unsigned int time) +{ + return g_ValveMenuStyle.DoClientMenu(client, this, handler, time); +} + +CValveMenu::CValveMenu() : CBaseMenu(&g_ValveMenuStyle), + m_IntroColor(255, 0, 0, 255), m_bShouldDelete(false), m_bCancelling(false) +{ + strcpy(m_IntroMsg, "You have a menu, press ESC"); + m_Pagination = 5; +} + +void CValveMenu::Cancel() +{ + if (m_bCancelling) + { + return; + } + + m_bCancelling = true; + g_ValveMenuStyle.CancelMenu(this); + m_bCancelling = false; + + if (m_bShouldDelete) + { + delete this; + } +} + +void CValveMenu::Destroy() +{ + if (!m_bCancelling || m_bShouldDelete) + { + Cancel(); + delete this; + } else { + m_bShouldDelete = true; + } +} + +bool CValveMenu::SetPagination(unsigned int itemsPerPage) +{ + if (itemsPerPage < 1 || itemsPerPage > 5) + { + return false; + } + + CBaseMenu::SetPagination(itemsPerPage); + + return true; +} + +bool CValveMenu::SetExtOption(MenuOption option, const void *valuePtr) +{ + if (option == MenuOption_IntroMessage) + { + strncopy(m_IntroMsg, (const char *)valuePtr, sizeof(m_IntroMsg)); + return true; + } else if (option == MenuOption_IntroColor) { + unsigned int *array = (unsigned int *)valuePtr; + m_IntroColor = Color(array[0], array[1], array[2], array[3]); + return true; + } + + return false; +} + +bool CValveMenu::Display(int client, IMenuHandler *handler, unsigned int time) +{ + if (m_bCancelling) + { + return false; + } + + return g_ValveMenuStyle.DoClientMenu(client, this, handler, time); +} + +IMenuDisplay *CValveMenu::CreateDisplay() +{ + return new CValveMenuDisplay(this); +} + +bool CValveMenu::GetExitButton() +{ + return true; +} + +bool CValveMenu::SetExitButton(bool set) +{ + return false; +} + +static const char *g_OptionNumTable[11] = +{ + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" +}; + +static const char *g_OptionCmdTable[11] = +{ + "sm_vmenuselect 0", /* INVALID! */ + "sm_vmenuselect 1", + "sm_vmenuselect 2", + "sm_vmenuselect 3", + "sm_vmenuselect 4", + "sm_vmenuselect 5", + "sm_vmenuselect 6", + "sm_vmenuselect 7", + "sm_vmenuselect 8", + "sm_vmenuselect 9", + "sm_vmenuselect 10" +}; diff --git a/core/MenuStyle_Valve.h b/core/MenuStyle_Valve.h new file mode 100644 index 00000000..4f45c2fa --- /dev/null +++ b/core/MenuStyle_Valve.h @@ -0,0 +1,127 @@ +/** + * vim: set ts=4 : + * =============================================================== + * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. + * =============================================================== + * + * This file is not open source and may not be copied without explicit + * written permission of AlliedModders LLC. This file may not be redistributed + * in whole or significant part. + * For information, see LICENSE.txt or http://www.sourcemod.net/license.php + * + * Version: $Id$ + */ + +#ifndef _INCLUDE_MENUSTYLE_VALVE_H +#define _INCLUDE_MENUSTYLE_VALVE_H + +#include "sm_globals.h" +#include "MenuManager.h" +#include "MenuStyle_Base.h" +#include "sourcemm_api.h" +#include "KeyValues.h" +#include +#include "sm_fastlink.h" + +using namespace SourceMod; + +class CValveMenuPlayer +{ +public: + CValveMenuPlayer() : bInMenu(false), bAutoIgnore(false), curPrioLevel(1) + { + } + menu_states_t states; + bool bInMenu; + bool bAutoIgnore; + int curPrioLevel; + float menuStartTime; + unsigned int menuHoldTime; +}; + +class CValveMenu; +class CValveMenuDisplay; + +class ValveMenuStyle : + public SMGlobalClass, + public IMenuStyle, + public IClientListener +{ +public: + ValveMenuStyle(); + bool DoClientMenu(int client, CValveMenu *menu, IMenuHandler *mh, unsigned int time); + bool DoClientMenu(int client, CValveMenuDisplay *menu, IMenuHandler *mh, unsigned int time); + void ClientPressedKey(int client, unsigned int key_press); + bool OnClientCommand(int client); + void CancelMenu(CValveMenu *menu); + void ProcessWatchList(); +public: //SMGlobalClass + void OnSourceModAllInitialized(); + void OnSourceModShutdown(); + void OnSourceModVSPReceived(IServerPluginCallbacks *iface); +public: //IClientListener + void OnClientDisconnected(int client); +public: //IMenuStyle + const char *GetStyleName(); + IMenuDisplay *CreateDisplay(); + IBaseMenu *CreateMenu(); + unsigned int GetMaxPageItems(); + MenuSource GetClientMenu(int client, void **object); + bool CancelClientMenu(int client, bool autoIgnore=false); +private: + bool RedoClientMenu(int client, ItemOrder order); + void HookCreateMessage(edict_t *pEdict, DIALOG_TYPE type, KeyValues *kv, IServerPluginCallbacks *plugin); + void _CancelMenu(int client, bool bAutoIgnore=false); +private: + CValveMenuPlayer *m_players; + FastLink m_WatchList; +}; + +class CValveMenu; + +class CValveMenuDisplay : public IMenuDisplay +{ +public: + CValveMenuDisplay(); + CValveMenuDisplay(CValveMenu *pMenu); + ~CValveMenuDisplay(); +public: + IMenuStyle *GetParentStyle(); + void Reset(); + void DrawTitle(const char *text, bool onlyIfEmpty=false); + unsigned int DrawItem(const ItemDrawInfo &item); + bool DrawRawLine(const char *rawline); + bool SendDisplay(int client, IMenuHandler *handler, unsigned int time); + bool SetExtOption(MenuOption option, const void *valuePtr); + bool CanDrawItem(unsigned int drawFlags); + void SendRawDisplay(int client, int priority, unsigned int time); +private: + KeyValues *m_pKv; + unsigned int m_NextPos; + bool m_TitleDrawn; +}; + +class CValveMenu : public CBaseMenu +{ + friend class CValveMenuDisplay; +public: + CValveMenu(); +public: + bool SetExtOption(MenuOption option, const void *valuePtr); + IMenuDisplay *CreateDisplay(); + bool GetExitButton(); + bool SetExitButton(bool set); + bool SetPagination(unsigned int itemsPerPage); + bool Display(int client, IMenuHandler *handler, unsigned int time); + void Cancel(); + void Destroy(); +private: + Color m_IntroColor; + char m_IntroMsg[128]; + bool m_bCancelling; + bool m_bShouldDelete; +}; + +extern ValveMenuStyle g_ValveMenuStyle; + +#endif //_INCLUDE_MENUSTYLE_VALVE_H diff --git a/core/PlayerManager.cpp b/core/PlayerManager.cpp index 44163e11..6260285e 100644 --- a/core/PlayerManager.cpp +++ b/core/PlayerManager.cpp @@ -17,6 +17,7 @@ #include "ShareSys.h" #include "AdminCache.h" #include "ConCmdManager.h" +#include "MenuStyle_Valve.h" PlayerManager g_Players; @@ -398,6 +399,12 @@ void PlayerManager::OnClientCommand(edict_t *pEntity) RETURN_META(MRES_SUPERCEDE); } + bool result = g_ValveMenuStyle.OnClientCommand(client); + if (result) + { + res = Pl_Handled; + } + res = g_ConCmds.DispatchClientCommand(client, (ResultType)res); if (res >= Pl_Handled) diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index a6526fba..09667d5a 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -227,6 +227,18 @@ RelativePath="..\MemoryUtils.cpp" > + + + + + + @@ -333,6 +345,18 @@ RelativePath="..\MemoryUtils.h" > + + + + + + @@ -341,6 +365,10 @@ RelativePath="..\sm_autonatives.h" > + + @@ -438,6 +466,10 @@ RelativePath="..\..\public\IMemoryUtils.h" > + + diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index a0576d71..95eaaf8a 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -29,6 +29,7 @@ #include "Translator.h" #include "ForwardSys.h" #include "TimerSys.h" +#include "MenuStyle_Valve.h" SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool); SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false); @@ -43,6 +44,7 @@ ISourcePawnEngine *g_pSourcePawn = &g_SourcePawn; IVirtualMachine *g_pVM; IdentityToken_t *g_pCoreIdent = NULL; float g_LastTime = 0.0f; +float g_LastMenuTime = 0.0f; float g_LastAuthCheck = 0.0f; IForward *g_pOnGameFrame = NULL; IForward *g_pOnMapEnd = NULL; @@ -264,6 +266,7 @@ bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, ch m_IsMapLoading = true; m_ExecPluginReload = true; g_LastTime = 0.0f; + g_LastMenuTime = 0.0f; g_LastAuthCheck = 0.0f; g_SimTicks.ticking = true; g_SimTicks.tickcount = 0; @@ -384,6 +387,12 @@ void SourceModBase::GameFrame(bool simulating) g_LastTime = curtime; } + if (g_SimTicks.tickcount && (curtime - g_LastMenuTime >= 1.0f)) + { + g_ValveMenuStyle.ProcessWatchList(); + g_LastMenuTime = curtime; + } + if (g_pOnGameFrame && g_pOnGameFrame->GetFunctionCount()) { g_pOnGameFrame->Execute(NULL);