diff --git a/core/MenuManager.cpp b/core/MenuManager.cpp index 6554465b..baad12c4 100644 --- a/core/MenuManager.cpp +++ b/core/MenuManager.cpp @@ -308,7 +308,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord { ItemDrawInfo &dr = drawItems[foundItems].draw; /* Is the item valid? */ - if (menu->GetItemInfo(i, &dr) != NULL) + if (menu->GetItemInfo(i, &dr, client) != NULL) { /* Ask the user to change the style, if necessary */ mh->OnMenuDrawItem(menu, client, i, dr.style); @@ -398,7 +398,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord } while (++lastItem < totalItems) { - if (menu->GetItemInfo(lastItem, &dr) != NULL) + if (menu->GetItemInfo(lastItem, &dr, client) != NULL) { mh->OnMenuDrawItem(menu, client, lastItem, dr.style); if (IsSlotItem(panel, dr.style)) @@ -420,7 +420,7 @@ IMenuPanel *MenuManager::RenderMenu(int client, menu_states_t &md, ItemOrder ord lastItem--; while (lastItem != 0) { - if (menu->GetItemInfo(lastItem, &dr) != NULL) + if (menu->GetItemInfo(lastItem, &dr, client) != NULL) { mh->OnMenuDrawItem(menu, client, lastItem, dr.style); if (IsSlotItem(panel, dr.style)) diff --git a/core/MenuStyle_Base.cpp b/core/MenuStyle_Base.cpp index 415029c1..8a7657e6 100644 --- a/core/MenuStyle_Base.cpp +++ b/core/MenuStyle_Base.cpp @@ -607,7 +607,7 @@ bool BaseMenuStyle::RedoClientMenu(int client, ItemOrder order) CBaseMenu::CBaseMenu(IMenuHandler *pHandler, IMenuStyle *pStyle, IdentityToken_t *pOwner) : m_pStyle(pStyle), m_Pagination(7), m_bShouldDelete(false), m_bCancelling(false), m_pOwner(pOwner ? pOwner : g_pCoreIdent), m_bDeleting(false), m_bWillFreeHandle(false), -m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT) +m_hHandle(BAD_HANDLE), m_pHandler(pHandler), m_nFlags(MENUFLAG_BUTTON_EXIT), m_RandomMapLen(0) { } @@ -633,7 +633,7 @@ bool CBaseMenu::AppendItem(const char *info, const ItemDrawInfo &draw) return false; } - CItem item; + CItem item(m_items.length()); item.info = info; if (draw.display) @@ -655,7 +655,7 @@ bool CBaseMenu::InsertItem(unsigned int position, const char *info, const ItemDr if (position >= m_items.length()) return false; - CItem item; + CItem item(position); item.info = info; if (draw.display) item.display = new ke::AString(draw.display); @@ -679,11 +679,16 @@ void CBaseMenu::RemoveAllItems() m_items.clear(); } -const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */) +const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* =NULL */, int client/* =0 */) { if (position >= m_items.length()) return NULL; + if (client > 0 && position < m_RandomMapLen) + { + position = m_RandomMaps[client][position]; + } + if (draw) { draw->display = m_items[position].display->chars(); @@ -693,6 +698,47 @@ const char *CBaseMenu::GetItemInfo(unsigned int position, ItemDrawInfo *draw/* = return m_items[position].info.chars(); } +void CBaseMenu::ShufflePerClient(int start, int stop) +{ + // limit map len to 255 items since it's using uint8 + m_RandomMapLen = MIN(GetItemCount(), 255); + if(stop >= 0) + m_RandomMapLen = MIN(m_RandomMapLen, (unsigned int)stop); + + for(int i = 1; i < SM_MAXPLAYERS + 1; i++) + { + // populate per-client map ... + m_RandomMaps[i].resize(m_RandomMapLen); + for(unsigned int j = 0; j < m_RandomMapLen; j++) + m_RandomMaps[i][j] = j; + + // ... and random shuffle it + for(int j = m_RandomMapLen - 1; j > start; j--) + { + int x = rand() % (j - start + 1) + start; + uint8_t tmp = m_RandomMaps[i][x]; + m_RandomMaps[i][x] = m_RandomMaps[i][j]; + m_RandomMaps[i][j] = tmp; + } + } +} + +bool CBaseMenu::IsPerClientShuffled() +{ + return m_RandomMapLen > 0; +} + +unsigned int CBaseMenu::GetRealItemIndex(int client, unsigned int position) +{ + if (client > 0 && position < m_RandomMapLen) + { + position = m_RandomMaps[client][position]; + return m_items[position].index; + } + + return position; +} + unsigned int CBaseMenu::GetItemCount() { return m_items.length(); diff --git a/core/MenuStyle_Base.h b/core/MenuStyle_Base.h index e9d83ebf..4319ddfc 100644 --- a/core/MenuStyle_Base.h +++ b/core/MenuStyle_Base.h @@ -44,8 +44,9 @@ using namespace SourceMod; class CItem { public: - CItem() + CItem(unsigned int index) { + this->index = index; style = 0; access = 0; } @@ -53,11 +54,13 @@ public: : info(ke::Move(other.info)), display(ke::Move(other.display)) { + index = other.index; style = other.style; access = other.access; } CItem & operator =(CItem &&other) { + index = other.index; info = ke::Move(other.info); display = ke::Move(other.display); style = other.style; @@ -66,6 +69,7 @@ public: } public: + unsigned int index; ke::AString info; ke::AutoPtr display; unsigned int style; @@ -138,7 +142,7 @@ public: 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 const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw=NULL, int client=0); virtual unsigned int GetItemCount(); virtual bool SetPagination(unsigned int itemsPerPage); virtual unsigned int GetPagination(); @@ -152,6 +156,9 @@ public: virtual unsigned int GetMenuOptionFlags(); virtual void SetMenuOptionFlags(unsigned int flags); virtual IMenuHandler *GetHandler(); + virtual void ShufflePerClient(int start, int stop); + virtual bool IsPerClientShuffled(); + virtual unsigned int GetRealItemIndex(int client, unsigned int position); unsigned int GetBaseMemUsage(); private: void InternalDelete(); @@ -168,6 +175,8 @@ protected: Handle_t m_hHandle; IMenuHandler *m_pHandler; unsigned int m_nFlags; + unsigned int m_RandomMapLen; + ke::Vector m_RandomMaps[SM_MAXPLAYERS+1]; }; #endif //_INCLUDE_MENUSTYLE_BASE_H diff --git a/core/MenuVoting.cpp b/core/MenuVoting.cpp index 0a873b67..93925c6b 100644 --- a/core/MenuVoting.cpp +++ b/core/MenuVoting.cpp @@ -514,15 +514,16 @@ void VoteMenuHandler::OnMenuSelect(IBaseMenu *menu, int client, unsigned int ite /* Check by our item count, NOT the vote array size */ if (item < m_Items) { - m_ClientVotes[client] = item; - m_Votes[item]++; + unsigned int index = menu->GetRealItemIndex(client, item); + m_ClientVotes[client] = index; + m_Votes[index]++; m_NumVotes++; if (sm_vote_chat.GetBool() || sm_vote_console.GetBool() || sm_vote_client_console.GetBool()) { static char buffer[1024]; ItemDrawInfo dr; - menu->GetItemInfo(item, &dr); + menu->GetItemInfo(item, &dr, client); if (sm_vote_console.GetBool()) { diff --git a/core/logic/smn_menus.cpp b/core/logic/smn_menus.cpp index 7b220d74..505f9a02 100644 --- a/core/logic/smn_menus.cpp +++ b/core/logic/smn_menus.cpp @@ -816,8 +816,14 @@ static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params) ItemDrawInfo dr; const char *info; + cell_t client = (params[0] >= 8) ? params[8] : 0; + if(!client && menu->IsPerClientShuffled()) + { + return pContext->ThrowNativeError("This menu has been per-client random shuffled. " + "You have to call GetMenuItem with a client index!"); + } - if ((info=menu->GetItemInfo(params[2], &dr)) == NULL) + if ((info=menu->GetItemInfo(params[2], &dr, client)) == NULL) { return 0; } @@ -832,6 +838,30 @@ static cell_t GetMenuItem(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t MenuShufflePerClient(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = (Handle_t)params[1]; + HandleError err; + IBaseMenu *menu; + + if ((err = ReadMenuHandle(params[1], &menu)) != HandleError_None) + { + return pContext->ThrowNativeError("Menu handle %x is invalid (error %d)", hndl, err); + } + + int start = params[2]; + int stop = params[3]; + + if (stop > 0 && !(stop >= start)) + { + return pContext->ThrowNativeError("Stop must be -1 or >= start!"); + } + + menu->ShufflePerClient(start, stop); + + return 1; +} + static cell_t SetMenuPagination(IPluginContext *pContext, const cell_t *params) { Handle_t hndl = (Handle_t)params[1]; @@ -1645,6 +1675,7 @@ REGISTER_NATIVES(menuNatives) {"SetPanelKeys", SetPanelKeys}, {"SetVoteResultCallback", SetVoteResultCallback}, {"VoteMenu", VoteMenu}, + {"MenuShufflePerClient", MenuShufflePerClient}, {"SetMenuNoVoteButton", SetMenuNoVoteButton}, // Transitional syntax support. @@ -1673,6 +1704,7 @@ REGISTER_NATIVES(menuNatives) {"Menu.ToPanel", CreatePanelFromMenu}, {"Menu.Cancel", CancelMenu}, {"Menu.DisplayVote", VoteMenu}, + {"Menu.ShufflePerClient", MenuShufflePerClient}, {"Menu.Pagination.get", GetMenuPagination}, {"Menu.Pagination.set", SetMenuPagination}, {"Menu.OptionFlags.get", GetMenuOptionFlags}, diff --git a/plugins/include/menus.inc b/plugins/include/menus.inc index b7fae0e9..2637cdc9 100644 --- a/plugins/include/menus.inc +++ b/plugins/include/menus.inc @@ -307,9 +307,16 @@ methodmap Menu < Handle // @param style By-reference variable to store drawing flags. // @param dispBuf Display buffer. // @param dispBufLen Maximum length of the display buffer. + // @param client Client index. Must be specified if menu is per-client random shuffled, -1 to ignore. // @return True on success, false if position is invalid. public native bool GetItem(int position, char[] infoBuf, int infoBufLen, - int &style=0, char[] dispBuf="", int dispBufLen=0); + int &style=0, char[] dispBuf="", int dispBufLen=0, int client=0); + + // Generates a per-client random mapping for the current vote options. + // + // @param start Menu item index to start randomizing from. + // @param stop Menu item index to stop randomizing at. -1 = infinite + public native void ShufflePerClient(int start=0, int stop=-1); // Sets the menu's default title/instruction message. // @@ -537,6 +544,7 @@ native void RemoveAllMenuItems(Handle menu); * @param style By-reference variable to store drawing flags. * @param dispBuf Display buffer. * @param dispBufLen Maximum length of the display buffer. + * @param client Client index. Must be specified if menu is per-client random shuffled, -1 to ignore. * @return True on success, false if position is invalid. * @error Invalid Handle. */ @@ -546,7 +554,16 @@ native bool GetMenuItem(Handle menu, int infoBufLen, int &style=0, char[] dispBuf="", - int dispBufLen=0); + int dispBufLen=0, + int client=0); + +/** + * Generates a per-client random mapping for the current vote options. + * + * @param start Menu item index to start randomizing from. + * @param stop Menu item index to stop randomizing at. -1 = infinite + */ +native void MenuShufflePerClient(Handle menu, int start=0, int stop=-1); /** * Returns the first item on the page of a currently selected menu. diff --git a/public/IMenuManager.h b/public/IMenuManager.h index 233cf430..6d74ea3b 100644 --- a/public/IMenuManager.h +++ b/public/IMenuManager.h @@ -36,7 +36,7 @@ #include #define SMINTERFACE_MENUMANAGER_NAME "IMenuManager" -#define SMINTERFACE_MENUMANAGER_VERSION 16 +#define SMINTERFACE_MENUMANAGER_VERSION 17 /** * @file IMenuManager.h @@ -485,9 +485,10 @@ namespace SourceMod * * @param position Position, starting from 0. * @param draw Optional pointer to store a draw information. + * @param client Client index. (Important for randomized menus.) * @return Info string pointer, or NULL if position was invalid. */ - virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw) =0; + virtual const char *GetItemInfo(unsigned int position, ItemDrawInfo *draw, int client=0) =0; /** * @brief Returns the number of items. @@ -636,6 +637,27 @@ namespace SourceMod * @return Approximate number of bytes being used. */ virtual unsigned int GetApproxMemUsage() =0; + + /** + * @brief Generates a per-client random mapping for the current vote options. + * @param start Menu item index to start randomizing from. + * @param stop Menu item index to stop randomizing at. -1 = infinite + */ + virtual void ShufflePerClient(int start=0, int stop=-1) =0; + + /** + * @brief Returns true if the menu has a per-client random mapping. + * @return True on success, false otherwise. + */ + virtual bool IsPerClientShuffled() =0; + + /** + * @brief Returns the actual (not shuffled) item index from the client position. + * @param client Client index. + * @param position Position, starting from 0. + * @return Actual item index in menu. + */ + virtual unsigned int GetRealItemIndex(int client, unsigned int position) =0; }; /**