about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authoroy <Tom_Adams@web.de>2012-01-10 00:49:31 +0100
committeroy <Tom_Adams@web.de>2012-01-10 00:49:31 +0100
commitdfe91338f03534a6c8ed596f16742e7a1617d78e (patch)
tree4f2f93488745628c56dbd4d29d8b482dc584a724 /src
parent9251796c3a8e3f31f9aa52ef84b9e0ce475d2f9b (diff)
downloadzcatch-dfe91338f03534a6c8ed596f16742e7a1617d78e.tar.gz
zcatch-dfe91338f03534a6c8ed596f16742e7a1617d78e.zip
added pause command. Closes #15
Diffstat (limited to 'src')
-rw-r--r--src/game/client/components/controls.cpp2
-rw-r--r--src/game/client/components/damageind.cpp5
-rw-r--r--src/game/client/components/items.cpp45
-rw-r--r--src/game/client/components/maplayers.cpp22
-rw-r--r--src/game/client/components/particles.cpp10
-rw-r--r--src/game/client/components/players.cpp31
-rw-r--r--src/game/server/entities/character.cpp14
-rw-r--r--src/game/server/entities/character.h1
-rw-r--r--src/game/server/entities/flag.cpp7
-rw-r--r--src/game/server/entities/flag.h1
-rw-r--r--src/game/server/entities/laser.cpp5
-rw-r--r--src/game/server/entities/laser.h1
-rw-r--r--src/game/server/entities/pickup.cpp6
-rw-r--r--src/game/server/entities/pickup.h1
-rw-r--r--src/game/server/entities/projectile.cpp5
-rw-r--r--src/game/server/entities/projectile.h1
-rw-r--r--src/game/server/entity.h6
-rw-r--r--src/game/server/gamecontext.cpp11
-rw-r--r--src/game/server/gamecontext.h1
-rw-r--r--src/game/server/gamecontroller.cpp6
-rw-r--r--src/game/server/gamecontroller.h1
-rw-r--r--src/game/server/gameworld.cpp11
-rw-r--r--src/game/server/player.cpp41
23 files changed, 180 insertions, 54 deletions
diff --git a/src/game/client/components/controls.cpp b/src/game/client/components/controls.cpp
index dd856413..c0790799 100644
--- a/src/game/client/components/controls.cpp
+++ b/src/game/client/components/controls.cpp
@@ -199,7 +199,7 @@ int CControls::SnapInput(int *pData)
 void CControls::OnRender()
 {
 	// update target pos
-	if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED || m_pClient->m_Snap.m_SpecInfo.m_Active))
+	if(m_pClient->m_Snap.m_pGameInfoObj && !m_pClient->m_Snap.m_SpecInfo.m_Active)
 		m_TargetPos = m_pClient->m_LocalCharacterPos + m_MousePos;
 	else if(m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_UsePosition)
 		m_TargetPos = m_pClient->m_Snap.m_SpecInfo.m_Position + m_MousePos;
diff --git a/src/game/client/components/damageind.cpp b/src/game/client/components/damageind.cpp
index e2da21e1..0f521753 100644
--- a/src/game/client/components/damageind.cpp
+++ b/src/game/client/components/damageind.cpp
@@ -60,6 +60,11 @@ void CDamageInd::OnRender()
 			else
 				m_aItems[i].m_StartTime += (Client()->LocalTime()-s_LastLocalTime)*(1.0f-pInfo->m_Speed);
 		}
+		else
+		{
+			if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)
+				m_aItems[i].m_StartTime += Client()->LocalTime()-s_LastLocalTime;
+		}
 
 		float Life = 0.75f - (Client()->LocalTime() - m_aItems[i].m_StartTime);
 		if(Life < 0.0f)
diff --git a/src/game/client/components/items.cpp b/src/game/client/components/items.cpp
index 9f787a4b..e1032a51 100644
--- a/src/game/client/components/items.cpp
+++ b/src/game/client/components/items.cpp
@@ -41,7 +41,10 @@ void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID)
 		Speed = m_pClient->m_Tuning.m_GunSpeed;
 	}
 
-	float Ct = (Client()->PrevGameTick()-pCurrent->m_StartTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime();
+	static float s_LastGameTickTime = Client()->GameTickTime();
+	if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED))
+		s_LastGameTickTime = Client()->GameTickTime();
+	float Ct = (Client()->PrevGameTick()-pCurrent->m_StartTick)/(float)SERVER_TICK_SPEED + s_LastGameTickTime;
 	if(Ct < 0)
 		return; // projectile havn't been shot yet
 
@@ -63,22 +66,23 @@ void CItems::RenderProjectile(const CNetObj_Projectile *pCurrent, int ItemID)
 	if(pCurrent->m_Type == WEAPON_GRENADE)
 	{
 		m_pClient->m_pEffects->SmokeTrail(Pos, Vel*-1);
+		static float s_Time = 0.0f;
+		static float s_LastLocalTime = Client()->LocalTime();
 
 		if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
 		{
 			const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
-			static float Time = 0;
-			static float LastLocalTime = Client()->LocalTime();
-
 			if(!pInfo->m_Paused)
-				Time += (Client()->LocalTime()-LastLocalTime)*pInfo->m_Speed;
-
-			Graphics()->QuadsSetRotation(Time*pi*2*2 + ItemID);
-
-			LastLocalTime = Client()->LocalTime();
+				s_Time += (Client()->LocalTime()-s_LastLocalTime)*pInfo->m_Speed;
 		}
 		else
-			Graphics()->QuadsSetRotation(Client()->LocalTime()*pi*2*2 + ItemID);
+		{
+			if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED))
+				s_Time += Client()->LocalTime()-s_LastLocalTime;
+		}
+
+		Graphics()->QuadsSetRotation(s_Time*pi*2*2 + ItemID);
+		s_LastLocalTime = Client()->LocalTime();
 	}
 	else
 	{
@@ -130,26 +134,23 @@ void CItems::RenderPickup(const CNetObj_Pickup *pPrev, const CNetObj_Pickup *pCu
 
 	Graphics()->QuadsSetRotation(Angle);
 
+	static float s_Time = 0.0f;
+	static float s_LastLocalTime = Client()->LocalTime();
 	float Offset = Pos.y/32.0f + Pos.x/32.0f;
 	if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
 	{
 		const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
-		static float Time = 0;
-		static float LastLocalTime = Client()->LocalTime();
-
 		if(!pInfo->m_Paused)
-			Time += (Client()->LocalTime()-LastLocalTime)*pInfo->m_Speed;
-
-		Pos.x += cosf(Time*2.0f+Offset)*2.5f;
-		Pos.y += sinf(Time*2.0f+Offset)*2.5f;
-
-		LastLocalTime = Client()->LocalTime();
+			s_Time += (Client()->LocalTime()-s_LastLocalTime)*pInfo->m_Speed;
 	}
 	else
 	{
-		Pos.x += cosf(Client()->LocalTime()*2.0f+Offset)*2.5f;
-		Pos.y += sinf(Client()->LocalTime()*2.0f+Offset)*2.5f;
-	}
+		if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED))
+			s_Time += Client()->LocalTime()-s_LastLocalTime;
+ 	}
+	Pos.x += cosf(s_Time*2.0f+Offset)*2.5f;
+	Pos.y += sinf(s_Time*2.0f+Offset)*2.5f;
+	s_LastLocalTime = Client()->LocalTime();
 	RenderTools()->DrawSprite(Pos.x, Pos.y, Size);
 	Graphics()->QuadsEnd();
 }
diff --git a/src/game/client/components/maplayers.cpp b/src/game/client/components/maplayers.cpp
index f0460d26..db74b165 100644
--- a/src/game/client/components/maplayers.cpp
+++ b/src/game/client/components/maplayers.cpp
@@ -77,7 +77,8 @@ void CMapLayers::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void
 
 	CMapItemEnvelope *pItem = (CMapItemEnvelope *)pThis->m_pLayers->Map()->GetItem(Start+Env, 0, 0);
 
-	static float Time = 0;
+	static float s_Time = 0.0f;
+	static float s_LastLocalTime = pThis->Client()->LocalTime();
 	if(pThis->Client()->State() == IClient::STATE_DEMOPLAYBACK)
 	{
 		const IDemoPlayer::CInfo *pInfo = pThis->DemoPlayer()->BaseInfo();
@@ -90,25 +91,28 @@ void CMapLayers::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void
 				pThis->m_CurrentLocalTick = pInfo->m_CurrentTick;
 			}
 
-			Time = mix(pThis->m_LastLocalTick / (float)pThis->Client()->GameTickSpeed(),
+			s_Time = mix(pThis->m_LastLocalTick / (float)pThis->Client()->GameTickSpeed(),
 						pThis->m_CurrentLocalTick / (float)pThis->Client()->GameTickSpeed(),
 						pThis->Client()->IntraGameTick());
 		}
 
-		pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, Time+TimeOffset, pChannels);
+		pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, s_Time+TimeOffset, pChannels);
 	}
 	else
 	{
-		if(pItem->m_Version < 2 || pItem->m_Synchronized)
+		if(pThis->m_pClient->m_Snap.m_pGameInfoObj && !(pThis->m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED))
 		{
-			if(pThis->m_pClient->m_Snap.m_pGameInfoObj)
-				Time = mix((pThis->Client()->PrevGameTick()-pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)pThis->Client()->GameTickSpeed(),
+			if(pItem->m_Version < 2 || pItem->m_Synchronized)
+			{
+				s_Time = mix((pThis->Client()->PrevGameTick()-pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)pThis->Client()->GameTickSpeed(),
 							(pThis->Client()->GameTick()-pThis->m_pClient->m_Snap.m_pGameInfoObj->m_RoundStartTick) / (float)pThis->Client()->GameTickSpeed(),
 							pThis->Client()->IntraGameTick());
+			}
+			else
+				s_Time += pThis->Client()->LocalTime()-s_LastLocalTime;
 		}
-		else
-			Time = pThis->Client()->LocalTime();
-		pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, Time+TimeOffset, pChannels);
+		pThis->RenderTools()->RenderEvalEnvelope(pPoints+pItem->m_StartPoint, pItem->m_NumPoints, 4, s_Time+TimeOffset, pChannels);
+		s_LastLocalTime = pThis->Client()->LocalTime();
 	}
 }
 
diff --git a/src/game/client/components/particles.cpp b/src/game/client/components/particles.cpp
index c4583cb1..66a62f1c 100644
--- a/src/game/client/components/particles.cpp
+++ b/src/game/client/components/particles.cpp
@@ -43,6 +43,11 @@ void CParticles::Add(int Group, CParticle *pPart)
 		if(pInfo->m_Paused)
 			return;
 	}
+	else
+	{
+		if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)
+			return;
+	}
 
 	if (m_FirstFree == -1)
 		return;
@@ -142,7 +147,10 @@ void CParticles::OnRender()
 			Update((float)((t-LastTime)/(double)time_freq())*pInfo->m_Speed);
 	}
 	else
-		Update((float)((t-LastTime)/(double)time_freq()));
+	{
+		if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED))
+			Update((float)((t-LastTime)/(double)time_freq()));
+	}
 
 	LastTime = t;
 }
diff --git a/src/game/client/components/players.cpp b/src/game/client/components/players.cpp
index c53a1977..2eddd8a9 100644
--- a/src/game/client/components/players.cpp
+++ b/src/game/client/components/players.cpp
@@ -285,14 +285,17 @@ void CPlayers::RenderPlayer(
 	else if(!WantOtherDir)
 		State.Add(&g_pData->m_aAnimations[ANIM_WALK], WalkTime, 1.0f);
 
+	static float s_LastGameTickTime = Client()->GameTickTime();
+	if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED))
+		s_LastGameTickTime = Client()->GameTickTime();
 	if (Player.m_Weapon == WEAPON_HAMMER)
 	{
-		float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime();
+		float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + s_LastGameTickTime;
 		State.Add(&g_pData->m_aAnimations[ANIM_HAMMER_SWING], clamp(ct*5.0f,0.0f,1.0f), 1.0f);
 	}
 	if (Player.m_Weapon == WEAPON_NINJA)
 	{
-		float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + Client()->GameTickTime();
+		float ct = (Client()->PrevGameTick()-Player.m_AttackTick)/(float)SERVER_TICK_SPEED + s_LastGameTickTime;
 		State.Add(&g_pData->m_aAnimations[ANIM_NINJA_SWING], clamp(ct*2.0f,0.0f,1.0f), 1.0f);
 	}
 
@@ -364,15 +367,22 @@ void CPlayers::RenderPlayer(
 			if ((Client()->GameTick()-Player.m_AttackTick) <= (SERVER_TICK_SPEED / 6) && g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles)
 			{
 				int IteX = rand() % g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles;
+				static int s_LastIteX = IteX;
 				if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
 				{
-					static int s_LastIteX = IteX;
 					const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
 					if(pInfo->m_Paused)
 						IteX = s_LastIteX;
 					else
 						s_LastIteX = IteX;
 				}
+				else
+				{
+					if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)
+						IteX = s_LastIteX;
+					else
+						s_LastIteX = IteX;
+				}
 				if(g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX])
 				{
 					vec2 Dir = vec2(pPlayerChar->m_X,pPlayerChar->m_Y) - vec2(pPrevChar->m_X, pPrevChar->m_Y);
@@ -393,7 +403,11 @@ void CPlayers::RenderPlayer(
 		{
 			// TODO: should be an animation
 			Recoil = 0;
-			float a = (Client()->GameTick()-Player.m_AttackTick+IntraTick)/5.0f;
+			static float s_LastIntraTick = IntraTick;
+			if(m_pClient->m_Snap.m_pGameInfoObj && !(m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED))
+				s_LastIntraTick = IntraTick;
+
+			float a = (Client()->GameTick()-Player.m_AttackTick+s_LastIntraTick)/5.0f;
 			if(a < 1)
 				Recoil = sinf(a*pi);
 			p = Position + Dir * g_pData->m_Weapons.m_aId[iw].m_Offsetx - Dir*Recoil*10.0f;
@@ -415,15 +429,22 @@ void CPlayers::RenderPlayer(
 				}
 
 				int IteX = rand() % g_pData->m_Weapons.m_aId[iw].m_NumSpriteMuzzles;
+				static int s_LastIteX = IteX;
 				if(Client()->State() == IClient::STATE_DEMOPLAYBACK)
 				{
-					static int s_LastIteX = IteX;
 					const IDemoPlayer::CInfo *pInfo = DemoPlayer()->BaseInfo();
 					if(pInfo->m_Paused)
 						IteX = s_LastIteX;
 					else
 						s_LastIteX = IteX;
 				}
+				else
+				{
+					if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_PAUSED)
+						IteX = s_LastIteX;
+					else
+						s_LastIteX = IteX;
+				}
 				if (Alpha > 0.0f && g_pData->m_Weapons.m_aId[iw].m_aSpriteMuzzles[IteX])
 				{
 					float OffsetY = -g_pData->m_Weapons.m_aId[iw].m_Muzzleoffsety;
diff --git a/src/game/server/entities/character.cpp b/src/game/server/entities/character.cpp
index 5e8c29b6..1c76f655 100644
--- a/src/game/server/entities/character.cpp
+++ b/src/game/server/entities/character.cpp
@@ -657,6 +657,20 @@ void CCharacter::TickDefered()
 	}
 }
 
+void CCharacter::TickPaused()
+{
+	++m_AttackTick;
+	++m_DamageTakenTick;
+	++m_Ninja.m_ActivationTick;
+	++m_ReckoningTick;
+	if(m_LastAction != -1)
+		++m_LastAction;
+	if(m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart > -1)
+		++m_aWeapons[m_ActiveWeapon].m_AmmoRegenStart;
+	if(m_EmoteStop > -1)
+		++m_EmoteStop;
+}
+
 bool CCharacter::IncreaseHealth(int Amount)
 {
 	if(m_Health >= 10)
diff --git a/src/game/server/entities/character.h b/src/game/server/entities/character.h
index 02a779bb..aa127979 100644
--- a/src/game/server/entities/character.h
+++ b/src/game/server/entities/character.h
@@ -30,6 +30,7 @@ public:
 	virtual void Destroy();
 	virtual void Tick();
 	virtual void TickDefered();
+	virtual void TickPaused();
 	virtual void Snap(int SnappingClient);
 
 	bool IsGrounded();
diff --git a/src/game/server/entities/flag.cpp b/src/game/server/entities/flag.cpp
index d279e4df..558ee154 100644
--- a/src/game/server/entities/flag.cpp
+++ b/src/game/server/entities/flag.cpp
@@ -23,6 +23,13 @@ void CFlag::Reset()
 	m_GrabTick = 0;
 }
 
+void CFlag::TickPaused()
+{
+	++m_DropTick;
+	if(m_GrabTick)
+		++m_GrabTick;
+}
+
 void CFlag::Snap(int SnappingClient)
 {
 	if(NetworkClipped(SnappingClient))
diff --git a/src/game/server/entities/flag.h b/src/game/server/entities/flag.h
index 2f91bc02..be22d32b 100644
--- a/src/game/server/entities/flag.h
+++ b/src/game/server/entities/flag.h
@@ -21,6 +21,7 @@ public:
 	CFlag(CGameWorld *pGameWorld, int Team);
 
 	virtual void Reset();
+	virtual void TickPaused();
 	virtual void Snap(int SnappingClient);
 };
 
diff --git a/src/game/server/entities/laser.cpp b/src/game/server/entities/laser.cpp
index 7278995f..eb40c4e1 100644
--- a/src/game/server/entities/laser.cpp
+++ b/src/game/server/entities/laser.cpp
@@ -91,6 +91,11 @@ void CLaser::Tick()
 		DoBounce();
 }
 
+void CLaser::TickPaused()
+{
+	++m_EvalTick;
+}
+
 void CLaser::Snap(int SnappingClient)
 {
 	if(NetworkClipped(SnappingClient))
diff --git a/src/game/server/entities/laser.h b/src/game/server/entities/laser.h
index 1d7fa227..8ae6f792 100644
--- a/src/game/server/entities/laser.h
+++ b/src/game/server/entities/laser.h
@@ -12,6 +12,7 @@ public:
 
 	virtual void Reset();
 	virtual void Tick();
+	virtual void TickPaused();
 	virtual void Snap(int SnappingClient);
 
 protected:
diff --git a/src/game/server/entities/pickup.cpp b/src/game/server/entities/pickup.cpp
index ba26d85b..1aff5750 100644
--- a/src/game/server/entities/pickup.cpp
+++ b/src/game/server/entities/pickup.cpp
@@ -117,6 +117,12 @@ void CPickup::Tick()
 	}
 }
 
+void CPickup::TickPaused()
+{
+	if(m_SpawnTick != -1)
+		++m_SpawnTick;
+}
+
 void CPickup::Snap(int SnappingClient)
 {
 	if(m_SpawnTick != -1 || NetworkClipped(SnappingClient))
diff --git a/src/game/server/entities/pickup.h b/src/game/server/entities/pickup.h
index 77347de2..fe45b5ae 100644
--- a/src/game/server/entities/pickup.h
+++ b/src/game/server/entities/pickup.h
@@ -14,6 +14,7 @@ public:
 
 	virtual void Reset();
 	virtual void Tick();
+	virtual void TickPaused();
 	virtual void Snap(int SnappingClient);
 
 private:
diff --git a/src/game/server/entities/projectile.cpp b/src/game/server/entities/projectile.cpp
index 2baa24b1..e89e0e6f 100644
--- a/src/game/server/entities/projectile.cpp
+++ b/src/game/server/entities/projectile.cpp
@@ -82,6 +82,11 @@ void CProjectile::Tick()
 	}
 }
 
+void CProjectile::TickPaused()
+{
+	++m_StartTick;
+}
+
 void CProjectile::FillInfo(CNetObj_Projectile *pProj)
 {
 	pProj->m_X = (int)m_Pos.x;
diff --git a/src/game/server/entities/projectile.h b/src/game/server/entities/projectile.h
index 5df04bcd..7b5c5e0e 100644
--- a/src/game/server/entities/projectile.h
+++ b/src/game/server/entities/projectile.h
@@ -14,6 +14,7 @@ public:
 
 	virtual void Reset();
 	virtual void Tick();
+	virtual void TickPaused();
 	virtual void Snap(int SnappingClient);
 
 private:
diff --git a/src/game/server/entity.h b/src/game/server/entity.h
index b9b33eb7..0f838730 100644
--- a/src/game/server/entity.h
+++ b/src/game/server/entity.h
@@ -106,6 +106,12 @@ public:
 	virtual void TickDefered() {}
 
 	/*
+		Function: TickPaused
+			Called when the game is paused, to freeze the state and position of the entity.
+	*/
+	virtual void TickPaused() {}
+
+	/*
 		Function: snap
 			Called when a new snapshot is being generated for a specific
 			client.
diff --git a/src/game/server/gamecontext.cpp b/src/game/server/gamecontext.cpp
index fdd78f60..87b9b3b7 100644
--- a/src/game/server/gamecontext.cpp
+++ b/src/game/server/gamecontext.cpp
@@ -1036,6 +1036,16 @@ void CGameContext::ConTuneDump(IConsole::IResult *pResult, void *pUserData)
 	}
 }
 
+void CGameContext::ConPause(IConsole::IResult *pResult, void *pUserData)
+{
+	CGameContext *pSelf = (CGameContext *)pUserData;
+
+	if(pSelf->m_pController->IsGameOver())
+		return;
+
+	pSelf->m_World.m_Paused ^= 1;
+}
+
 void CGameContext::ConChangeMap(IConsole::IResult *pResult, void *pUserData)
 {
 	CGameContext *pSelf = (CGameContext *)pUserData;
@@ -1409,6 +1419,7 @@ void CGameContext::OnConsoleInit()
 	Console()->Register("tune_reset", "", CFGFLAG_SERVER, ConTuneReset, this, "Reset tuning");
 	Console()->Register("tune_dump", "", CFGFLAG_SERVER, ConTuneDump, this, "Dump tuning");
 
+	Console()->Register("pause", "", CFGFLAG_SERVER, ConPause, this, "Pause/unpause game");
 	Console()->Register("change_map", "?r", CFGFLAG_SERVER|CFGFLAG_STORE, ConChangeMap, this, "Change map");
 	Console()->Register("restart", "?i", CFGFLAG_SERVER|CFGFLAG_STORE, ConRestart, this, "Restart in x seconds (0 = abort)");
 	Console()->Register("broadcast", "r", CFGFLAG_SERVER, ConBroadcast, this, "Broadcast message");
diff --git a/src/game/server/gamecontext.h b/src/game/server/gamecontext.h
index 559d1c0d..2ffb0ab4 100644
--- a/src/game/server/gamecontext.h
+++ b/src/game/server/gamecontext.h
@@ -48,6 +48,7 @@ class CGameContext : public IGameServer
 	static void ConTuneParam(IConsole::IResult *pResult, void *pUserData);
 	static void ConTuneReset(IConsole::IResult *pResult, void *pUserData);
 	static void ConTuneDump(IConsole::IResult *pResult, void *pUserData);
+	static void ConPause(IConsole::IResult *pResult, void *pUserData);
 	static void ConChangeMap(IConsole::IResult *pResult, void *pUserData);
 	static void ConRestart(IConsole::IResult *pResult, void *pUserData);
 	static void ConBroadcast(IConsole::IResult *pResult, void *pUserData);
diff --git a/src/game/server/gamecontroller.cpp b/src/game/server/gamecontroller.cpp
index 181e5052..7001ca32 100644
--- a/src/game/server/gamecontroller.cpp
+++ b/src/game/server/gamecontroller.cpp
@@ -430,8 +430,12 @@ void IGameController::Tick()
 		}
 	}
 
+	// game is Paused
+	if(GameServer()->m_World.m_Paused)
+		++m_RoundStartTick;
+
 	// do team-balancing
-	if (IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60)
+	if(IsTeamplay() && m_UnbalancedTick != -1 && Server()->Tick() > m_UnbalancedTick+g_Config.m_SvTeambalanceTime*Server()->TickSpeed()*60)
 	{
 		GameServer()->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "game", "Balancing teams");
 
diff --git a/src/game/server/gamecontroller.h b/src/game/server/gamecontroller.h
index 4706df48..1675fe35 100644
--- a/src/game/server/gamecontroller.h
+++ b/src/game/server/gamecontroller.h
@@ -64,6 +64,7 @@ public:
 	const char *m_pGameType;
 
 	bool IsTeamplay() const;
+	bool IsGameOver() const { return m_GameOverTick != -1; }
 
 	IGameController(class CGameContext *pGameServer);
 	virtual ~IGameController();
diff --git a/src/game/server/gameworld.cpp b/src/game/server/gameworld.cpp
index 89f4808d..d803ae67 100644
--- a/src/game/server/gameworld.cpp
+++ b/src/game/server/gameworld.cpp
@@ -173,6 +173,17 @@ void CGameWorld::Tick()
 				pEnt = m_pNextTraverseEntity;
 			}
 	}
+	else
+	{
+		// update all objects
+		for(int i = 0; i < NUM_ENTTYPES; i++)
+			for(CEntity *pEnt = m_apFirstEntityTypes[i]; pEnt; )
+			{
+				m_pNextTraverseEntity = pEnt->m_pNextTypeEntity;
+				pEnt->TickPaused();
+				pEnt = m_pNextTraverseEntity;
+			}
+	}
 
 	RemoveEntities();
 }
diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp
index df9727a9..75c2c1c6 100644
--- a/src/game/server/player.cpp
+++ b/src/game/server/player.cpp
@@ -60,26 +60,37 @@ void CPlayer::Tick()
 		}
 	}
 
-	if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW)
-		m_ViewPos -= vec2(clamp(m_ViewPos.x-m_LatestActivity.m_TargetX, -500.0f, 500.0f), clamp(m_ViewPos.y-m_LatestActivity.m_TargetY, -400.0f, 400.0f));
+	if(!GameServer()->m_World.m_Paused)
+	{
+		if(!m_pCharacter && m_Team == TEAM_SPECTATORS && m_SpectatorID == SPEC_FREEVIEW)
+			m_ViewPos -= vec2(clamp(m_ViewPos.x-m_LatestActivity.m_TargetX, -500.0f, 500.0f), clamp(m_ViewPos.y-m_LatestActivity.m_TargetY, -400.0f, 400.0f));
 
-	if(!m_pCharacter && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick())
-		m_Spawning = true;
+		if(!m_pCharacter && m_DieTick+Server()->TickSpeed()*3 <= Server()->Tick())
+			m_Spawning = true;
 
-	if(m_pCharacter)
-	{
-		if(m_pCharacter->IsAlive())
-		{
-			m_ViewPos = m_pCharacter->m_Pos;
-		}
-		else
+		if(m_pCharacter)
 		{
-			delete m_pCharacter;
-			m_pCharacter = 0;
+			if(m_pCharacter->IsAlive())
+			{
+				m_ViewPos = m_pCharacter->m_Pos;
+			}
+			else
+			{
+				delete m_pCharacter;
+				m_pCharacter = 0;
+			}
 		}
+		else if(m_Spawning && m_RespawnTick <= Server()->Tick())
+			TryRespawn();
 	}
-	else if(m_Spawning && m_RespawnTick <= Server()->Tick())
-		TryRespawn();
+	else
+	{
+		++m_RespawnTick;
+		++m_DieTick;
+		++m_ScoreStartTick;
+		++m_LastActionTick;
+		++m_TeamChangeTick;
+ 	}
 }
 
 void CPlayer::PostTick()