diff --git a/CS2Fixes.vcxproj b/CS2Fixes.vcxproj
index dc5095c2..b0e5926f 100644
--- a/CS2Fixes.vcxproj
+++ b/CS2Fixes.vcxproj
@@ -237,9 +237,11 @@
     <ClInclude Include="src\cs2_sdk\entity\clogiccase.h" />
     <ClInclude Include="src\cs2_sdk\entity\cparticlesystem.h" />
     <ClInclude Include="src\cs2_sdk\entity\cphysthruster.h" />
+    <ClInclude Include="src\cs2_sdk\entity\cpointviewcontrol.h" />
     <ClInclude Include="src\cs2_sdk\entity\ctakedamageinfo.h" />
     <ClInclude Include="src\cs2_sdk\entity\cteam.h" />
     <ClInclude Include="src\cs2_sdk\entity\ctriggerpush.h" />
+    <ClInclude Include="src\cs2_sdk\entity\ctriggerteleport.h" />
     <ClInclude Include="src\cs2_sdk\entity\cvotecontroller.h" />
     <ClInclude Include="src\cs2_sdk\entity\globaltypes.h" />
     <ClInclude Include="src\cs2_sdk\entity\lights.h" />
diff --git a/CS2Fixes.vcxproj.filters b/CS2Fixes.vcxproj.filters
index 0b7637ab..89e84892 100644
--- a/CS2Fixes.vcxproj.filters
+++ b/CS2Fixes.vcxproj.filters
@@ -349,5 +349,11 @@
     <ClInclude Include="src\cs2_sdk\entity\cenvhudhint.h">
       <Filter>Header Files\cs2_sdk\entity</Filter>
     </ClInclude>
+    <ClInclude Include="src\cs2_sdk\entity\cpointviewcontrol.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="src\cs2_sdk\entity\ctriggerteleport.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/gamedata/cs2fixes.games.txt b/gamedata/cs2fixes.games.txt
index b45964a7..9db4904d 100644
--- a/gamedata/cs2fixes.games.txt
+++ b/gamedata/cs2fixes.games.txt
@@ -345,6 +345,18 @@
 				"windows"	"\x48\x89\x5C\x24\x20\x48\x89\x4C\x24\x08\x55\x56\x41\x55"
 				"linux"		"\x55\x48\x89\xE5\x41\x57\x41\x56\x49\x89\xCE\x41\x55\x4D\x89\xC5\x41\x54\x49\x89\xD4\x53\x4C\x89\xCB"
 			}
+			"CBasePlayerPawn_GetEyePosition"
+			{
+				"library"	"server"
+				"windows"	"\x48\x89\x5C\x24\x2A\x57\x48\x83\xEC\x2A\x48\x8B\xF9\x48\x8B\xDA\x48\x8B\x89\x2A\x2A\x2A\x2A\x48\x85\xC9\x74\x2A\x48\x8B\x01"
+				"linux"		"\x55\x48\x89\xE5\x41\x54\x49\x89\xFC\x48\x83\xEC\x2A\x48\x8B\xBF\x2A\x2A\x2A\x2A\x48\x85\xFF\x74\x2A\x48\x8B\x07\x48\x8D\x15"
+			}
+			"CBasePlayerPawn_GetEyeAngles"
+			{
+				"library"	"server"
+				"windows"	"\x48\x89\x5C\x24\x2A\x57\x48\x81\xEC\x2A\x2A\x2A\x2A\x48\x8B\xF9\x48\x8B\xDA\x48\x8B\x89"
+				"linux"		"\x55\x48\x89\xE5\x41\x55\x41\x54\x49\x89\xFC\x48\x83\xEC\x2A\x48\x8B\xBF\x2A\x2A\x2A\x2A\x48\x85\xFF\x0F\x84\x2A\x2A\x2A\x2A\x48\x8B\x07\x48\x8D\x15"
+			}
 		}
 		"Offsets"
 		{
diff --git a/src/cs2_sdk/entity/cbaseentity.h b/src/cs2_sdk/entity/cbaseentity.h
index a3e10947..e51b5f92 100644
--- a/src/cs2_sdk/entity/cbaseentity.h
+++ b/src/cs2_sdk/entity/cbaseentity.h
@@ -34,6 +34,7 @@ extern CGameConfig *g_GameConfig;
 
 class CGameUI;
 class CEnvHudHint;
+class CPointViewControl;
 
 class CGameSceneNode
 {
@@ -291,6 +292,19 @@ class CBaseEntity : public CEntityInstance
 		return nullptr;
 	}
 
+	[[nodiscard]] CPointViewControl *AsPointViewControl()
+	{
+		if (V_strcasecmp(GetClassname(), "logic_relay") != 0)
+			return nullptr;
+
+		const auto tag = m_iszPrivateVScripts.IsValid() ? m_iszPrivateVScripts.String() : nullptr;
+
+		if (tag && V_strcasecmp(tag, "point_viewcontrol") == 0)
+			return reinterpret_cast<CPointViewControl *>(this);
+
+		return nullptr;
+	}
+
 	/* End Custom Entities Cast */
 };
 
diff --git a/src/cs2_sdk/entity/cbasemodelentity.h b/src/cs2_sdk/entity/cbasemodelentity.h
index 266dced3..74ab7635 100644
--- a/src/cs2_sdk/entity/cbasemodelentity.h
+++ b/src/cs2_sdk/entity/cbasemodelentity.h
@@ -32,7 +32,8 @@ class CBaseModelEntity : public CBaseEntity
 	SCHEMA_FIELD(Color, m_clrRender)
 	SCHEMA_FIELD(RenderMode_t, m_nRenderMode)
 	SCHEMA_FIELD(float, m_flDissolveStartTime)
-	
+	SCHEMA_FIELD(Vector, m_vecViewOffset)
+
 	void SetModel(const char *szModel)
 	{
 		addresses::CBaseModelEntity_SetModel(this, szModel);
@@ -48,4 +49,10 @@ class CBaseModelEntity : public CBaseEntity
 	{
 		return ((CSkeletonInstance*)m_CBodyComponent->m_pSceneNode.Get())->m_modelState().m_ModelName.Get().String();
 	}
+
+	Vector GetEyePosition()
+	{
+		const auto x = m_vecViewOffset();
+		return x + GetAbsOrigin();
+	}
 };
\ No newline at end of file
diff --git a/src/cs2_sdk/entity/cbaseplayercontroller.h b/src/cs2_sdk/entity/cbaseplayercontroller.h
index 8d2296ef..f8610cfa 100644
--- a/src/cs2_sdk/entity/cbaseplayercontroller.h
+++ b/src/cs2_sdk/entity/cbaseplayercontroller.h
@@ -44,6 +44,7 @@ class CBasePlayerController : public CBaseEntity
 	SCHEMA_FIELD_POINTER(char, m_iszPlayerName)
 	SCHEMA_FIELD(PlayerConnectedState, m_iConnected)
 	SCHEMA_FIELD(bool, m_bIsHLTV)
+	SCHEMA_FIELD(uint, m_iDesiredFOV)
 
 	// Returns the current pawn, which could be one of those:
 	// - The player's actual pawn
diff --git a/src/cs2_sdk/entity/cbaseplayerpawn.h b/src/cs2_sdk/entity/cbaseplayerpawn.h
index dfb403b0..662af781 100644
--- a/src/cs2_sdk/entity/cbaseplayerpawn.h
+++ b/src/cs2_sdk/entity/cbaseplayerpawn.h
@@ -34,7 +34,9 @@ class CBasePlayerPawn : public CBaseModelEntity
 	SCHEMA_FIELD(CCSPlayer_WeaponServices*, m_pWeaponServices)
 	SCHEMA_FIELD(CCSPlayer_ItemServices*, m_pItemServices)
 	SCHEMA_FIELD(CPlayer_ObserverServices*, m_pObserverServices)
+	SCHEMA_FIELD(CPlayer_CameraServices*, m_pCameraServices)
 	SCHEMA_FIELD(CHandle<CBasePlayerController>, m_hController)
+	SCHEMA_FIELD(QAngle, v_angle)
 
 	// Drops any map-spawned weapons the pawn is holding
 	// NOTE: Currently very broken with map items (entities parented to weapons?) due to a game bug..? Needs further investigation/work
diff --git a/src/cs2_sdk/entity/ccsplayerpawn.h b/src/cs2_sdk/entity/ccsplayerpawn.h
index 7cf50481..6b6b6a0f 100644
--- a/src/cs2_sdk/entity/ccsplayerpawn.h
+++ b/src/cs2_sdk/entity/ccsplayerpawn.h
@@ -62,4 +62,9 @@ class CCSPlayerPawn : public CCSPlayerPawnBase
 
 	SCHEMA_FIELD(float, m_flVelocityModifier)
 	SCHEMA_FIELD(CCSPlayer_ActionTrackingServices*, m_pActionTrackingServices)
+
+	[[nodiscard]] CCSPlayer_CameraServices* GetCameraService()
+	{
+		return reinterpret_cast<CCSPlayer_CameraServices*>(m_pCameraServices());
+	}
 };
\ No newline at end of file
diff --git a/src/cs2_sdk/entity/ccsweaponbase.h b/src/cs2_sdk/entity/ccsweaponbase.h
index 0d828ad3..c31a59c5 100644
--- a/src/cs2_sdk/entity/ccsweaponbase.h
+++ b/src/cs2_sdk/entity/ccsweaponbase.h
@@ -21,6 +21,8 @@
 
 #include "cbaseentity.h"
 
+extern CGlobalVars* gpGlobals;
+
 enum gear_slot_t : uint32_t
 {
 	GEAR_SLOT_INVALID = 0xffffffff,
@@ -88,8 +90,16 @@ class CBasePlayerWeapon : public CEconEntity
 {
 public:
 	DECLARE_SCHEMA_CLASS(CBasePlayerWeapon)
+	SCHEMA_FIELD(int, m_nNextPrimaryAttackTick)
+	SCHEMA_FIELD(int, m_nNextSecondaryAttackTick)
 
 	CCSWeaponBaseVData* GetWeaponVData() { return (CCSWeaponBaseVData*)GetVData(); }
+
+	void Disarm()
+	{
+		m_nNextPrimaryAttackTick(MAX(m_nNextPrimaryAttackTick(), gpGlobals->tickcount + 24));
+		m_nNextSecondaryAttackTick(MAX(m_nNextSecondaryAttackTick(), gpGlobals->tickcount + 24));
+	}
 };
 
 class CCSWeaponBase : public CBasePlayerWeapon
diff --git a/src/cs2_sdk/entity/cgamerules.h b/src/cs2_sdk/entity/cgamerules.h
index c193384b..61c25a63 100644
--- a/src/cs2_sdk/entity/cgamerules.h
+++ b/src/cs2_sdk/entity/cgamerules.h
@@ -67,6 +67,7 @@ class CCSGameRules : public CGameRules
 	SCHEMA_FIELD_POINTER(int, m_nEndMatchMapGroupVoteOptions)
 	SCHEMA_FIELD(int, m_nEndMatchMapVoteWinner)
 	SCHEMA_FIELD(int, m_iRoundTime)
+	SCHEMA_FIELD(bool, m_bFreezePeriod)
 	SCHEMA_FIELD_POINTER(CUtlVector<SpawnPoint*>, m_CTSpawnPoints)
 	SCHEMA_FIELD_POINTER(CUtlVector<SpawnPoint*>, m_TerroristSpawnPoints)
 
diff --git a/src/cs2_sdk/entity/cpointviewcontrol.h b/src/cs2_sdk/entity/cpointviewcontrol.h
new file mode 100644
index 00000000..069d45a8
--- /dev/null
+++ b/src/cs2_sdk/entity/cpointviewcontrol.h
@@ -0,0 +1,69 @@
+/**
+ * =============================================================================
+ * CS2Fixes
+ * Copyright (C) 2023-2024 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 <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include "../schema.h"
+#include "cbaseentity.h"
+#include "entity.h"
+
+/* logic_relay */
+
+class CPointViewControl : public CBaseEntity
+{
+    DECLARE_SCHEMA_CLASS(CPointViewControl)
+
+    // hello, don't port my garbage code to cssharp :)
+
+    static constexpr int SF_POINT_VIEWCONTROL_FROZEN = 1 << 5;
+    static constexpr int SF_POINT_VIEWCONTROL_FOV    = 1 << 6;
+    static constexpr int SF_POINT_VIEWCONTROL_DISARM = 1 << 7;
+
+public:
+    [[nodiscard]] CBaseEntity* GetTargetCameraEntity()
+    {
+        const auto pTarget = UTIL_FindEntityByName(nullptr, m_target().String());
+        return pTarget && pTarget->m_pCollision() ? pTarget : nullptr;
+    }
+
+    [[nodiscard]] bool HasTargetCameraEntity()
+    {
+        return m_target().IsValid() && strlen(m_target().String()) >= 2;
+    }
+
+    [[nodiscard]] bool HasFrozen()
+    {
+        return !!(m_spawnflags() & SF_POINT_VIEWCONTROL_FROZEN);
+    }
+
+    [[nodiscard]] bool HasFOV()
+    {
+        return !!(m_spawnflags() & SF_POINT_VIEWCONTROL_FOV);
+    }
+
+    [[nodiscard]] bool HasDisarm()
+    {
+        return !!(m_spawnflags() & SF_POINT_VIEWCONTROL_DISARM);
+    }
+
+    [[nodiscard]] uint GetFOV()
+    {
+        return clamp(static_cast<uint>(m_iHealth()), 16, 179);
+    }
+};
diff --git a/src/cs2_sdk/entity/services.h b/src/cs2_sdk/entity/services.h
index 658fbf7a..6c0ea436 100644
--- a/src/cs2_sdk/entity/services.h
+++ b/src/cs2_sdk/entity/services.h
@@ -219,4 +219,24 @@ class CPlayer_ObserverServices
 	SCHEMA_FIELD(CHandle<CBaseEntity>, m_hObserverTarget)
 	SCHEMA_FIELD(ObserverMode_t, m_iObserverLastMode)
 	SCHEMA_FIELD(bool, m_bForcedObserverMode)
-};
\ No newline at end of file
+};
+
+class CPlayer_CameraServices
+{
+public:
+    DECLARE_SCHEMA_CLASS(CPlayer_CameraServices)
+
+    SCHEMA_FIELD(CHandle<CBaseEntity>, m_hViewEntity)
+};
+
+class CCSPlayerBase_CameraServices : public CPlayer_CameraServices
+{
+public:
+    DECLARE_SCHEMA_CLASS(CCSPlayerBase_CameraServices)
+
+    SCHEMA_FIELD(CHandle<CBaseEntity>, m_hZoomOwner)
+    SCHEMA_FIELD(uint, m_iFOV)
+};
+
+class CCSPlayer_CameraServices : public CCSPlayerBase_CameraServices
+{};
\ No newline at end of file
diff --git a/src/cs2fixes.h b/src/cs2fixes.h
index edd4b924..c053b7f3 100644
--- a/src/cs2fixes.h
+++ b/src/cs2fixes.h
@@ -67,6 +67,13 @@ class CS2Fixes : public ISmmPlugin, public IMetamodListener
 	void Hook_CreateWorkshopMapGroup(const char* name, const CUtlStringList& mapList);
 	void Hook_GoToIntermission(bool bAbortedMatch);
 	bool Hook_OnTakeDamage_Alive(CTakeDamageInfoContainer *pInfoContainer);
+#ifdef PLATFORM_WINDOWS
+	Vector* Hook_GetEyePosition(Vector*);
+	QAngle* Hook_GetEyeAngles(QAngle*);
+#else
+	Vector Hook_GetEyePosition();
+	QAngle Hook_GetEyeAngles();
+#endif
 	void Hook_CheckMovingGround(double frametime);
 	int Hook_LoadEventsFromFile(const char *filename, bool bSearchAll);
 
diff --git a/src/detours.cpp b/src/detours.cpp
index 13bb3e4f..4bb60869 100644
--- a/src/detours.cpp
+++ b/src/detours.cpp
@@ -17,37 +17,38 @@
  * this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "cs_usercmd.pb.h"
 #include "networkbasetypes.pb.h"
 #include "usercmd.pb.h"
-#include "cs_usercmd.pb.h"
 
-#include "cdetour.h"
-#include "common.h"
-#include "module.h"
 #include "addresses.h"
+#include "cdetour.h"
 #include "commands.h"
-#include "detours.h"
+#include "common.h"
 #include "ctimer.h"
-#include "irecipientfilter.h"
+#include "customio.h"
+#include "detours.h"
+#include "entities.h"
+#include "entity/cbasemodelentity.h"
 #include "entity/ccsplayercontroller.h"
 #include "entity/ccsplayerpawn.h"
-#include "entity/cbasemodelentity.h"
 #include "entity/ccsweaponbase.h"
 #include "entity/cenvhudhint.h"
-#include "entity/ctriggerpush.h"
 #include "entity/cgamerules.h"
+#include "entity/cpointviewcontrol.h"
 #include "entity/ctakedamageinfo.h"
+#include "entity/ctriggerpush.h"
 #include "entity/services.h"
-#include "playermanager.h"
-#include "igameevents.h"
 #include "gameconfig.h"
-#include "zombiereborn.h"
-#include "customio.h"
-#include "entities.h"
-#include "serversideclient.h"
-#include "networksystem/inetworkserializer.h"
+#include "igameevents.h"
+#include "irecipientfilter.h"
 #include "map_votes.h"
+#include "module.h"
+#include "networksystem/inetworkserializer.h"
+#include "playermanager.h"
+#include "serversideclient.h"
 #include "tier0/vprof.h"
+#include "zombiereborn.h"
 
 #include "tier0/memdbgon.h"
 
@@ -77,6 +78,8 @@ DECLARE_DETOUR(CCSPlayerPawn_GetMaxSpeed, Detour_CCSPlayerPawn_GetMaxSpeed);
 DECLARE_DETOUR(FindUseEntity, Detour_FindUseEntity);
 DECLARE_DETOUR(TraceFunc, Detour_TraceFunc);
 DECLARE_DETOUR(TraceShape, Detour_TraceShape);
+DECLARE_DETOUR(CBasePlayerPawn_GetEyePosition, Detour_CBasePlayerPawn_GetEyePosition);
+DECLARE_DETOUR(CBasePlayerPawn_GetEyeAngles, Detour_CBasePlayerPawn_GetEyeAngles);
 
 static bool g_bBlockMolotovSelfDmg = false;
 static bool g_bBlockAllDamage = false;
@@ -443,6 +446,17 @@ bool FASTCALL Detour_CEntityIdentity_AcceptInput(CEntityIdentity* pThis, CUtlSym
 		if (!V_strcasecmp(pInputName->String(), "Deactivate"))
 			return CGameUIHandler::OnDeactivate(pGameUI, reinterpret_cast<CBaseEntity*>(pActivator));
 	}
+	else if (const auto pViewControl = reinterpret_cast<CPointViewControl*>(pThis->m_pInstance)->AsPointViewControl())
+	{
+		if (!V_strcasecmp(pInputName->String(), "EnableCamera"))
+			return CPointViewControlHandler::OnEnable(pViewControl, reinterpret_cast<CBaseEntity*>(pActivator));
+		if (!V_strcasecmp(pInputName->String(), "DisableCamera"))
+			return CPointViewControlHandler::OnDisable(pViewControl, reinterpret_cast<CBaseEntity*>(pActivator));
+		if (!V_strcasecmp(pInputName->String(), "EnableCameraAll"))
+			return CPointViewControlHandler::OnEnableAll(pViewControl);
+		if (!V_strcasecmp(pInputName->String(), "DisableCameraAll"))
+			return CPointViewControlHandler::OnDisableAll(pViewControl);
+	}
 
 	VPROF_SCOPE_END();
 
@@ -597,6 +611,52 @@ bool FASTCALL Detour_TraceShape(int64* a1, int64 a2, int64 a3, int64 a4, CTraceF
 	return TraceShape(a1, a2, a3, a4, filter, a6);
 }
 
+#ifdef PLATFORM_WINDOWS
+Vector* FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn* pPawn, Vector* pRet)
+{
+    if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast<CCSPlayerPawn*>(pPawn)))
+    {
+        const auto& origin = pPawn->GetEyePosition();
+        pRet->Init(origin.x, origin.y, origin.z);
+        return pRet;
+    }
+
+    return CBasePlayerPawn_GetEyePosition(pPawn, pRet);
+}
+QAngle* FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn* pPawn, QAngle* pRet)
+{
+    if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast<CCSPlayerPawn*>(pPawn)))
+    {
+        const auto& angles = pPawn->v_angle();
+        pRet->Init(angles.x, angles.y, angles.z);
+        return pRet;
+    }
+
+    return CBasePlayerPawn_GetEyeAngles(pPawn, pRet);
+}
+#else
+Vector FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn* pPawn)
+{
+    if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast<CCSPlayerPawn*>(pPawn)))
+    {
+        const auto& origin = pPawn->GetEyePosition();
+        return origin;
+    }
+
+    return CBasePlayerPawn_GetEyePosition(pPawn);
+}
+QAngle FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn* pPawn)
+{
+    if (pPawn->IsAlive() && CPointViewControlHandler::IsViewControl(reinterpret_cast<CCSPlayerPawn*>(pPawn)))
+    {
+        const auto& angles = pPawn->v_angle();
+        return angles;
+    }
+
+    return CBasePlayerPawn_GetEyeAngles(pPawn);
+}
+#endif
+
 bool InitDetours(CGameConfig *gameConfig)
 {
 	bool success = true;
diff --git a/src/detours.h b/src/detours.h
index 727484fa..d85983db 100644
--- a/src/detours.h
+++ b/src/detours.h
@@ -48,6 +48,8 @@ class InputData_t;
 class CCSPlayerPawn;
 class CCSPlayer_UseServices;
 class CTraceFilter;
+class Vector;
+class QAngle;
 
 bool InitDetours(CGameConfig *gameConfig);
 void FlushAllDetours();
@@ -68,4 +70,11 @@ CServerSideClient* FASTCALL Detour_GetFreeClient(int64_t unk1, const __m128i* un
 float FASTCALL Detour_CCSPlayerPawn_GetMaxSpeed(CCSPlayerPawn*);
 int64 FASTCALL Detour_FindUseEntity(CCSPlayer_UseServices* pThis, float);
 bool FASTCALL Detour_TraceFunc(int64*, int*, float*, uint64);
-bool FASTCALL Detour_TraceShape(int64*, int64, int64, int64, CTraceFilter*, int64);
\ No newline at end of file
+bool FASTCALL Detour_TraceShape(int64*, int64, int64, int64, CTraceFilter*, int64);
+#ifdef PLATFORM_WINDOWS
+Vector* FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*, Vector*);
+QAngle* FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*, QAngle*);
+#else
+Vector FASTCALL Detour_CBasePlayerPawn_GetEyePosition(CBasePlayerPawn*);
+QAngle FASTCALL Detour_CBasePlayerPawn_GetEyeAngles(CBasePlayerPawn*);
+#endif
\ No newline at end of file
diff --git a/src/entities.cpp b/src/entities.cpp
index c57080f4..d48f4f2f 100644
--- a/src/entities.cpp
+++ b/src/entities.cpp
@@ -22,19 +22,24 @@
 #include "ctimer.h"
 #include "entity.h"
 #include "entity/cbaseplayercontroller.h"
+#include "entity/ccsplayercontroller.h"
 #include "entity/ccsplayerpawn.h"
 #include "entity/cgameplayerequip.h"
+#include "entity/cgamerules.h"
 #include "entity/clogiccase.h"
+#include "entity/cpointviewcontrol.h"
 
 // #define ENTITY_HANDLER_ASSERTION
 
+extern CCSGameRules* g_pGameRules;
+
 class InputData_t
 {
 public:
     CBaseEntity* pActivator;
     CBaseEntity* pCaller;
-    variant_t      value;
-    int            nOutputID;
+    variant_t    value;
+    int          nOutputID;
 };
 
 inline bool StripPlayer(CCSPlayerPawn* pPawn)
@@ -379,12 +384,327 @@ bool OnDeactivate(CGameUI* pEntity, CBaseEntity* pActivator)
 
 } // namespace CGameUIHandler
 
+namespace CPointViewControlHandler
+{
+struct ViewControl
+{
+    CUtlVector<CHandle<CCSPlayerPawn>> m_players;
+    std::string                        m_viewTarget;
+    std::string                        m_name;
+};
+
+static std::unordered_map<uint32, ViewControl> s_repository;
+static constexpr uint                          INVALID_FOV = 0xFFFFFFFF;
+static constexpr uint                          RESET_FOV   = 0xFFFFFFFE;
+static CHandle<CBaseEntity>                    INVALID_HANDLE(0xFFFFFFFF);
+
+inline void UpdatePlayerState(CCSPlayerPawn* pPawn, const CHandle<CBaseEntity>& target, bool frozen, uint fov = INVALID_FOV, bool disarm = false)
+{
+    if (!pPawn)
+        return;
+
+    if (const auto pCameraService = pPawn->GetCameraService())
+    {
+        pCameraService->m_hViewEntity(target);
+        pCameraService->m_hZoomOwner(INVALID_HANDLE);
+
+        if (fov != INVALID_FOV)
+        {
+            if (const auto pController = pPawn->GetController())
+            {
+                if (fov == RESET_FOV)
+                {
+                    pCameraService->m_iFOV(pController->m_iDesiredFOV());
+                }
+                else
+                {
+                    pCameraService->m_iFOV(fov);
+                }
+            }
+        }
+    }
+
+    if (disarm)
+    {
+        if (const auto pWeaponService = pPawn->m_pWeaponServices())
+        {
+            if (const auto pActiveWeapon = pWeaponService->m_hActiveWeapon().Get())
+            {
+                pActiveWeapon->Disarm();
+            }
+        }
+    }
+
+    auto flags = pPawn->m_fFlags();
+
+    if (g_pGameRules && g_pGameRules->m_bFreezePeriod())
+        frozen = true;
+
+    if (frozen)
+    {
+        flags |= FL_FROZEN;
+    }
+    else
+    {
+        flags &= ~FL_FROZEN;
+    }
+
+    pPawn->m_fFlags(flags);
+}
+
+void OnCreated(CBaseEntity* pEntity)
+{
+    const auto pViewControl = pEntity->AsPointViewControl();
+    if (!pViewControl)
+        return;
+
+    if (!pViewControl->HasTargetCameraEntity())
+    {
+        Warning("PointViewControl %s has no target camera entity\n", pViewControl->GetName());
+        return;
+    }
+
+    ViewControl vc{};
+    vc.m_viewTarget                            = pViewControl->m_target().String();
+    vc.m_name                                  = pViewControl->GetName();
+    s_repository[pEntity->GetHandle().ToInt()] = vc;
+}
+bool OnEnable(CPointViewControl* pEntity, CBaseEntity* pActivator)
+{
+    if (!pActivator || !pActivator->IsPawn() || !pActivator->IsAlive())
+        return false;
+
+    const auto key = pEntity->GetHandle().ToInt();
+    const auto it  = s_repository.find(key);
+    if (it == s_repository.end())
+        return false;
+
+    const auto pPawn       = reinterpret_cast<CCSPlayerPawn*>(pActivator);
+    const auto pController = reinterpret_cast<CCSPlayerController*>(pPawn->GetController());
+    if (!pController)
+        return false;
+
+    if (pController->IsBot() || pController->m_bIsHLTV())
+    {
+        Warning("PointViewControl %s try enable for bot or HLTV: %s\n", it->second.m_name.c_str(), pController->GetPlayerName());
+        return false;
+    }
+
+    const auto handle = CHandle<CCSPlayerPawn>(pPawn->GetHandle());
+
+    for (auto& [vk, vc] : s_repository)
+    {
+        if (const auto index = vc.m_players.Find(handle); index > -1)
+        {
+            if (vk == static_cast<uint>(key))
+            {
+                Warning("PointViewControl %s was enabled twice in a row! player: %s\n", vc.m_name.c_str(), pController->GetPlayerName());
+                return false;
+            }
+
+            vc.m_players.Remove(index);
+            UpdatePlayerState(pPawn, INVALID_HANDLE, false, RESET_FOV);
+            Warning("PointViewControl %s already enabled for %s\n", vc.m_name.c_str(), pController->GetPlayerName());
+            break;
+        }
+    }
+
+    return it->second.m_players.AddToTail(handle) >= 0;
+}
+bool OnDisable(CPointViewControl* pEntity, CBaseEntity* pActivator)
+{
+    if (!pActivator || !pActivator->IsPawn() || !pActivator->IsAlive())
+        return false;
+
+    const auto key = pEntity->GetHandle().ToInt();
+    const auto it  = s_repository.find(key);
+    if (it == s_repository.end())
+        return false;
+
+    const auto pPawn       = reinterpret_cast<CCSPlayerPawn*>(pActivator);
+    const auto pController = reinterpret_cast<CCSPlayerController*>(pPawn->GetController());
+    if (!pController)
+        return false;
+
+    if (pController->IsBot() || pController->m_bIsHLTV())
+    {
+        Warning("PointViewControl %s try disable for bot or HLTV: %s\n", it->second.m_name.c_str(), pController->GetPlayerName());
+        return false;
+    }
+
+    const auto handle = CHandle<CCSPlayerPawn>(pPawn->GetHandle());
+
+    UpdatePlayerState(pPawn, INVALID_HANDLE, false, RESET_FOV);
+
+    return it->second.m_players.FindAndRemove(handle);
+}
+bool OnEnableAll(CPointViewControl* pEntity)
+{
+    const auto key = pEntity->GetHandle().ToInt();
+    const auto it  = s_repository.find(key);
+    if (it == s_repository.end())
+        return false;
+
+    for (auto i = 0; i < gpGlobals->maxClients; i++)
+    {
+        const auto pController = CCSPlayerController::FromSlot(i);
+        if (!pController || !pController->IsConnected() || pController->IsBot() || pController->m_bIsHLTV())
+            continue;
+
+        const auto pPawn = pController->GetPlayerPawn();
+        if (!pPawn || !pPawn->IsAlive())
+            continue;
+
+        const auto handle = CHandle<CCSPlayerPawn>(pPawn->GetHandle());
+
+        for (auto& [vk, vc] : s_repository)
+        {
+            if (vk == static_cast<uint>(key))
+            {
+                continue;
+            }
+            if (const auto index = vc.m_players.Find(handle); index > -1)
+            {
+                vc.m_players.Remove(index);
+                UpdatePlayerState(pPawn, INVALID_HANDLE, false, RESET_FOV);
+                Warning("PointViewControl %s already enabled for %s\n", vc.m_name.c_str(), pController->GetPlayerName());
+            }
+        }
+
+        it->second.m_players.AddToTail(handle);
+    }
+
+    return true;
+}
+bool OnDisableAll(CPointViewControl* pEntity)
+{
+    const auto key = pEntity->GetHandle().ToInt();
+    const auto it  = s_repository.find(key);
+    if (it == s_repository.end())
+        return false;
+
+    FOR_EACH_VEC(it->second.m_players, i)
+    {
+        const auto& handle = it->second.m_players.Element(i);
+
+        if (const auto player = handle.Get())
+            UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV);
+    }
+
+    it->second.m_players.Purge();
+
+    return true;
+}
+
+void RunThink(int tick)
+{
+    // validate
+    for (auto it = s_repository.begin(); it != s_repository.end();)
+    {
+        const auto entity = CHandle<CPointViewControl>(it->first).Get();
+        if (!entity)
+        {
+            FOR_EACH_VEC(it->second.m_players, i)
+            {
+                const auto& handle = it->second.m_players.Element(i);
+
+                if (const auto player = handle.Get())
+                    UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV);
+            }
+
+            it = s_repository.erase(it);
+        }
+        else
+        {
+            ++it;
+        }
+    }
+
+    // think every tick
+
+    for (auto& [vk, vc] : s_repository)
+    {
+        const auto entity = CHandle<CPointViewControl>(vk).Get();
+        if (!entity)
+        {
+            Error("Why invalid entity here?");
+            continue;
+        }
+
+        if (vc.m_players.Count() == 0)
+            continue;
+
+        const auto pTarget = entity->GetTargetCameraEntity();
+        if (!pTarget)
+        {
+            FOR_EACH_VEC(vc.m_players, i)
+            {
+                const auto& handle = vc.m_players.Element(i);
+
+                if (const auto player = handle.Get())
+                    UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV);
+            }
+            vc.m_players.Purge();
+            continue;
+        }
+
+        FOR_EACH_VEC(vc.m_players, i)
+        {
+            const auto& handle = vc.m_players.Element(i);
+            const auto  player = handle.Get();
+            if (!player)
+            {
+                vc.m_players.Remove(i--);
+                continue;
+            }
+            if (!player->IsAlive())
+            {
+                UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV);
+                vc.m_players.Remove(i--);
+                continue;
+            }
+
+            UpdatePlayerState(player, pTarget->GetHandle(), entity->HasFrozen(), entity->HasFOV() ? entity->GetFOV() : INVALID_FOV, entity->HasDisarm());
+        }
+    }
+}
+bool IsViewControl(CCSPlayerPawn* pPawn)
+{
+    const auto handle = pPawn->GetHandle().ToInt();
+    for (const auto& [vk, vc] : s_repository)
+    {
+        FOR_EACH_VEC(vc.m_players, i)
+        {
+            if (vc.m_players.Element(i).ToInt() == handle)
+                return true;
+        }
+    }
+    return false;
+}
+void Shutdown()
+{
+    for (auto& [vk, vc] : s_repository)
+    {
+        FOR_EACH_VEC(vc.m_players, i)
+        {
+            const auto& handle = vc.m_players.Element(i);
+
+            if (const auto player = handle.Get())
+                UpdatePlayerState(player, INVALID_HANDLE, false, RESET_FOV);
+        }
+        vc.m_players.Purge();
+    }
+    s_repository.clear();
+}
+} // namespace CPointViewControlHandler
+
 void EntityHandler_OnGameFramePre(bool simulate, int tick)
 {
     if (!simulate)
         return;
 
     CGameUIHandler::RunThink(tick);
+    CPointViewControlHandler::RunThink(tick);
 }
 
 void EntityHandler_OnGameFramePost(bool simulate, int tick)
@@ -392,3 +712,13 @@ void EntityHandler_OnGameFramePost(bool simulate, int tick)
     if (!simulate)
         return;
 }
+
+void EntityHandler_OnRoundRestart()
+{
+    CPointViewControlHandler::Shutdown();
+}
+
+void EntityHandler_OnEntitySpawned(CBaseEntity* pEntity)
+{
+    CPointViewControlHandler::OnCreated(pEntity);
+}
diff --git a/src/entities.h b/src/entities.h
index 828e4fd2..07fad8f9 100644
--- a/src/entities.h
+++ b/src/entities.h
@@ -23,6 +23,8 @@ class InputData_t;
 class CGamePlayerEquip;
 class CBaseEntity;
 class CGameUI;
+class CPointViewControl;
+class CCSPlayerPawn;
 
 namespace CGamePlayerEquipHandler
 {
@@ -38,5 +40,16 @@ bool OnDeactivate(CGameUI* pEntity, CBaseEntity* pActivator);
 void RunThink(int tick);
 } // namespace CGameUIHandler
 
+namespace CPointViewControlHandler
+{
+bool OnEnable(CPointViewControl* pEntity, CBaseEntity* pActivator);
+bool OnDisable(CPointViewControl* pEntity, CBaseEntity* pActivator);
+bool OnEnableAll(CPointViewControl* pEntity);
+bool OnDisableAll(CPointViewControl* pEntity);
+bool IsViewControl(CCSPlayerPawn*);
+} // namespace CPointViewControlHandler
+
 void EntityHandler_OnGameFramePre(bool simulate, int tick);
-void EntityHandler_OnGameFramePost(bool simulate, int tick);
\ No newline at end of file
+void EntityHandler_OnGameFramePost(bool simulate, int tick);
+void EntityHandler_OnRoundRestart();
+void EntityHandler_OnEntitySpawned(CBaseEntity* pEntity);
\ No newline at end of file
diff --git a/src/entitylistener.cpp b/src/entitylistener.cpp
index 75f05c0d..4cc4df6f 100644
--- a/src/entitylistener.cpp
+++ b/src/entitylistener.cpp
@@ -19,11 +19,12 @@
 
 #include "entitylistener.h"
 #include "common.h"
+#include "cs2_sdk/entity/cbaseentity.h"
 #include "cs2fixes.h"
+#include "entities.h"
+#include "entity/cgamerules.h"
 #include "gameconfig.h"
-#include "cs2_sdk/entity/cbaseentity.h"
 #include "plat.h"
-#include "entity/cgamerules.h"
 
 extern CGameConfig *g_GameConfig;
 extern CCSGameRules* g_pGameRules;
@@ -52,6 +53,8 @@ void CEntityListener::OnEntitySpawned(CEntityInstance* pEntity)
 	{
 		reinterpret_cast<CBaseEntity*>(pEntity)->SetCollisionGroup(COLLISION_GROUP_DEBRIS);
 	}
+
+	EntityHandler_OnEntitySpawned(reinterpret_cast<CBaseEntity*>(pEntity));
 }
 
 void CEntityListener::OnEntityCreated(CEntityInstance* pEntity)
diff --git a/src/events.cpp b/src/events.cpp
index fce359fd..b0a04588 100644
--- a/src/events.cpp
+++ b/src/events.cpp
@@ -21,6 +21,7 @@
 #include "KeyValues.h"
 #include "commands.h"
 #include "ctimer.h"
+#include "entities.h"
 #include "eventlistener.h"
 #include "networkstringtabledefs.h"
 #include "entity/cbaseplayercontroller.h"
@@ -100,6 +101,8 @@ GAME_EVENT_F(round_prestart)
 		}
 	}
 
+	EntityHandler_OnRoundRestart();
+
 	CBaseEntity* pShake = nullptr;
 
 	// Prevent shakes carrying over from previous rounds