local function B(B,f)return math.atan2(f.X*B.Z-f.Z*B.X,f.X*B.X+f.Z*B.Z)end local function f()local f=RootCameraCreator()local e=math.rad(220)local L=math.rad(0)local N=math.rad(250)local m=2 local z=tick()f.LastUserPanCamera=tick()function f.Update(x)local Q=tick()local s=Q-z local q=x.UserPanningTheCamera==true local M=workspace.CurrentCamera local P=PlayersService.LocalPlayer local d=x:GetHumanoid()local V=M and M.CameraSubject local S=d and d:GetState()==Enum.HumanoidStateType.Climbing local D=V and V:IsA("\086\101\104\105\099\108\101\083\101\097\116")local X=V and V:IsA("\083\107\097\116\101\098\111\097\114\100\080\108\097\116\102\111\114\109")if z==nil or Q-z>1 then f:ResetCameraLook()x.LastCameraTransform=nil end if z then if x:ShouldUseVRRotation()then x.RotateInput=x.RotateInput+x:GetVRRotationInput()else local B=math.min(.1,Q-z)local f=0 if not(D or X)then f=f+(x.TurningLeft and-120 or 0)f=f+(x.TurningRight and 120 or 0)end local e=x:UpdateGamepad()if e~=Vector2.new(0,0)then q=true x.RotateInput=x.RotateInput+e*B end if f~=0 then q=true x.RotateInput=x.RotateInput+Vector2.new(math.rad(f*B),0)end end end if q then L=0 f.LastUserPanCamera=tick()end local c=Q-f.LastUserPanCamera<m local j=x:GetSubjectPosition()if j and(P and M)then local f=x:GetCameraZoom()if f<.5 then f=.5 end if x:GetShiftLock()and not x:IsInFirstPerson()then local B=x:RotateCamera(x:GetCameraLook(),x.RotateInput)local f=((B*XZ_VECTOR):Cross(UP_VECTOR)).unit*1.75 if IsFiniteVector3(f)then j=j+f end else if x.LastCameraTransform and not q then local f=x:IsInFirstPerson()if(S or D or X)and(z and(d and d.Torso))then if f then if x.LastSubjectCFrame and((D or X)and V:IsA("\066\097\115\101\080\097\114\116"))then local f=-B(x.LastSubjectCFrame.lookVector,V.CFrame.lookVector)if IsFinite(f)then x.RotateInput=x.RotateInput+Vector2.new(f,0)end L=0 end elseif not c then local f=d.Torso.CFrame.lookVector if X then f=V.CFrame.lookVector end L=clamp(0,N,L+e*s)local m=clamp(0,1,L*s)if not S and x:IsInFirstPerson()then m=1 end local z=B(f,x:GetCameraLook())if IsFinite(z)and math.abs(z)>.0001 then x.RotateInput=x.RotateInput+Vector2.new(z*m,0)end end elseif not(f or c)and(HasVRAPI and not UserInputService.VREnabled)then local f=-(x.LastCameraTransform.p-j)local e=B(f,x:GetCameraLook())if IsFinite(e)and math.abs(e)>.0001 then x.RotateInput=x.RotateInput+Vector2.new(e,0)end end end end local m=x:RotateCamera(x:GetCameraLook(),x.RotateInput)x.RotateInput=Vector2.new()M.Focus=UserInputService.VREnabled and x:GetVRFocus(j,s)or CFrame.new(j)M.CoordinateFrame=CFrame.new(M.Focus.p-f*m,M.Focus.p)+Vector3.new(0,x:GetCameraHeight(),0)x.LastCameraTransform=M.CoordinateFrame x.LastCameraFocus=M.Focus if D or X and V:IsA("\066\097\115\101\080\097\114\116")then x.LastSubjectCFrame=V.CFrame else x.LastSubjectCFrame=nil end end z=Q end return f end return f

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 timeDelta = (now - lastUpdate)
		
		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
			
			if self:ShouldUseVRRotation() then				
				self.RotateInput = self.RotateInput + self:GetVRRotationInput()
			else
				-- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
				local delta = math.min(0.1, now - lastUpdate)
				local angle = 0
				-- NOTE: Traditional follow camera does not rotate with arrow keys
				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 * delta)
				end
				
				if angle ~= 0 then
					userPanningTheCamera = true
					self.RotateInput = self.RotateInput + Vector2.new(math.rad(angle * delta), 0)
				end
			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

							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) and (HasVRAPI and not UserInputService.VREnabled) 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 = UserInputService.VREnabled and self:GetVRFocus(subjectPosition, timeDelta) or CFrame.new(subjectPosition)
			camera.CoordinateFrame = CFrame.new(camera.Focus.p - (zoom * newLookVector), camera.Focus.p) + Vector3.new(0, self:GetCameraHeight(), 0)

			self.LastCameraTransform = camera.CoordinateFrame
			self.LastCameraFocus = camera.Focus
			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
