local function F(F,T)return math.atan2(T.X*F.Z-T.Z*F.X,T.X*F.X+T.Z*F.Z)end local function T()local T=RootCameraCreator()local r=math.rad(220)local u=math.rad(0)local V=math.rad(250)local J=2 local n=tick()T.LastUserPanCamera=tick()function T.Update(D)local m=tick()local N=D.UserPanningTheCamera==true local Z=workspace.CurrentCamera local M=PlayersService.LocalPlayer local b=D:GetHumanoid()local L=Z and Z.CameraSubject local c=b and b:GetState()==Enum.HumanoidStateType.Climbing local a=L and L:IsA("\086\101\104\105\099\108\101\083\101\097\116")local S=L and L:IsA("\083\107\097\116\101\098\111\097\114\100\080\108\097\116\102\111\114\109")if n==nil or m-n>1 then T:ResetCameraLook()D.LastCameraTransform=nil end if n then local F=math.min(.5,m-n)local T=0 if not(a or S)then T=T+(D.TurningLeft and-120 or 0)T=T+(D.TurningRight and 120 or 0)end local r=D:UpdateGamepad()if r~=Vector2.new(0,0)then N=true D.RotateInput=D.RotateInput+r end if T~=0 then N=true D.RotateInput=D.RotateInput+Vector2.new(math.rad(T*F),0)end end if N then u=0 T.LastUserPanCamera=tick()end local P=m-T.LastUserPanCamera<J local O=D:GetSubjectPosition()if O and(M and Z)then local T=D:GetCameraZoom()if T<.5 then T=.5 end if D:GetShiftLock()and not D:IsInFirstPerson()then local F=D:RotateCamera(D:GetCameraLook(),D.RotateInput)local T=((F*XZ_VECTOR):Cross(UP_VECTOR)).unit*1.75 if IsFiniteVector3(T)then O=O+T end else if D.LastCameraTransform and not N then local T=D:IsInFirstPerson()if(c or a or S)and(n and(b and b.Torso))then if T then if D.LastSubjectCFrame and((a or S)and L:IsA("\066\097\115\101\080\097\114\116"))then local T=-F(D.LastSubjectCFrame.lookVector,L.CFrame.lookVector)if IsFinite(T)then D.RotateInput=D.RotateInput+Vector2.new(T,0)end u=0 end elseif not P then local T=b.Torso.CFrame.lookVector if S then T=L.CFrame.lookVector end local J=m-n u=clamp(0,V,u+r*J)local N=clamp(0,1,u*J)if not c and D:IsInFirstPerson()then N=1 end local Z=F(T,D:GetCameraLook())if IsFinite(Z)and math.abs(Z)>.0001 then D.RotateInput=D.RotateInput+Vector2.new(Z*N,0)end end elseif not(T or P)then local T=-(D.LastCameraTransform.p-O)local r=F(T,D:GetCameraLook())if IsFinite(r)and math.abs(r)>.0001 then D.RotateInput=D.RotateInput+Vector2.new(r,0)end end end end local J=D:RotateCamera(D:GetCameraLook(),D.RotateInput)D.RotateInput=Vector2.new()Z.Focus=CFrame.new(O)if _G.LockTarget then Z.CoordinateFrame=CFrame.new(Z.Focus.p-T*J,_G.LockTarget.Torso.Position)else Z.CoordinateFrame=CFrame.new(Z.Focus.p-T*J,Z.Focus.p)end D.LastCameraTransform=Z.CoordinateFrame if a or S and L:IsA("\066\097\115\101\080\097\114\116")then D.LastSubjectCFrame=L.CFrame else D.LastSubjectCFrame=nil end end n=m end return T end return T
-- May return NaN or inf or -inf
local function findAngleBetweenXZVectors(vec2, vec1)
	-- This is a way of finding the angle between the two vectors:
	return math.atan2(vec1.X * vec2.Z - vec1.Z * vec2.X, vec1.X * vec2.X + vec1.Z * vec2.Z)
end

local function CreateFollowCamera()
	local module = RootCameraCreator()

	local tweenAcceleration = math.rad(220)
	local tweenSpeed = math.rad(0)
	local tweenMaxSpeed = math.rad(250)
	local timeBeforeAutoRotate = 2

	local lastUpdate = tick()
	module.LastUserPanCamera = tick()
	function module:Update()
		local now = tick()

		local userPanningTheCamera = (self.UserPanningTheCamera == true)
		local camera = workspace.CurrentCamera
		local player = PlayersService.LocalPlayer
		local humanoid = self:GetHumanoid()
		local cameraSubject = camera and camera.CameraSubject
		local isClimbing = humanoid and humanoid:GetState() == Enum.HumanoidStateType.Climbing
		local isInVehicle = cameraSubject and cameraSubject:IsA("VehicleSeat")
		local isOnASkateboard = cameraSubject and cameraSubject:IsA("SkateboardPlatform")

		if lastUpdate == nil or now - lastUpdate > 1 then
			module:ResetCameraLook()
			self.LastCameraTransform = nil
		end

		if lastUpdate then
			-- Cap out the delta to 0.5 so we don't get some crazy things when we re-resume from
			local delta = math.min(0.5, now - lastUpdate)
			local angle = 0
			if not (isInVehicle or isOnASkateboard) then
				angle = angle + (self.TurningLeft and -120 or 0)
				angle = angle + (self.TurningRight and 120 or 0)
			end

			local gamepadRotation = self:UpdateGamepad()
			if gamepadRotation ~= Vector2.new(0, 0) then
				userPanningTheCamera = true
				self.RotateInput = self.RotateInput + gamepadRotation
			end

			if angle ~= 0 then
				userPanningTheCamera = true
				self.RotateInput = self.RotateInput + Vector2.new(math.rad(angle * delta), 0)
			end
		end

		-- Reset tween speed if user is panning
		if userPanningTheCamera then
			tweenSpeed = 0
			module.LastUserPanCamera = tick()
		end

		local userRecentlyPannedCamera = now - module.LastUserPanCamera < timeBeforeAutoRotate

		local subjectPosition = self:GetSubjectPosition()
		if subjectPosition and player and camera then
			local zoom = self:GetCameraZoom()
			if zoom < 0.5 then
				zoom = 0.5
			end

			if self:GetShiftLock() and not self:IsInFirstPerson() then
				local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput)
				local offset = ((newLookVector * XZ_VECTOR):Cross(UP_VECTOR).unit * 1.75)
				if IsFiniteVector3(offset) then
					subjectPosition = subjectPosition + offset
				end
			else
				if self.LastCameraTransform and not userPanningTheCamera then
					local isInFirstPerson = self:IsInFirstPerson()
					if
						(isClimbing or isInVehicle or isOnASkateboard)
						and lastUpdate
						and humanoid
						and humanoid.Torso
					then
						if isInFirstPerson then
							if
								self.LastSubjectCFrame
								and (isInVehicle or isOnASkateboard)
								and cameraSubject:IsA("BasePart")
							then
								local y = -findAngleBetweenXZVectors(
									self.LastSubjectCFrame.lookVector,
									cameraSubject.CFrame.lookVector
								)
								if IsFinite(y) then
									self.RotateInput = self.RotateInput + Vector2.new(y, 0)
								end
								tweenSpeed = 0
							end
						elseif not userRecentlyPannedCamera then
							local forwardVector = humanoid.Torso.CFrame.lookVector
							if isOnASkateboard then
								forwardVector = cameraSubject.CFrame.lookVector
							end
							local timeDelta = (now - lastUpdate)

							tweenSpeed = clamp(0, tweenMaxSpeed, tweenSpeed + tweenAcceleration * timeDelta)

							local percent = clamp(0, 1, tweenSpeed * timeDelta)
							if not isClimbing and self:IsInFirstPerson() then
								percent = 1
							end
							local y = findAngleBetweenXZVectors(forwardVector, self:GetCameraLook())
							-- Check for NaN
							if IsFinite(y) and math.abs(y) > 0.0001 then
								self.RotateInput = self.RotateInput + Vector2.new(y * percent, 0)
							end
						end
					elseif not (isInFirstPerson or userRecentlyPannedCamera) then
						local lastVec = -(self.LastCameraTransform.p - subjectPosition)
						local y = findAngleBetweenXZVectors(lastVec, self:GetCameraLook())
						-- Check for NaNs
						if IsFinite(y) and math.abs(y) > 0.0001 then
							self.RotateInput = self.RotateInput + Vector2.new(y, 0)
						end
					end
				end
			end
			local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput)
			self.RotateInput = Vector2.new()

			camera.Focus = CFrame.new(subjectPosition)
			if _G.LockTarget then
				camera.CoordinateFrame =
					CFrame.new(camera.Focus.p - (zoom * newLookVector), _G.LockTarget.Torso.Position)
			else
				camera.CoordinateFrame = CFrame.new(camera.Focus.p - (zoom * newLookVector), camera.Focus.p)
			end
			self.LastCameraTransform = camera.CoordinateFrame
			if isInVehicle or isOnASkateboard and cameraSubject:IsA("BasePart") then
				self.LastSubjectCFrame = cameraSubject.CFrame
			else
				self.LastSubjectCFrame = nil
			end
		end

		lastUpdate = now
	end

	return module
end

return CreateFollowCamera
