diff --git a/cstrike/addons/sourcemod/translations/es/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/es/zombiereloaded.phrases.txt
index c890184..0c8f0dc 100644
--- a/cstrike/addons/sourcemod/translations/es/zombiereloaded.phrases.txt
+++ b/cstrike/addons/sourcemod/translations/es/zombiereloaded.phrases.txt
@@ -358,6 +358,13 @@
"es" "El ultimo zombi a dejado el juego, y te ha pasado la infeccion a ti. (luego se lo agradeces xD)"
}
+ // Center Text
+
+ "Infect countdown"
+ {
+ "en" "First infection in {1} seconds."
+ }
+
// Menu
"Infect menu clients title"
diff --git a/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt
index be8633e..7f10b76 100644
--- a/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt
+++ b/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt
@@ -357,6 +357,13 @@
"no" "Den siste zombien har forlatt spillet. Du er blitt valgt som erstatning."
}
+ // Center Text
+
+ "Infect countdown"
+ {
+ "en" "Første infeksjon om {1} sekunder."
+ }
+
// Menu
"Infect menu clients title"
diff --git a/cstrike/addons/sourcemod/translations/ru/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/ru/zombiereloaded.phrases.txt
index 255312c..5b40cd5 100644
--- a/cstrike/addons/sourcemod/translations/ru/zombiereloaded.phrases.txt
+++ b/cstrike/addons/sourcemod/translations/ru/zombiereloaded.phrases.txt
@@ -358,6 +358,13 @@
"ru" "Последний зомби покинул игру, поэтому вам досталась его инфекция."
}
+ // Center Text
+
+ "Infect countdown"
+ {
+ "en" "First infection in {1} seconds."
+ }
+
// Menu
"Infect menu clients title"
diff --git a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt
index 2483b57..a88e08e 100644
--- a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt
+++ b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt
@@ -372,6 +372,14 @@
"en" "The last zombie has left the game, and has passed the infection on to you."
}
+ // Center Text
+
+ "Infect countdown"
+ {
+ "#format" "{1:d}"
+ "en" "First infection in {1} seconds."
+ }
+
// Menu
"Infect menu clients title"
diff --git a/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg b/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg
index a6a2956..c230cff 100644
--- a/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg
+++ b/cstrike/cfg/sourcemod/zombiereloaded/zombiereloaded.cfg
@@ -289,6 +289,10 @@ zr_hitgroups "1"
// Default: "5"
zr_infect_mzombie_ratio "5"
+// Counts down to the first infection of the round. The counter is displayed in the middle of the screen.
+// Default: "0"
+zr_infect_mzombie_countdown "0"
+
// Teleport mother zombies back to spawn on infect.
// Default: "0"
zr_infect_mzombie_respawn "0"
diff --git a/docs/zr_manual.htm b/docs/zr_manual.htm
index b177ff9..ea34835 100644
--- a/docs/zr_manual.htm
+++ b/docs/zr_manual.htm
@@ -2570,6 +2570,19 @@ tune game balance.
+
+ zr_infect_mzombie_countdown |
+ 0 |
+
+
+
+ Counts down to the first infection of the round. The counter is
+ displayed in the middle of the screen.
+ Options:
+ 0 or 1
+ |
+
+
zr_infect_mzombie_respawn |
0 |
diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc
index 261898a..ce3390c 100644
--- a/src/zr/cvars.inc
+++ b/src/zr/cvars.inc
@@ -100,6 +100,7 @@ enum CvarsList
Handle:CVAR_INFECT_CONSECUTIVE_BLOCK,
Handle:CVAR_INFECT_WEAPONS_DROP,
Handle:CVAR_INFECT_MZOMBIE_RATIO,
+ Handle:CVAR_INFECT_MZOMBIE_COUNTDOWN,
Handle:CVAR_INFECT_MZOMBIE_RESPAWN,
Handle:CVAR_INFECT_EXPLOSION,
Handle:CVAR_INFECT_FIREBALL,
@@ -316,6 +317,7 @@ CvarsCreate()
// General
g_hCvarsList[CVAR_INFECT_MZOMBIE_RATIO] = CreateConVar("zr_infect_mzombie_ratio", "5", "Number of mother zombies to infect (when infect timer is up) in proportion to number of humans on the server.");
+ g_hCvarsList[CVAR_INFECT_MZOMBIE_COUNTDOWN] = CreateConVar("zr_infect_mzombie_countdown", "0", "Counts down to the first infection of the round. Countdown is printed in the middle of the client's screen.");
g_hCvarsList[CVAR_INFECT_MZOMBIE_RESPAWN] = CreateConVar("zr_infect_mzombie_respawn", "0", "Teleport mother zombies back to spawn on infect.");
g_hCvarsList[CVAR_INFECT_SPAWNTIME_MIN] = CreateConVar("zr_infect_spawntime_min", "30.0", "Minimum time from the start of the round until picking the mother zombie(s).");
g_hCvarsList[CVAR_INFECT_SPAWNTIME_MAX] = CreateConVar("zr_infect_spawntime_max", "50.0", "Maximum time from the start of the round until picking the mother zombie(s).");
diff --git a/src/zr/infect.inc b/src/zr/infect.inc
index 9f7f415..15ff065 100644
--- a/src/zr/infect.inc
+++ b/src/zr/infect.inc
@@ -46,9 +46,13 @@
*/
/**
- * Global variable to store infect timer handle.
+ * @section Global variables to store infect timer handles.
*/
new Handle:tInfect = INVALID_HANDLE;
+new Handle:tInfectCountdown = INVALID_HANDLE;
+/**
+ * @endsection
+ */
/**
* Array for flagging client as zombie.
@@ -74,8 +78,9 @@ new bool:bInfectImmune[MAXPLAYERS + 1][2];
*/
InfectOnMapStart()
{
- // Reset timer handle.
- tInfect = INVALID_HANDLE;
+ // Stop timers if running.
+ ZREndTimer(tInfect);
+ ZREndTimer(tInfectCountdown);
}
/**
@@ -336,15 +341,9 @@ InfectOnClientHurt(client, attacker, const String:weapon[])
*/
InfectOnRoundStart()
{
- // If infect timer is running, then kill it.
- if (tInfect != INVALID_HANDLE)
- {
- // Kill timer.
- KillTimer(tInfect);
-
- // Reset timer handle.
- tInfect = INVALID_HANDLE;
- }
+ // Stop infect timers if running.
+ ZREndTimer(tInfect);
+ ZREndTimer(tInfectCountdown);
// Tell plugin there are no zombies.
g_bZombieSpawned = false;
@@ -355,12 +354,9 @@ InfectOnRoundStart()
*/
InfectOnRoundFreezeEnd()
{
- // If infect timer is running, then kill it.
- if (tInfect != INVALID_HANDLE)
- {
- // Kill timer.
- KillTimer(tInfect);
- }
+ // Stop infect timers if running.
+ ZREndTimer(tInfect);
+ ZREndTimer(tInfectCountdown);
// If the zombie has spawned already (had to be through admin) then stop.
if (g_bZombieSpawned)
@@ -375,7 +371,24 @@ InfectOnRoundFreezeEnd()
// Pick random time between min and max.
new Float:randomtime = GetRandomFloat(infectspawntimemin, infectspawntimemax);
+ // Round to the nearest whole number (and convert back to a float) so the countdown is synched with it.
+ float(RoundToNearest(randomtime));
+
tInfect = CreateTimer(randomtime, InfectMotherZombie, _, TIMER_FLAG_NO_MAPCHANGE);
+
+ // Check cvar and start a countdown timer if enabled.
+ new bool:countdown = GetConVarBool(g_hCvarsList[CVAR_INFECT_MZOMBIE_COUNTDOWN]);
+ if (countdown)
+ {
+ // Store the time until infection, and initialize the counter.
+ new Handle:hCountdownData = CreateDataPack();
+ WritePackFloat(hCountdownData, randomtime);
+ WritePackFloat(hCountdownData, 0.0);
+ tInfectCountdown = CreateTimer(1.0, InfectCountdown, hCountdownData, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
+
+ // Display initial tick.
+ InfectCountdown(tInfectCountdown, hCountdownData);
+ }
}
/**
@@ -551,6 +564,49 @@ public Action:InfectMotherZombie(Handle:timer)
CloseHandle(arrayEligibleClients);
}
+/**
+ * Timer callback, displays countdown to clients.
+ *
+ * @param timer The timer handle.
+ */
+public Action:InfectCountdown(Handle:timer, Handle:hCountdownData)
+{
+ new bool:countdown = GetConVarBool(g_hCvarsList[CVAR_INFECT_MZOMBIE_COUNTDOWN]);
+ if (!countdown)
+ {
+ // Kill the timer.
+ ZREndTimer(tInfectCountdown, false);
+ CloseHandle(hCountdownData);
+ return Plugin_Stop;
+ }
+
+ // Read the info from the datapack.
+ ResetPack(hCountdownData);
+ new Float:length = ReadPackFloat(hCountdownData);
+ new Float:counter = ReadPackFloat(hCountdownData);
+
+ // Check if the countdown has finished.
+ if (counter >= length)
+ {
+ // Kill timer.
+ ZREndTimer(tInfectCountdown, false);
+ CloseHandle(hCountdownData);
+ return Plugin_Stop;
+ }
+
+ // Print the countdown text to the clients.
+ TranslationPrintCenterTextAll(false, "Infect countdown", RoundToNearest(length - counter));
+
+ counter++;
+
+ // Write the new counter value to the datapack.
+ ResetPack(hCountdownData);
+ WritePackFloat(hCountdownData, length);
+ WritePackFloat(hCountdownData, counter);
+
+ return Plugin_Continue;
+}
+
/**
* Infects a client. Execute events, sets attributes and flags that indicate
* that the client is a zombie.
diff --git a/src/zr/translation.inc b/src/zr/translation.inc
index 812fdae..9e72e1c 100644
--- a/src/zr/translation.inc
+++ b/src/zr/translation.inc
@@ -274,6 +274,46 @@ stock TranslationPrintCenterText(client, any:...)
PrintCenterText(client, translation);
}
+/**
+ * Print center text to all clients. (with style)
+ *
+ * @param client The client index.
+ * @param ... Translation formatting parameters.
+ */
+stock TranslationPrintCenterTextAll(bool:admin, any:...)
+{
+ for (new client = 1; client <= MaxClients; client++)
+ {
+ // Skip clients not in game.
+ if (!IsClientInGame(client))
+ {
+ continue;
+ }
+
+ // Skip clients who haven't selected a team yet (team menu open).
+ if (GetClientTeam(client) == CS_TEAM_NONE)
+ {
+ continue;
+ }
+
+ // Skip non-admins if only printing to admins.
+ if (admin && !ZRIsClientAdmin(client))
+ {
+ continue;
+ }
+
+ // Set translation target
+ SetGlobalTransTarget(client);
+
+ // Translate phrase.
+ decl String:translation[TRANSLATION_MAX_LENGTH_CHAT];
+ VFormat(translation, sizeof(translation), "%t", 2);
+
+ // Print translated phrase to client.
+ PrintCenterText(client, translation);
+ }
+}
+
/**
* Print HUD text to client. (with style)
*