diff --git a/core/MenuManager.cpp b/core/MenuManager.cpp index 9121518d..5a66b8d0 100644 --- a/core/MenuManager.cpp +++ b/core/MenuManager.cpp @@ -790,3 +790,13 @@ unsigned int MenuManager::GetRemainingVoteDelay() { return s_VoteHandler.GetRemainingVoteDelay(); } + +bool MenuManager::IsClientInVotePool(int client) +{ + return s_VoteHandler.IsClientInVotePool(client); +} + +bool MenuManager::RedrawClientVoteMenu(int client) +{ + return s_VoteHandler.RedrawToClient(client); +} diff --git a/core/MenuManager.h b/core/MenuManager.h index 2672d92d..3993eacf 100644 --- a/core/MenuManager.h +++ b/core/MenuManager.h @@ -89,6 +89,8 @@ public: bool IsVoteInProgress(); void CancelVoting(); unsigned int GetRemainingVoteDelay(); + bool IsClientInVotePool(int client); + bool RedrawClientVoteMenu(int client); public: //IHandleTypeDispatch void OnHandleDestroy(HandleType_t type, void *object); bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize); diff --git a/core/MenuVoting.cpp b/core/MenuVoting.cpp index ee9b4c1f..44c3e9d2 100644 --- a/core/MenuVoting.cpp +++ b/core/MenuVoting.cpp @@ -113,14 +113,19 @@ void VoteMenuHandler::OnClientDisconnected(int client) return; } - /* Wipe out their vote if they had one */ + /* Wipe out their vote if they had one. We have to make sure the the the + * newly connected client is not allowed to vote. + */ int item; - if ((item = m_ClientVotes[client]) >= 0) + if ((item = m_ClientVotes[client]) >= -1) { - assert((unsigned)item < m_Items); - assert(m_Votes[item] > 0); - m_Votes[item]--; - m_ClientVotes[client] = -1; + if (item >= 0) + { + assert((unsigned)item < m_Items); + assert(m_Votes[item] > 0); + m_Votes[item]--; + } + m_ClientVotes[client] = -2; } } @@ -129,18 +134,28 @@ bool VoteMenuHandler::IsVoteInProgress() return (m_pCurMenu != NULL); } -bool VoteMenuHandler::StartVote(IBaseMenu *menu, unsigned int num_clients, int clients[], unsigned int max_time, unsigned int flags/* =0 */) +bool VoteMenuHandler::StartVote(IBaseMenu *menu, + unsigned int num_clients, + int clients[], + unsigned int max_time, + unsigned int flags/* =0 */) { if (!InitializeVoting(menu, menu->GetHandler(), max_time, flags)) { return false; } + /* Note: we can use game time and not universal time because + * if we're voting then players are in-game. + */ + float fVoteDelay = sm_vote_delay.GetFloat(); if (fVoteDelay < 1.0) { g_next_vote = 0.0; - } else { + } + else + { /* This little trick breaks for infinite votes! * However, we just ignore that since those 1) shouldn't exist and * 2) people must be checking IsVoteInProgress() beforehand anyway. @@ -148,8 +163,15 @@ bool VoteMenuHandler::StartVote(IBaseMenu *menu, unsigned int num_clients, int c g_next_vote = gpGlobals->curtime + fVoteDelay + (float)max_time; } + m_fStartTime = gpGlobals->curtime; + m_nMenuTime = max_time; + for (unsigned int i=0; i 256) + { + continue; + } menu->Display(clients[i], max_time, this); } @@ -158,6 +180,58 @@ bool VoteMenuHandler::StartVote(IBaseMenu *menu, unsigned int num_clients, int c return true; } +bool VoteMenuHandler::IsClientInVotePool(int client) +{ + if (client < 1 + || client > g_Players.MaxClients() + || m_pCurMenu == NULL) + { + return false; + } + + return (m_ClientVotes[client] > -2); +} + +bool VoteMenuHandler::GetClientVoteChoice(int client, unsigned int *pItem) +{ + if (!IsClientInVotePool(client) + || m_ClientVotes[client] == -1) + { + return false; + } + + *pItem = m_ClientVotes[client]; + + return true; +} + +bool VoteMenuHandler::RedrawToClient(int client) +{ + unsigned int time_limit; + + if (!IsClientInVotePool(client)) + { + return false; + } + + if (m_nMenuTime == MENU_TIME_FOREVER) + { + time_limit = m_nMenuTime; + } + else + { + time_limit = (int)((float)m_nMenuTime - (gpGlobals->curtime - m_fStartTime)); + + /* Make sure this doesn't round to zero */ + if (time_limit == MENU_TIME_FOREVER) + { + time_limit = 1; + } + } + + return m_pCurMenu->Display(client, time_limit, this); +} + bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu, IMenuHandler *handler, unsigned int time, @@ -187,7 +261,9 @@ bool VoteMenuHandler::InitializeVoting(IBaseMenu *menu, m_Votes[i] = 0; } m_Votes.resize(m_Items, 0); - } else { + } + else + { for (unsigned int i=0; icurtime + fVoteDelay; } diff --git a/core/MenuVoting.h b/core/MenuVoting.h index 0710c298..1627fd41 100644 --- a/core/MenuVoting.h +++ b/core/MenuVoting.h @@ -72,6 +72,9 @@ public: IBaseMenu *GetCurrentMenu(); bool IsCancelling(); unsigned int GetRemainingVoteDelay(); + bool IsClientInVotePool(int client); + bool GetClientVoteChoice(int client, unsigned int *pItem); + bool RedrawToClient(int client); private: void Reset(IMenuHandler *mh); void DecrementPlayerCount(); @@ -93,6 +96,8 @@ private: unsigned int m_NumVotes; unsigned int m_VoteTime; unsigned int m_VoteFlags; + float m_fStartTime; + unsigned int m_nMenuTime; int m_ClientVotes[256+1]; }; diff --git a/core/smn_menus.cpp b/core/smn_menus.cpp index efaaaf63..1e5d636a 100644 --- a/core/smn_menus.cpp +++ b/core/smn_menus.cpp @@ -1405,6 +1405,49 @@ static cell_t GetMenuSelectionPosition(IPluginContext *pContext, const cell_t *p return *s_CurSelectPosition; } +static cell_t IsClientInVotePool(IPluginContext *pContext, const cell_t *params) +{ + int client; + IGamePlayer *pPlayer; + + client = params[1]; + if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL) + { + return pContext->ThrowNativeError("Invalid client index %d", client); + } + + if (!g_Menus.IsVoteInProgress()) + { + return pContext->ThrowNativeError("No vote is in progress"); + } + + return g_Menus.IsClientInVotePool(client) ? 1 : 0; +} + +static cell_t RedrawClientVoteMenu(IPluginContext *pContext, const cell_t *params) +{ + int client; + IGamePlayer *pPlayer; + + client = params[1]; + if ((pPlayer = g_Players.GetPlayerByIndex(client)) == NULL) + { + return pContext->ThrowNativeError("Invalid client index %d", client); + } + + if (!g_Menus.IsVoteInProgress()) + { + return pContext->ThrowNativeError("No vote is in progress"); + } + + if (!g_Menus.IsClientInVotePool(client)) + { + return pContext->ThrowNativeError("Client is not in the voting pool"); + } + + return g_Menus.RedrawClientVoteMenu(client) ? 1 : 0; +} + class EmptyMenuHandler : public IMenuHandler { public: @@ -1505,7 +1548,9 @@ REGISTER_NATIVES(menuNatives) {"GetPanelStyle", GetPanelStyle}, {"InsertMenuItem", InsertMenuItem}, {"InternalShowMenu", InternalShowMenu}, + {"IsClientInVotePool", IsClientInVotePool}, {"IsVoteInProgress", IsVoteInProgress}, + {"RedrawClientVoteMenu", RedrawClientVoteMenu}, {"RedrawMenuItem", RedrawMenuItem}, {"RemoveAllMenuItems", RemoveAllMenuItems}, {"RemoveMenuItem", RemoveMenuItem}, diff --git a/plugins/include/menus.inc b/plugins/include/menus.inc index b338808a..5582339c 100644 --- a/plugins/include/menus.inc +++ b/plugins/include/menus.inc @@ -540,6 +540,28 @@ native SetVoteResultCallback(Handle:menu, VoteHandler:callback); */ native CheckVoteDelay(); +/** + * Returns whether a client is in the pool of clients allowed + * to participate in the current vote. This is determined by + * the client list passed to StartVote(). + * + * @param client Client index. + * @return True if client is allowed to vote, false otherwise. + * @error If no vote is in progress or client index is invalid. + */ +native bool:IsClientInVotePool(client); + +/** + * Redraws the current vote menu to a client in the voting pool. + * + * @param client Client index. + * @return True on success, false if the client is in the vote pool + * but cannot vote again. + * @error No vote in progress, client is not in the voting pool, + * or client index is invalid. + */ +native bool:RedrawClientVoteMenu(client); + /** * Returns a style's global Handle. * diff --git a/public/IMenuManager.h b/public/IMenuManager.h index dfacf2cd..b85ca7d4 100644 --- a/public/IMenuManager.h +++ b/public/IMenuManager.h @@ -36,7 +36,7 @@ #include #define SMINTERFACE_MENUMANAGER_NAME "IMenuManager" -#define SMINTERFACE_MENUMANAGER_VERSION 14 +#define SMINTERFACE_MENUMANAGER_VERSION 15 /** * @file IMenuManager.h @@ -897,6 +897,23 @@ namespace SourceMod * @return Number of seconds to wait. */ virtual unsigned int GetRemainingVoteDelay() =0; + + /** + * @brief Returns whether a client is in the "allowed to vote" pool determined + * by the initial call to StartVote(). + * + * @param client Client index. + * @return True if client is allowed to vote, false on failure. + */ + virtual bool IsClientInVotePool(int client) =0; + + /** + * @brief Redraws the current vote menu to a client in the voting pool. + * + * @param client Client index. + * @return True on success, false if client is not allowed to vote. + */ + virtual bool RedrawClientVoteMenu(int client) =0; }; } diff --git a/translations/common.phrases.txt b/translations/common.phrases.txt index 18aeebd7..d2ab0eeb 100644 --- a/translations/common.phrases.txt +++ b/translations/common.phrases.txt @@ -222,32 +222,14 @@ "en" "The given player is not fully in-game yet." } - /* :UNUSED: */ - "Played Sound" + "Cannot participate in vote" { - "#format" "{1:s}" - "en" "Played sound on player '{1}'" + "en" "You cannot participate in this vote." } - /* :UNUSED: */ - "Slapped player" + "Cannot change vote" { - "#format" "{1:s}" - "en" "Slapped player '{1}'" - } - - /* :UNUSED: */ - "Slayed player" - { - "#format" "{1:s}" - "en" "Slayed player '{1}'" - } - - /* :UNUSED: */ - "Kicked player" - { - "#format" "{1:s}" - "en" "Kicked player '{1}'" + "en" "You cannot change your selection for this vote." } "On" {