diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index 3cdfe3f0b..a99216558 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -217,7 +217,7 @@ class CMenus : public CComponent void RenderGame(CUIRect MainView); void RenderServerInfo(CUIRect MainView); void RenderServerControl(CUIRect MainView); - void RenderServerControlKick(CUIRect MainView); + void RenderServerControlKick(CUIRect MainView, bool FilterSpectators); void RenderServerControlServer(CUIRect MainView); // found in menus_browser.cpp diff --git a/src/game/client/components/menus_ingame.cpp b/src/game/client/components/menus_ingame.cpp index 06da001ab..4b593989f 100644 --- a/src/game/client/components/menus_ingame.cpp +++ b/src/game/client/components/menus_ingame.cpp @@ -345,7 +345,7 @@ void CMenus::RenderServerControlServer(CUIRect MainView) m_CallvoteSelectedOption = UiDoListboxEnd(&s_ScrollValue, 0); } -void CMenus::RenderServerControlKick(CUIRect MainView) +void CMenus::RenderServerControlKick(CUIRect MainView, bool FilterSpectators) { int NumOptions = 0; int Selected = -1; @@ -354,6 +354,8 @@ void CMenus::RenderServerControlKick(CUIRect MainView) { if(!m_pClient->m_Snap.m_paPlayerInfos[i] || i == m_pClient->m_Snap.m_LocalClientID) continue; + if(FilterSpectators && m_pClient->m_Snap.m_paPlayerInfos[i]->m_Team == TEAM_SPECTATORS) + continue; if(m_CallvoteSelectedPlayer == i) Selected = NumOptions; aPlayerIDs[NumOptions++] = i; @@ -400,7 +402,8 @@ void CMenus::RenderServerControl(CUIRect MainView) const char *paTabs[] = { Localize("Settings"), - Localize("Kick")}; + Localize("Kick"), + Localize("Spectate")}; int aNumTabs = (int)(sizeof(paTabs)/sizeof(*paTabs)); for(int i = 0; i < aNumTabs; i++) @@ -424,8 +427,9 @@ void CMenus::RenderServerControl(CUIRect MainView) if(s_ControlPage == 0) RenderServerControlServer(MainView); else if(s_ControlPage == 1) - RenderServerControlKick(MainView); - + RenderServerControlKick(MainView, false); + else if(s_ControlPage == 2) + RenderServerControlKick(MainView, true); { CUIRect Button; @@ -445,6 +449,15 @@ void CMenus::RenderServerControl(CUIRect MainView) SetActive(false); } } + else if(s_ControlPage == 2) + { + if(m_CallvoteSelectedPlayer >= 0 && m_CallvoteSelectedPlayer < MAX_CLIENTS && + m_pClient->m_Snap.m_paPlayerInfos[m_CallvoteSelectedPlayer]) + { + m_pClient->m_pVoting->CallvoteSpectate(m_CallvoteSelectedPlayer, m_aCallvoteReason); + SetActive(false); + } + } m_aCallvoteReason[0] = 0; } @@ -479,6 +492,15 @@ void CMenus::RenderServerControl(CUIRect MainView) SetActive(false); } } + else if(s_ControlPage == 2) + { + if(m_CallvoteSelectedPlayer >= 0 && m_CallvoteSelectedPlayer < MAX_CLIENTS && + m_pClient->m_Snap.m_paPlayerInfos[m_CallvoteSelectedPlayer]) + { + m_pClient->m_pVoting->CallvoteSpectate(m_CallvoteSelectedPlayer, m_aCallvoteReason, true); + SetActive(false); + } + } m_aCallvoteReason[0] = 0; } } diff --git a/src/game/client/components/voting.cpp b/src/game/client/components/voting.cpp index 6e00a5d58..d8c87855b 100644 --- a/src/game/client/components/voting.cpp +++ b/src/game/client/components/voting.cpp @@ -31,6 +31,22 @@ void CVoting::Callvote(const char *pType, const char *pValue, const char *pReaso Client()->SendPackMsg(&Msg, MSGFLAG_VITAL); } +void CVoting::CallvoteSpectate(int ClientID, const char *pReason, bool ForceVote) +{ + if(ForceVote) + { + char aBuf[128]; + str_format(aBuf, sizeof(aBuf), "set_team %d -1", ClientID); + Client()->Rcon(aBuf); + } + else + { + char aBuf[32]; + str_format(aBuf, sizeof(aBuf), "%d", ClientID); + Callvote("spectate", aBuf, pReason); + } +} + void CVoting::CallvoteKick(int ClientID, const char *pReason, bool ForceVote) { if(ForceVote) diff --git a/src/game/client/components/voting.h b/src/game/client/components/voting.h index 92e4b395b..9e7da2dce 100644 --- a/src/game/client/components/voting.h +++ b/src/game/client/components/voting.h @@ -45,6 +45,7 @@ public: void RenderBars(CUIRect Bars, bool Text); + void CallvoteSpectate(int ClientID, const char *pReason, bool ForceVote = false); void CallvoteKick(int ClientID, const char *pReason, bool ForceVote = false); void CallvoteOption(int Option, const char *pReason, bool ForceVote = false); diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp index 8b492ec0d..8245d508a 100644 --- a/src/game/server/gamecontext.cpp +++ b/src/game/server/gamecontext.cpp @@ -711,6 +711,30 @@ void CGameContext::OnMessage(int MsgID, CUnpacker *pUnpacker, int ClientID) str_format(aCmd, sizeof(aCmd), "ban %s %d Banned by vote", aBuf, g_Config.m_SvVoteKickBantime); } } + else if(str_comp_nocase(pMsg->m_Type, "spectate") == 0) + { + if(!g_Config.m_SvVoteSpectate) + { + SendChatTarget(ClientID, "Server does not allow voting to move players to spectators"); + return; + } + + int SpectateID = str_toint(pMsg->m_Value); + if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !m_apPlayers[SpectateID] || m_apPlayers[SpectateID]->GetTeam() == TEAM_SPECTATORS) + { + SendChatTarget(ClientID, "Invalid client id to move"); + return; + } + if(SpectateID == ClientID) + { + SendChatTarget(ClientID, "You cant move yourself"); + return; + } + + str_format(aChatmsg, sizeof(aChatmsg), "'%s' called for vote to move '%s' to spectators (%s)", Server()->ClientName(ClientID), Server()->ClientName(SpectateID), pReason); + str_format(aDesc, sizeof(aDesc), "move '%s' to spectators", Server()->ClientName(SpectateID)); + str_format(aCmd, sizeof(aCmd), "set_team %d -1", SpectateID); + } if(aCmd[0]) { @@ -1133,6 +1157,18 @@ void CGameContext::ConForceVote(IConsole::IResult *pResult, void *pUserData) pSelf->Console()->ExecuteLine(aBuf); } } + else if(str_comp_nocase(pType, "spectate") == 0) + { + int SpectateID = str_toint(pValue); + if(SpectateID < 0 || SpectateID >= MAX_CLIENTS || !pSelf->m_apPlayers[SpectateID] || pSelf->m_apPlayers[SpectateID]->GetTeam() == TEAM_SPECTATORS) + { + pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "Invalid client id to move"); + return; + } + + str_format(aBuf, sizeof(aBuf), "set_team %d -1", SpectateID); + pSelf->Console()->ExecuteLine(aBuf); + } } void CGameContext::ConClearVotes(IConsole::IResult *pResult, void *pUserData) diff --git a/src/game/variables.h b/src/game/variables.h index a0ac64c47..ce8e6ccb1 100644 --- a/src/game/variables.h +++ b/src/game/variables.h @@ -69,6 +69,7 @@ MACRO_CONFIG_INT(SvTeambalanceTime, sv_teambalance_time, 1, 0, 1000, CFGFLAG_SER MACRO_CONFIG_INT(SvInactiveKickTime, sv_inactivekick_time, 3, 0, 1000, CFGFLAG_SERVER, "How many minutes to wait before taking care of inactive players") MACRO_CONFIG_INT(SvInactiveKick, sv_inactivekick, 1, 0, 2, CFGFLAG_SERVER, "How to deal with inactive players (0=move to spectator, 1=move to free spectator slot/kick, 2=kick)") +MACRO_CONFIG_INT(SvVoteSpectate, sv_vote_spectate, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to move players to spectators") MACRO_CONFIG_INT(SvVoteKick, sv_vote_kick, 1, 0, 1, CFGFLAG_SERVER, "Allow voting to kick players") MACRO_CONFIG_INT(SvVoteKickMin, sv_vote_kick_min, 0, 0, MAX_CLIENTS, CFGFLAG_SERVER, "Minimum number of players required to start a kick vote") MACRO_CONFIG_INT(SvVoteKickBantime, sv_vote_kick_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time to ban a player if kicked by vote. 0 makes it just use kick")