diff --git a/AMBuilder b/AMBuilder
index f61c5142..d5f6d3dc 100644
--- a/AMBuilder
+++ b/AMBuilder
@@ -62,6 +62,7 @@ for sdk_name in MMSPlugin.sdks:
'src/ctimer.cpp',
'src/playermanager.cpp',
'src/gameconfig.cpp',
+ 'src/votemanager.cpp',
'src/httpmanager.cpp',
]
diff --git a/CS2Fixes.vcxproj b/CS2Fixes.vcxproj
index 68c44003..2769e2f5 100644
--- a/CS2Fixes.vcxproj
+++ b/CS2Fixes.vcxproj
@@ -181,6 +181,7 @@
+
@@ -215,6 +216,7 @@
+
diff --git a/CS2Fixes.vcxproj.filters b/CS2Fixes.vcxproj.filters
index 5dbd9978..f2925f90 100644
--- a/CS2Fixes.vcxproj.filters
+++ b/CS2Fixes.vcxproj.filters
@@ -98,6 +98,9 @@
Source Files
+
+ Source Files
+
@@ -196,5 +199,8 @@
Header Files
+
+ Header Files
+
\ No newline at end of file
diff --git a/src/adminsystem.cpp b/src/adminsystem.cpp
index 30abe87c..b0132b66 100644
--- a/src/adminsystem.cpp
+++ b/src/adminsystem.cpp
@@ -39,8 +39,6 @@ CAdminSystem* g_pAdminSystem = nullptr;
CUtlMap g_CommandList(0, 0, DefLessFunc(uint32));
-#define ADMIN_PREFIX "Admin %s has "
-
void PrintSingleAdminAction(const char *pszAdminName, const char *pszTargetName, const char *pszAction, const char *pszAction2 = "")
{
ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX ADMIN_PREFIX "%s %s%s.", pszAdminName, pszAction, pszTargetName, pszAction2);
@@ -839,6 +837,41 @@ CON_COMMAND_CHAT_FLAGS(rcon, "send a command to server console", ADMFLAG_RCON)
g_pEngineServer2->ServerCommand(args.ArgS());
}
+CON_COMMAND_CHAT_FLAGS(extend, "extend current map (negative value reduces map duration)", ADMFLAG_CHANGEMAP)
+{
+ if (args.ArgC() < 3)
+ {
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Usage: !extend ");
+ return;
+ }
+
+ int iExtendTime = V_StringToInt32(args[1], 0);
+
+ // CONVAR_TODO
+ ConVar* cvar = g_pCVar->GetConVar(g_pCVar->FindConVar("mp_timelimit"));
+
+ float flTimelimit;
+ memcpy(&flTimelimit, &cvar->values, sizeof(flTimelimit));
+
+ if (gpGlobals->curtime - g_pGameRules->m_flGameStartTime > flTimelimit * 60)
+ flTimelimit = (gpGlobals->curtime - g_pGameRules->m_flGameStartTime) / 60.0f + iExtendTime;
+ else
+ {
+ if (flTimelimit == 1)
+ flTimelimit = 0;
+ flTimelimit += iExtendTime;
+ }
+
+ if (flTimelimit <= 0)
+ flTimelimit = 1;
+
+ char buf[32];
+ V_snprintf(buf, sizeof(buf), "mp_timelimit %.6f", flTimelimit);
+
+ // CONVAR_TODO
+ g_pEngineServer2->ServerCommand(buf);
+}
+
bool CAdminSystem::LoadAdmins()
{
m_vecAdmins.Purge();
diff --git a/src/adminsystem.h b/src/adminsystem.h
index eba29c51..64713439 100644
--- a/src/adminsystem.h
+++ b/src/adminsystem.h
@@ -51,6 +51,7 @@
#define ADMFLAG_CUSTOM11 (1 << 24) // y
#define ADMFLAG_ROOT (1 << 25) // z
+#define ADMIN_PREFIX "Admin %s has "
class CInfractionBase
{
diff --git a/src/cs2fixes.cpp b/src/cs2fixes.cpp
index 336b8f50..87c857f5 100644
--- a/src/cs2fixes.cpp
+++ b/src/cs2fixes.cpp
@@ -45,6 +45,7 @@
#include "commands.h"
#include "eventlistener.h"
#include "gameconfig.h"
+#include "votemanager.h"
#include "httpmanager.h"
#include "entity/cgamerules.h"
@@ -302,6 +303,24 @@ void CS2Fixes::Hook_StartupServer(const GameSessionConfiguration_t& config, ISou
g_bHasTicked = false;
RegisterEventListeners();
+
+ // Disable RTV and Extend votes after map has just started
+ g_RTVState = ERTVState::MAP_START;
+ g_ExtendState = EExtendState::MAP_START;
+
+ // Allow RTV and Extend votes after 2 minutes post map start
+ new CTimer(120.0f, false, []()
+ {
+ if (g_RTVState != ERTVState::BLOCKED_BY_ADMIN)
+ g_RTVState = ERTVState::RTV_ALLOWED;
+
+ if (g_ExtendState != EExtendState::NO_EXTENDS)
+ g_ExtendState = EExtendState::EXTEND_ALLOWED;
+ return -1.0f;
+ });
+
+ // Set amount of Extends left
+ g_ExtendsLeft = 1;
}
void CS2Fixes::Hook_GameServerSteamAPIActivated()
diff --git a/src/playermanager.h b/src/playermanager.h
index 394abbd3..b12d48d7 100644
--- a/src/playermanager.h
+++ b/src/playermanager.h
@@ -49,6 +49,8 @@ class ZEPlayer
m_iHideDistance = 0;
m_bConnected = false;
m_iTotalDamage = 0;
+ m_bVotedRTV = false;
+ m_bVotedExtend = false;
}
bool IsFakeClient() { return m_bFakeClient; }
@@ -69,6 +71,8 @@ class ZEPlayer
void ClearTransmit() { m_shouldTransmit.ClearAll(); }
void SetHideDistance(int distance) { m_iHideDistance = distance; }
void SetTotalDamage(int damage) { m_iTotalDamage = damage; }
+ void SetRTVVote(bool bRTVVote) { m_bVotedRTV = bRTVVote; }
+ void SetExtendVote(bool bExtendVote) { m_bVotedExtend = bExtendVote; }
bool IsMuted() { return m_bMuted; }
bool IsGagged() { return m_bGagged; }
@@ -76,6 +80,8 @@ class ZEPlayer
int GetHideDistance() { return m_iHideDistance; }
CPlayerSlot GetPlayerSlot() { return m_slot; }
int GetTotalDamage() { return m_iTotalDamage; }
+ bool GetRTVVote() { return m_bVotedRTV; }
+ bool GetExtendVote() { return m_bVotedExtend; }
void OnAuthenticated();
void CheckAdmin();
@@ -93,6 +99,8 @@ class ZEPlayer
int m_iHideDistance;
CBitVec m_shouldTransmit;
int m_iTotalDamage;
+ bool m_bVotedRTV;
+ bool m_bVotedExtend;
};
class CPlayerManager
diff --git a/src/votemanager.cpp b/src/votemanager.cpp
new file mode 100644
index 00000000..a6e7ffd5
--- /dev/null
+++ b/src/votemanager.cpp
@@ -0,0 +1,452 @@
+/**
+ * =============================================================================
+ * CS2Fixes
+ * Copyright (C) 2023 Source2ZE
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#include "votemanager.h"
+#include "commands.h"
+#include "playermanager.h"
+#include "ctimer.h"
+#include "entity/cgamerules.h"
+
+#include "tier0/memdbgon.h"
+
+extern CEntitySystem *g_pEntitySystem;
+extern IVEngineServer2 *g_pEngineServer2;
+extern CGlobalVars *gpGlobals;
+extern CCSGameRules *g_pGameRules;
+
+ERTVState g_RTVState = ERTVState::MAP_START;
+EExtendState g_ExtendState = EExtendState::MAP_START;
+int g_ExtendsLeft = 1;
+
+// CONVAR_TODO
+float g_RTVSucceedRatio = 0.6f;
+float g_ExtendSucceedRatio = 0.5f;
+int g_ExtendTimeToAdd = 20;
+
+int GetCurrentRTVCount()
+{
+ int iVoteCount = 0;
+
+ for (int i = 0; i < gpGlobals->maxClients; i++)
+ {
+ ZEPlayer* pPlayer = g_playerManager->GetPlayer(i);
+
+ if (pPlayer && pPlayer->GetRTVVote())
+ iVoteCount++;
+ }
+
+ return iVoteCount;
+}
+
+int GetNeededRTVCount()
+{
+ int iOnlinePlayers = 0.0f;
+ int iVoteCount = 0;
+
+ for (int i = 0; i < gpGlobals->maxClients; i++)
+ {
+ ZEPlayer* pPlayer = g_playerManager->GetPlayer(i);
+
+ if (pPlayer)
+ {
+ iOnlinePlayers++;
+ if (pPlayer->GetRTVVote())
+ iVoteCount++;
+ }
+ }
+
+ return (int)(iOnlinePlayers * g_RTVSucceedRatio) + 1;
+}
+
+int GetCurrentExtendCount()
+{
+ int iVoteCount = 0;
+
+ for (int i = 0; i < gpGlobals->maxClients; i++)
+ {
+ ZEPlayer* pPlayer = g_playerManager->GetPlayer(i);
+
+ if (pPlayer && pPlayer->GetExtendVote())
+ iVoteCount++;
+ }
+
+ return iVoteCount;
+}
+
+int GetNeededExtendCount()
+{
+ int iOnlinePlayers = 0.0f;
+ int iVoteCount = 0;
+
+ for (int i = 0; i < gpGlobals->maxClients; i++)
+ {
+ ZEPlayer* pPlayer = g_playerManager->GetPlayer(i);
+
+ if (pPlayer)
+ {
+ iOnlinePlayers++;
+ if (pPlayer->GetExtendVote())
+ iVoteCount++;
+ }
+ }
+
+ return (int)(iOnlinePlayers * g_ExtendSucceedRatio) + 1;
+}
+
+CON_COMMAND_CHAT(rtv, "Vote to end the current map sooner.")
+{
+ if (!player)
+ {
+ ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "You cannot use this command from the server console.");
+ return;
+ }
+
+ int iPlayer = player->GetPlayerSlot();
+
+ ZEPlayer* pPlayer = g_playerManager->GetPlayer(iPlayer);
+
+ // Something has to really go wrong for this to happen
+ if (!pPlayer)
+ {
+ Warning("%s Tried to access a null ZEPlayer!!\n", player->GetPlayerName());
+ return;
+ }
+
+ switch (g_RTVState)
+ {
+ case ERTVState::MAP_START:
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "RTV is not open yet.");
+ return;
+ case ERTVState::POST_RTV_SUCCESSFULL:
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "RTV vote already succeeded.");
+ return;
+ case ERTVState::POST_LAST_ROUND_END:
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "RTV is closed during next map selection.");
+ return;
+ case ERTVState::BLOCKED_BY_ADMIN:
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "RTV has been blocked by an Admin.");
+ return;
+ }
+
+ int iCurrentRTVCount = GetCurrentRTVCount();
+ int iNeededRTVCount = GetNeededRTVCount();
+
+ if (pPlayer->GetRTVVote())
+ {
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "You have already rocked the vote (%i voted, %i needed).", iCurrentRTVCount, iNeededRTVCount);
+ return;
+ }
+
+ if (iCurrentRTVCount + 1 >= iNeededRTVCount)
+ {
+ g_RTVState = ERTVState::POST_RTV_SUCCESSFULL;
+ ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX "RTV succeeded! This is the last round of the map!");
+ // CONVAR_TODO
+ g_pEngineServer2->ServerCommand("mp_timelimit 1");
+
+ for (int i = 0; i < gpGlobals->maxClients; i++)
+ {
+ ZEPlayer* pPlayer2 = g_playerManager->GetPlayer(i);
+ if (pPlayer2)
+ pPlayer2->SetRTVVote(false);
+ }
+
+ return;
+ }
+
+ pPlayer->SetRTVVote(true);
+ ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX "%s wants to rock the vote (%i voted, %i needed).", player->GetPlayerName(), iCurrentRTVCount+1, iNeededRTVCount);
+}
+
+CON_COMMAND_CHAT(unrtv, "Remove your vote to end the current map sooner.")
+{
+ if (!player)
+ {
+ ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "You cannot use this command from the server console.");
+ return;
+ }
+
+ int iPlayer = player->GetPlayerSlot();
+
+ ZEPlayer* pPlayer = g_playerManager->GetPlayer(iPlayer);
+
+ // Something has to really go wrong for this to happen
+ if (!pPlayer)
+ {
+ Warning("%s Tried to access a null ZEPlayer!!\n", player->GetPlayerName());
+ return;
+ }
+
+ if (!pPlayer->GetRTVVote())
+ {
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "You have not voted to RTV current map.");
+ return;
+ }
+
+ pPlayer->SetRTVVote(false);
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "You no longer want to RTV current map.");
+}
+
+
+CON_COMMAND_CHAT(ve, "Vote to extend the current map.")
+{
+ if (!player)
+ {
+ ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "You cannot use this command from the server console.");
+ return;
+ }
+
+ int iPlayer = player->GetPlayerSlot();
+
+ ZEPlayer* pPlayer = g_playerManager->GetPlayer(iPlayer);
+
+ // Something has to really go wrong for this to happen
+ if (!pPlayer)
+ {
+ Warning("%s Tried to access a null ZEPlayer!!\n", player->GetPlayerName());
+ return;
+ }
+
+ switch (g_ExtendState)
+ {
+ case EExtendState::MAP_START:
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Extend vote is not open yet.");
+ return;
+ case EExtendState::POST_EXTEND_COOLDOWN:
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Extend vote is not open yet.");
+ return;
+ case EExtendState::POST_EXTEND_NO_EXTENDS_LEFT:
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "There are no extends left for the current map.");
+ return;
+ case EExtendState::POST_LAST_ROUND_END:
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Extend vote is closed during next map selection.");
+ return;
+ case EExtendState::NO_EXTENDS:
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Extend vote is not allowed for current map.");
+ return;
+ }
+
+ int iCurrentExtendCount = GetCurrentExtendCount();
+ int iNeededExtendCount = GetNeededExtendCount();
+
+ if (pPlayer->GetExtendVote())
+ {
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "You have already voted to extend the map (%i voted, %i needed).", iCurrentExtendCount, iNeededExtendCount);
+ return;
+ }
+
+ if (iCurrentExtendCount + 1 >= iNeededExtendCount)
+ {
+ // mimic behaviour of !extend
+ // CONVAR_TODO
+ ConVar* cvar = g_pCVar->GetConVar(g_pCVar->FindConVar("mp_timelimit"));
+
+ float flTimelimit;
+ // type punning
+ memcpy(&flTimelimit, &cvar->values, sizeof(flTimelimit));
+
+ if (gpGlobals->curtime - g_pGameRules->m_flGameStartTime > flTimelimit * 60)
+ flTimelimit = (gpGlobals->curtime - g_pGameRules->m_flGameStartTime) / 60.0f + g_ExtendTimeToAdd;
+ else
+ {
+ if (flTimelimit == 1)
+ flTimelimit = 0;
+ flTimelimit += g_ExtendTimeToAdd;
+ }
+
+ if (flTimelimit <= 0)
+ flTimelimit = 1;
+
+ char buf[32];
+ V_snprintf(buf, sizeof(buf), "mp_timelimit %.6f", flTimelimit + g_ExtendTimeToAdd);
+
+ // CONVAR_TODO
+ g_pEngineServer2->ServerCommand(buf);
+
+ if (g_ExtendsLeft == 1)
+ // there are no extends left after a successfull extend vote
+ g_ExtendState = EExtendState::POST_EXTEND_NO_EXTENDS_LEFT;
+ else
+ {
+ // there's an extend left after a successfull extend vote
+ g_ExtendState = EExtendState::POST_EXTEND_COOLDOWN;
+
+ // Allow another extend vote after 2 minutes
+ new CTimer(120.0f, false, []()
+ {
+ if (g_ExtendState == EExtendState::POST_EXTEND_COOLDOWN)
+ g_ExtendState = EExtendState::EXTEND_ALLOWED;
+ return -1.0f;
+ });
+ }
+
+ for (int i = 0; i < gpGlobals->maxClients; i++)
+ {
+ ZEPlayer* pPlayer2 = g_playerManager->GetPlayer(i);
+ if (pPlayer2)
+ pPlayer2->SetExtendVote(false);
+ }
+
+ g_ExtendsLeft--;
+ ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX "Extend vote succeeded! Current map has been extended by %i minutes.", g_ExtendTimeToAdd);
+
+ return;
+ }
+
+ pPlayer->SetExtendVote(true);
+ ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX "%s wants to extend the map (%i voted, %i needed).", player->GetPlayerName(), iCurrentExtendCount+1, iNeededExtendCount);
+}
+
+CON_COMMAND_CHAT(unve, "Remove your vote to extend current map.")
+{
+ if (!player)
+ {
+ ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "You cannot use this command from the server console.");
+ return;
+ }
+
+ int iPlayer = player->GetPlayerSlot();
+
+ ZEPlayer* pPlayer = g_playerManager->GetPlayer(iPlayer);
+
+ // Something has to really go wrong for this to happen
+ if (!pPlayer)
+ {
+ Warning("%s Tried to access a null ZEPlayer!!\n", player->GetPlayerName());
+ return;
+ }
+
+ if (!pPlayer->GetExtendVote())
+ {
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "You have not voted to extend current map.");
+ return;
+ }
+
+ pPlayer->SetExtendVote(false);
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "You no longer want to extend current map.");
+}
+
+CON_COMMAND_CHAT_FLAGS(blockrtv, "Block the ability for players to vote to end current map sooner.", ADMFLAG_CHANGEMAP)
+{
+ if (g_RTVState == ERTVState::BLOCKED_BY_ADMIN)
+ {
+ if (player)
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "RTV is already blocked.");
+ else
+ ConMsg("RTV is already blocked.");
+ return;
+ }
+
+ const char* pszCommandPlayerName = player ? player->GetPlayerName() : "Console";
+
+ g_RTVState = ERTVState::BLOCKED_BY_ADMIN;
+
+ ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX ADMIN_PREFIX "blocked vote for RTV.", pszCommandPlayerName);
+}
+
+CON_COMMAND_CHAT_FLAGS(unblockrtv, "Restore the ability for players to vote to end current map sooner.", ADMFLAG_CHANGEMAP)
+{
+ if (g_RTVState == ERTVState::RTV_ALLOWED)
+ {
+ if (player)
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "RTV is not blocked.");
+ else
+ ConMsg("RTV is not blocked.");
+ return;
+ }
+
+ const char* pszCommandPlayerName = player ? player->GetPlayerName() : "Console";
+
+ g_RTVState = ERTVState::RTV_ALLOWED;
+
+ ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX ADMIN_PREFIX "restored vote for RTV.", pszCommandPlayerName);
+}
+
+CON_COMMAND_CHAT_FLAGS(addextend, "Add another extend to the current map for players to vote.", ADMFLAG_CHANGEMAP)
+{
+ const char* pszCommandPlayerName = player ? player->GetPlayerName() : "Console";
+
+ if (g_ExtendState == EExtendState::POST_EXTEND_NO_EXTENDS_LEFT || g_ExtendState == EExtendState::NO_EXTENDS)
+ g_ExtendState = EExtendState::EXTEND_ALLOWED;
+
+ g_ExtendsLeft += 1;
+
+ ClientPrintAll(HUD_PRINTTALK, CHAT_PREFIX ADMIN_PREFIX "allowed for an additional extend.", pszCommandPlayerName);
+}
+
+CON_COMMAND_CHAT(extendsleft, "Display amount of extends left for the current map")
+{
+ char message[64];
+
+ switch (g_ExtendsLeft)
+ {
+ case 0:
+ strcpy(message, "There are no extends left.");
+ break;
+ case 1:
+ strcpy(message, "There's 1 extend left");
+ break;
+ default:
+ V_snprintf(message, sizeof(message), "There are %i extends left.", g_ExtendsLeft);
+ break;
+ }
+
+ if (player)
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "%s", message);
+ else
+ ConMsg("%s", message);
+}
+
+CON_COMMAND_CHAT(timeleft, "Display time left to end of current map.")
+{
+ if (!player)
+ {
+ ClientPrint(player, HUD_PRINTCONSOLE, CHAT_PREFIX "You cannot use this command from the server console.");
+ return;
+ }
+
+ // CONVAR_TODO
+ ConVar* cvar = g_pCVar->GetConVar(g_pCVar->FindConVar("mp_timelimit"));
+
+ float flTimelimit;
+ memcpy(&flTimelimit, &cvar->values, sizeof(flTimelimit));
+
+ if (flTimelimit == 0)
+ {
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "No time limit");
+ return;
+ }
+
+ int iTimeleft = (int) ((g_pGameRules->m_flGameStartTime + flTimelimit * 60.0f) - gpGlobals->curtime);
+
+ if (iTimeleft < 0)
+ {
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Last round!");
+ return;
+ }
+
+ div_t div = std::div(iTimeleft, 60);
+ int iMinutesLeft = div.quot;
+ int iSecondsLeft = div.rem;
+
+ if (iMinutesLeft > 0)
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Timeleft: %i minutes %i seconds", iMinutesLeft, iSecondsLeft);
+ else
+ ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX "Timeleft: %i seconds", iSecondsLeft);
+}
diff --git a/src/votemanager.h b/src/votemanager.h
new file mode 100644
index 00000000..f92a7e9e
--- /dev/null
+++ b/src/votemanager.h
@@ -0,0 +1,46 @@
+
+/**
+ * =============================================================================
+ * CS2Fixes
+ * Copyright (C) 2023 Source2ZE
+ * =============================================================================
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, version 3.0, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+#pragma once
+
+enum class ERTVState
+{
+ MAP_START,
+ RTV_ALLOWED,
+ POST_RTV_SUCCESSFULL,
+ POST_LAST_ROUND_END,
+ BLOCKED_BY_ADMIN,
+};
+
+enum class EExtendState
+{
+ MAP_START,
+ EXTEND_ALLOWED,
+ POST_EXTEND_COOLDOWN,
+ POST_EXTEND_NO_EXTENDS_LEFT,
+ POST_LAST_ROUND_END,
+ NO_EXTENDS,
+};
+
+extern ERTVState g_RTVState;
+extern EExtendState g_ExtendState;
+extern int g_ExtendsLeft;
+
+void SetExtendsLeft();
\ No newline at end of file