From 831e6416cc0a6032d00c96e3ccccab5a7ad472de Mon Sep 17 00:00:00 2001
From: z411 <z411@omaera.org>
Date: Thu, 15 Oct 2020 20:58:22 -0300
Subject: [PATCH] Proper timeout and no rounds in warmup

---
 qcsrc/client/hud/panel/scoreboard.qc          |  5 ++--
 qcsrc/client/hud/panel/timer.qc               | 17 ++++++++++---
 qcsrc/common/monsters/sv_monsters.qc          |  4 +--
 .../mutators/mutator/bloodloss/bloodloss.qc   |  2 +-
 .../common/mutators/mutator/buffs/sv_buffs.qc | 12 ++++-----
 .../mutator/campcheck/sv_campcheck.qc         |  2 +-
 .../mutators/mutator/instagib/sv_instagib.qc  |  2 +-
 qcsrc/common/stats.qh                         | 17 ++++++++++---
 qcsrc/ecs/systems/sv_physics.qc               |  2 +-
 qcsrc/server/command/cmd.qc                   |  4 +--
 qcsrc/server/command/common.qc                | 22 +++++++++++++---
 qcsrc/server/command/sv_cmd.qc                | 25 +++++++++++++++++++
 qcsrc/server/command/vote.qc                  |  1 +
 qcsrc/server/items/items.qc                   | 12 +++++----
 qcsrc/server/round_handler.qc                 | 18 +++++++++++--
 qcsrc/server/round_handler.qh                 |  4 ++-
 qcsrc/server/weapons/weaponsystem.qc          |  2 +-
 qcsrc/server/world.qc                         |  8 +++++-
 18 files changed, 123 insertions(+), 36 deletions(-)

diff --git a/qcsrc/client/hud/panel/scoreboard.qc b/qcsrc/client/hud/panel/scoreboard.qc
index 579ff95584..12eb71cd7e 100644
--- a/qcsrc/client/hud/panel/scoreboard.qc
+++ b/qcsrc/client/hud/panel/scoreboard.qc
@@ -1663,6 +1663,7 @@ vector MapStats_DrawKeyValue(vector pos, string key, string value) {
 	return pos;
 }
 
+/*
 vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size) {
 	float stat_secrets_found, stat_secrets_total;
 	float stat_monsters_killed, stat_monsters_total;
@@ -1731,7 +1732,7 @@ vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size) {
 	panel_size.x += panel_bg_padding * 2; // restore initial width
 	return end_pos;
 }
-
+*/
 
 vector Scoreboard_Rankings_Draw(vector pos, string ranktitle, entity pl, vector rgb, vector bg_size)
 {
@@ -2223,7 +2224,7 @@ void Scoreboard_Draw()
 		pos = Scoreboard_Rankings_Draw(pos, ranktitle, playerslots[player_localnum], panel_bg_color, bg_size);
 	}
 
-	pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
+	//pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
 
 	// List spectators
 	for(pl = players.sort_next; pl; pl = pl.sort_next)
diff --git a/qcsrc/client/hud/panel/timer.qc b/qcsrc/client/hud/panel/timer.qc
index 293247f951..65abb0d272 100644
--- a/qcsrc/client/hud/panel/timer.qc
+++ b/qcsrc/client/hud/panel/timer.qc
@@ -39,10 +39,13 @@ void HUD_Timer()
 
 	string timer;
 	string timer_sub = "";
-	float timelimit, timeleft, minutesLeft, overtimes;
+	bool game_timeout;
+	float timelimit, timeleft, minutesLeft, overtimes, timeout_last;
 
 	timelimit = STAT(TIMELIMIT);
 	overtimes = STAT(OVERTIMESADDED);
+	game_timeout = STAT(GAME_TIMEOUT);
+	timeout_last = STAT(TIMEOUT_LAST);
 
 	timeleft = max(0, timelimit * 60 + STAT(GAMESTARTTIME) - time);
 	timeleft = ceil(timeleft);
@@ -72,12 +75,20 @@ void HUD_Timer()
 		timer = seconds_tostring(max(0, floor(intermission_time - STAT(GAMESTARTTIME))));
 		timer_sub = "Intermission";
 	//} else if (autocvar_hud_panel_timer_increment || (!warmup_stage && timelimit == 0) || (warmup_stage && warmup_timeleft <= 0)) {
+	} else if (game_timeout) {
+		if(autocvar_hud_panel_timer_increment)
+			timer = seconds_tostring(max(0, floor(timeout_last - STAT(GAMESTARTTIME))));
+		else
+			timer = seconds_tostring(ceil(max(0, timelimit * 60 + STAT(GAMESTARTTIME) - timeout_last)));
+		timer_sub = "Timeout";
 	} else if (autocvar_hud_panel_timer_increment || timelimit == 0) {
+		// Time elapsed timer
 		if((warmup_stage && warmup_timeleft <= 0) || time < STAT(GAMESTARTTIME))
 			timer = seconds_tostring(0);
 		else
 			timer = seconds_tostring(floor(time - STAT(GAMESTARTTIME)));
 	} else {
+		// Time left timer
 		if(warmup_stage) {
 			if(warmup_timeleft <= 0)
 				timer = seconds_tostring(floor(timelimit * 60));
@@ -86,8 +97,8 @@ void HUD_Timer()
 		} else {
 			if (time < STAT(GAMESTARTTIME))
 				timer = seconds_tostring(floor(timelimit * 60));
-			else if (overtimes > 0)
-				timer = seconds_tostring(floor(time - STAT(OVERTIMESTARTTIME)));
+			//else if (overtimes > 0)
+			//	timer = seconds_tostring(floor(time - STAT(OVERTIMESTARTTIME)));
 			else
 				timer = seconds_tostring(timeleft);
 		}
diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc
index d5bff8f023..ba1d4941ff 100644
--- a/qcsrc/common/monsters/sv_monsters.qc
+++ b/qcsrc/common/monsters/sv_monsters.qc
@@ -31,8 +31,8 @@
 
 void monsters_setstatus(entity this)
 {
-	STAT(MONSTERS_TOTAL, this) = monsters_total;
-	STAT(MONSTERS_KILLED, this) = monsters_killed;
+	//STAT(MONSTERS_TOTAL, this) = monsters_total;
+	//STAT(MONSTERS_KILLED, this) = monsters_killed;
 }
 
 void monster_dropitem(entity this, entity attacker)
diff --git a/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc b/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc
index 41ceaa91fa..3992d985a3 100644
--- a/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc
+++ b/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc
@@ -14,7 +14,7 @@ MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink)
 {
 	entity player = M_ARGV(0, entity);
 
-	if(game_stopped)
+	if(game_stopped || game_timeout)
 		return; // during intermission, the player's health changes to strange values for the engine, let's not cause damage during this phase!
 
 	if(IS_PLAYER(player))
diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
index 30cef339a6..1c0327dcf1 100644
--- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
+++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
@@ -119,7 +119,7 @@ void buff_SetCooldown(entity this, float cd)
 
 void buff_Respawn(entity this)
 {
-	if(game_stopped) return;
+	if(game_stopped || game_timeout) return;
 
 	vector oldbufforigin = this.origin;
 	this.velocity = '0 0 200';
@@ -154,7 +154,7 @@ void buff_Respawn(entity this)
 
 void buff_Touch(entity this, entity toucher)
 {
-	if(game_stopped) return;
+	if(game_stopped || game_timeout) return;
 
 	if(ITEM_TOUCH_NEEDKILL())
 	{
@@ -269,7 +269,7 @@ void buff_Think(entity this)
 		this.oldbuffs = STAT(BUFFS, this);
 	}
 
-	if(!game_stopped)
+	if(!game_stopped && !game_timeout)
 	if((round_handler_IsActive() && round_handler_IsRoundStarted()) || time >= game_starttime)
 	if(!this.buff_activetime_updated)
 	{
@@ -290,7 +290,7 @@ void buff_Think(entity this)
 	}
 
 	if(this.buff_activetime)
-	if(!game_stopped)
+	if(!game_stopped && !game_timeout)
 	if((round_handler_IsActive() && round_handler_IsRoundStarted()) || time >= game_starttime)
 	{
 		this.buff_activetime = max(0, this.buff_activetime - frametime);
@@ -667,7 +667,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST)
 
 MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon)
 {
-	if(MUTATOR_RETURNVALUE || game_stopped) return;
+	if(MUTATOR_RETURNVALUE || game_stopped || game_timeout) return;
 	entity player = M_ARGV(0, entity);
 
 	if(STAT(BUFFS, player) & BUFF_SWAPPER.m_itemid)
@@ -827,7 +827,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
 {
 	entity player = M_ARGV(0, entity);
 
-	if(game_stopped || IS_DEAD(player) || frametime || !IS_PLAYER(player)) return;
+	if(game_stopped || game_timeout || IS_DEAD(player) || frametime || !IS_PLAYER(player)) return;
 
 	if(STAT(BUFFS, player) & BUFF_FLIGHT.m_itemid)
 	{
diff --git a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc
index f53d8c356a..a2370a48c0 100644
--- a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc
+++ b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc
@@ -38,7 +38,7 @@ MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink)
 	bool checked = false;
 
 	if(autocvar_g_campcheck_interval)
-	if(!game_stopped && !warmup_stage && time >= game_starttime)
+	if(!game_stopped && !game_timeout && !warmup_stage && time >= game_starttime)
 	if(IS_PLAYER(player) && !IS_DEAD(player) && !STAT(FROZEN, player))
 	if(autocvar_g_campcheck_typecheck || !PHYS_INPUT_BUTTON_CHAT(player))
 	if(IS_REAL_CLIENT(player)) // only apply to real clients (bots may "camp" due to missing waypoints in the map, but that's no reason to constantly kill them, clones can't move)
diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
index a23fc36970..3e3f7ac846 100644
--- a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
+++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
@@ -79,7 +79,7 @@ void instagib_ammocheck(entity this)
 	if(!IS_PLAYER(this))
 		return; // not a player
 
-	if(IS_DEAD(this) || game_stopped)
+	if(IS_DEAD(this) || game_stopped || game_timeout)
 		instagib_stop_countdown(this);
 	else if (GetResource(this, RES_CELLS) > 0 || (this.items & IT_UNLIMITED_AMMO) || (this.flags & FL_GODMODE))
 		instagib_stop_countdown(this);
diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh
index 2a614e427d..49be5178ce 100644
--- a/qcsrc/common/stats.qh
+++ b/qcsrc/common/stats.qh
@@ -67,12 +67,21 @@ float game_stopped;
 float game_starttime; //point in time when the countdown to game start is over
 float round_starttime; //point in time when the countdown to round start is over
 float overtime_starttime; // z411 point in time where first overtime started
+
 float checkrules_overtimesadded; // z411 add
+float timeout_last;
+float timeout_total_time;
+bool game_timeout;
+
 bool autocvar_g_allow_oldvortexbeam;
 int autocvar_leadlimit;
 #endif
 REGISTER_STAT(WEAPONRATEFACTOR, float, W_WeaponRateFactor(this))
 REGISTER_STAT(GAME_STOPPED, int, game_stopped)
+
+REGISTER_STAT(GAME_TIMEOUT, bool, game_timeout)
+REGISTER_STAT(TIMEOUT_LAST, float, timeout_last)
+
 REGISTER_STAT(GAMESTARTTIME, float, game_starttime)
 REGISTER_STAT(STRENGTH_FINISHED, float)
 REGISTER_STAT(INVINCIBLE_FINISHED, float)
@@ -103,14 +112,14 @@ REGISTER_STAT(VEHICLESTAT_AMMO2, int)
 REGISTER_STAT(VEHICLESTAT_RELOAD2, int)
 REGISTER_STAT(VEHICLESTAT_W2MODE, int)
 REGISTER_STAT(NADE_TIMER, float)
-REGISTER_STAT(SECRETS_TOTAL, int, secrets_total)
-REGISTER_STAT(SECRETS_FOUND, int, secrets_found)
+//REGISTER_STAT(SECRETS_TOTAL, int, secrets_total)
+//REGISTER_STAT(SECRETS_FOUND, int, secrets_found)
 REGISTER_STAT(RESPAWN_TIME, float)
 REGISTER_STAT(ROUNDSTARTTIME, float, round_starttime)
 REGISTER_STAT(OVERTIMESTARTTIME, float, overtime_starttime)
 REGISTER_STAT(OVERTIMESADDED, float, checkrules_overtimesadded)
-REGISTER_STAT(MONSTERS_TOTAL, int)
-REGISTER_STAT(MONSTERS_KILLED, int)
+//REGISTER_STAT(MONSTERS_TOTAL, int)
+//REGISTER_STAT(MONSTERS_KILLED, int)
 REGISTER_STAT(BUFFS, int)
 REGISTER_STAT(NADE_BONUS, float)
 REGISTER_STAT(NADE_BONUS_TYPE, int)
diff --git a/qcsrc/ecs/systems/sv_physics.qc b/qcsrc/ecs/systems/sv_physics.qc
index 1d5553d1d1..694359cbe1 100644
--- a/qcsrc/ecs/systems/sv_physics.qc
+++ b/qcsrc/ecs/systems/sv_physics.qc
@@ -42,7 +42,7 @@ void sys_phys_pregame_hold(entity this)
 	if (!IS_PLAYER(this)) { return; }
 	// z411
 	//const bool allowed_to_move = (time >= game_starttime && !game_stopped);
-	const bool allowed_to_move = (!game_stopped);
+	const bool allowed_to_move = (!game_stopped && !game_timeout);
 	if (!allowed_to_move) {
 		this.velocity = '0 0 0';
 		set_movetype(this, MOVETYPE_NONE);
diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc
index b1d568ba13..0ba8ed3006 100644
--- a/qcsrc/server/command/cmd.qc
+++ b/qcsrc/server/command/cmd.qc
@@ -285,7 +285,7 @@ void ClientCommand_join(entity caller, int request)
 	{
 		case CMD_REQUEST_COMMAND:
 		{
-			if (!game_stopped && IS_CLIENT(caller) && !IS_PLAYER(caller))
+			if (!game_stopped && !game_timeout && IS_CLIENT(caller) && !IS_PLAYER(caller))
 			{
 				if (joinAllowed(caller))
 					Join(caller);
@@ -636,7 +636,7 @@ void ClientCommand_spectate(entity caller, int request)
 	{
 		case CMD_REQUEST_COMMAND:
 		{
-			if (!intermission_running && IS_CLIENT(caller))
+			if (!intermission_running && IS_CLIENT(caller) && !game_timeout)
 			{
 				if(argv(1) != "")
 				{
diff --git a/qcsrc/server/command/common.qc b/qcsrc/server/command/common.qc
index c27c5ac337..94ea014e9f 100644
--- a/qcsrc/server/command/common.qc
+++ b/qcsrc/server/command/common.qc
@@ -212,7 +212,8 @@ void timeout_handler_think(entity this)
 				if (timeout_time == autocvar_sv_timeout_resumetime) // play a warning sound when only <sv_timeout_resumetime> seconds are left
 					Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_PREPARE);
 
-				this.nextthink = time + TIMEOUT_SLOWMO_VALUE;       // think again in one second
+				//this.nextthink = time + TIMEOUT_SLOWMO_VALUE;       // think again in one second
+				this.nextthink = time + 1;
 				timeout_time -= 1;                                  // decrease the time counter
 			}
 			else if (timeout_time == -1)  // infinite timer
@@ -224,9 +225,20 @@ void timeout_handler_think(entity this)
 			{
 				Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_TIMEIN);
 				timeout_status = TIMEOUT_INACTIVE;
+				float total_time = time - timeout_last;
 
 				// reset the slowmo value back to normal
-				cvar_set("slowmo", ftos(orig_slowmo));
+				// z411 TODO
+				//cvar_set("slowmo", ftos(orig_slowmo));
+				
+				// Disable timeout and fix times
+				game_timeout = false;
+				timeout_total_time += total_time;
+				game_starttime += total_time;
+				if(round_handler && round_handler_GetEndTime() > 0)
+					round_handler.round_endtime += total_time;
+					
+				LOG_INFOF("timeout lasted %d secs", total_time);
 
 				// unlock the view for players so they can move around again
 				FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), {
@@ -253,7 +265,11 @@ void timeout_handler_think(entity this)
 				timeout_status = TIMEOUT_ACTIVE;
 
 				// set the slowmo value to the timeout default slowmo value
-				cvar_set("slowmo", ftos(TIMEOUT_SLOWMO_VALUE));
+				//cvar_set("slowmo", ftos(TIMEOUT_SLOWMO_VALUE));
+				game_timeout = true;
+				timeout_last = time;
+				
+				// play timeout sound
 				sound(NULL, CH_INFO, SND_TIMEOUT, VOL_BASE, ATTN_NONE);
 
 				// reset all the flood variables
diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc
index 526775c7c8..4fe4c7c45b 100644
--- a/qcsrc/server/command/sv_cmd.qc
+++ b/qcsrc/server/command/sv_cmd.qc
@@ -235,6 +235,30 @@ void GameCommand_allready(int request)
 	}
 }
 
+// z411 TODO
+void GameCommand_stop(int request, int argc)
+{
+	switch (request)
+	{
+		case CMD_REQUEST_COMMAND:
+		{
+			if(argv(1) == "true")
+				game_stopped = true;
+			else
+				game_stopped = false;
+			return;
+		}
+
+		default:
+		case CMD_REQUEST_USAGE:
+		{
+			LOG_HELP("Usage:^3 sv_cmd stop");
+			LOG_HELP("  No arguments required.");
+			return;
+		}
+	}
+}
+
 void GameCommand_allspec(int request, int argc)
 {
 	switch (request)
@@ -1712,6 +1736,7 @@ void GameCommand_(int request)
 // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;)
 SERVER_COMMAND(setflag, "Set client flag") { GameCommand_setflag(request, arguments); }
 SERVER_COMMAND(teamname, "Set team name") { GameCommand_teamname(request, arguments); }
+SERVER_COMMAND(stop, "Stop") { GameCommand_stop(request, arguments); }
 
 SERVER_COMMAND(adminmsg, "Send an admin message to a client directly") { GameCommand_adminmsg(request, arguments); }
 SERVER_COMMAND(allready, "Restart the server and reset the players") { GameCommand_allready(request); }
diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc
index edd898546e..8306349cf3 100644
--- a/qcsrc/server/command/vote.qc
+++ b/qcsrc/server/command/vote.qc
@@ -481,6 +481,7 @@ void ReadyRestart_force()
 		FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { CS(it).allowed_timeouts = autocvar_sv_timeout_number; });
 	}
 
+	round_handler_Activate(true);
 	if (!sv_ready_restart_after_countdown) reset_map(true);
 	if (autocvar_sv_eventlog) GameLogEcho(":restart");
 }
diff --git a/qcsrc/server/items/items.qc b/qcsrc/server/items/items.qc
index 861b21eed2..906e89aeaf 100644
--- a/qcsrc/server/items/items.qc
+++ b/qcsrc/server/items/items.qc
@@ -280,12 +280,14 @@ void Item_RespawnCountdown(entity this)
 
 void Item_RespawnThink(entity this)
 {
-	this.nextthink = time;
+	this.nextthink = time + 1;
 	if(this.origin != this.oldorigin)
 		ItemUpdate(this);
-
-	if(time >= this.wait)
+	
+	if(!game_timeout && time - timeout_total_time >= this.wait)
 		Item_Respawn(this);
+	
+	//LOG_INFOF("time until respawn %d", (this.wait) - (time - timeout_total_time));
 }
 
 void Item_ScheduleRespawnIn(entity e, float t)
@@ -308,8 +310,8 @@ void Item_ScheduleRespawnIn(entity e, float t)
 	{
 		setthink(e, Item_RespawnThink);
 		e.nextthink = time;
-		e.scheduledrespawntime = time + t;
-		e.wait = time + t;
+		e.scheduledrespawntime = time - timeout_total_time + t;
+		e.wait = time - timeout_total_time + t;
 
 		if(Item_ItemsTime_Allow(e.itemdef) || (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS))
 		{
diff --git a/qcsrc/server/round_handler.qc b/qcsrc/server/round_handler.qc
index 66da6c37c1..9eb8963c8f 100644
--- a/qcsrc/server/round_handler.qc
+++ b/qcsrc/server/round_handler.qc
@@ -9,6 +9,8 @@
 
 void round_handler_Think(entity this)
 {
+	if (game_timeout) { this.nextthink = time + 1; return; }
+	
 	if (intermission_running)
 	{
 		round_handler_Reset(0);
@@ -96,13 +98,25 @@ void round_handler_Spawn(bool() canRoundStart_func, bool() canRoundEnd_func, voi
 	}
 	entity this = round_handler = new(round_handler);
 
-	setthink(this, round_handler_FirstThink);
+	
 	this.canRoundStart = canRoundStart_func;
 	this.canRoundEnd = canRoundEnd_func;
 	this.roundStart = roundStart_func;
 	this.wait = false;
 	round_handler_Init(5, 5, 180);
-	this.nextthink = time;
+	
+}
+
+void round_handler_Activate(bool active) {
+	if (round_handler) {
+		entity this = round_handler;
+	
+		this.isactive = active;
+		if(active) {
+			setthink(this, round_handler_FirstThink);
+			this.nextthink = time;
+		}
+	}
 }
 
 void round_handler_Reset(float next_think)
diff --git a/qcsrc/server/round_handler.qh b/qcsrc/server/round_handler.qh
index 5979eb5c33..01288ab646 100644
--- a/qcsrc/server/round_handler.qh
+++ b/qcsrc/server/round_handler.qh
@@ -1,6 +1,7 @@
 #pragma once
 
 entity round_handler;
+.bool isactive;
 .float delay; // stores delay from round end to countdown start
 .float count; // stores initial number of the countdown
 .bool wait; // it's set to true when round ends, to false when countdown starts
@@ -15,9 +16,10 @@ entity round_handler;
 void round_handler_Init(float the_delay, float the_count, float the_round_timelimit);
 void round_handler_Spawn(bool() canRoundStart_func, bool() canRoundEnd_func, void() roundStart_func);
 void round_handler_Reset(float next_think);
+void round_handler_Activate(bool active);
 void round_handler_Remove();
 
-#define round_handler_IsActive() (round_handler != NULL)
+#define round_handler_IsActive() (round_handler != NULL && round_handler.isactive)
 #define round_handler_AwaitingNextRound() (round_handler.wait)
 #define round_handler_CountdownRunning() (!round_handler.wait && round_handler.cnt)
 #define round_handler_IsRoundStarted() (!round_handler.wait && !round_handler.cnt)
diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc
index 7638813882..b3112fe3bf 100644
--- a/qcsrc/server/weapons/weaponsystem.qc
+++ b/qcsrc/server/weapons/weaponsystem.qc
@@ -440,7 +440,7 @@ bool weaponLocked(entity player)
 {
 	if (time < game_starttime && !sv_ready_restart_after_countdown) return true;
 	if (player.player_blocked) return true;
-	if (game_stopped) return true;
+	if (game_stopped || game_timeout) return true;
 	if (STAT(FROZEN, player)) return true;
 	if (MUTATOR_CALLHOOK(LockWeapon, player)) return true;
 	return false;
diff --git a/qcsrc/server/world.qc b/qcsrc/server/world.qc
index 891131b38d..309a42123e 100644
--- a/qcsrc/server/world.qc
+++ b/qcsrc/server/world.qc
@@ -1017,9 +1017,12 @@ spawnfunc(worldspawn)
 	modname = strzone(modname);
 
 	WinningConditionHelper(this); // set worldstatus
-
+	
 	world_initialized = 1;
 	__spawnfunc_spawn_all();
+	
+	if(!warmup_stage)
+		round_handler_Activate(true);
 }
 
 spawnfunc(light)
@@ -1955,6 +1958,9 @@ void CheckRules_World()
 			// again, but this shouldn't hurt
 		return;
 	}
+	
+	// z411 don't check rules if we're in a timeout
+	if (game_timeout) return;
 
 	float timelimit = autocvar_timelimit * 60;
 	float fraglimit = autocvar_fraglimit;
-- 
2.39.5