local function g(g,E)return math.acos(g:Dot(E)/(g.magnitude*E.magnitude))end local E={}local function X(g)local X=g and g.Character if X then local b=E[g]if b and b.Parent==X then return b else E[g]=nil for X,b in pairs(X:GetChildren())do if b:IsA("\072\117\109\097\110\111\105\100")then E[g]=b return b end end end end end local function b()local g=RootCameraCreator()local E=math.rad(250)local b=math.rad(0)local l=math.rad(250)local z=tick()function g.Update(O)local Y=tick()local A=O.UserPanningTheCamera==true if z==nil or Y-z>1 then g:ResetCameraLook()O.LastCameraTransform=nil end if z then local g=math.min(.5,Y-z)local E=O.TurningLeft and-120 or 0 E=E+(O.TurningRight and 120 or 0)if E~=0 then A=true end O:RotateCamera(O:GetCameraLook(),Vector2.new(math.rad(E*g),0))end if A then b=0 end local e=workspace.CurrentCamera local i=PlayersService.LocalPlayer local F=O:GetSubjectPosition()if F and(i and e)then local g=O:GetCameraZoom()if g<=0 then g=.1 end if O:GetShiftLock()and not O:IsInFirstPerson()then local E=((O:GetCameraLook()*XZ_VECTOR):Cross(UP_VECTOR)).unit*1.75 if IsFiniteVector3(E)then F=F+E end g=math.max(g,5)else if O.LastCameraTransform and not A then local g=X(i)local A=e and e.CameraSubject local s=g and g:GetState()==Enum.HumanoidStateType.Climbing local G=A and A:IsA("\086\101\104\105\099\108\101\083\101\097\116")local h=A and A:IsA("\083\107\097\116\101\098\111\097\114\100\080\108\097\116\102\111\114\109")if(s or G or h)and(z and(g and g.Torso))then local X=g.Torso.CFrame.lookVector if h then X=A.CFrame.lookVector end local e=Y-z b=clamp(0,l,b+E*e)local i=clamp(0,1,b*e)if not s and O:IsInFirstPerson()then i=1 end local F=findAngleBetweenXZVectors(X,O:GetCameraLook())if IsFinite(F)then O:RotateCamera(O:GetCameraLook(),Vector3.new(F*i,0,0))end elseif not O:IsInFirstPerson()then local g=-(O.LastCameraTransform.p-F)local E=findAngleBetweenXZVectors(g,O:GetCameraLook())if IsFinite(E)then O:RotateCamera(O:GetCameraLook(),Vector3.new(E,0,0))end end end end e.Focus=CFrame.new(F)e.CoordinateFrame=CFrame.new(e.Focus.p-g*O:GetCameraLook(),e.Focus.p)O.LastCameraTransform=e.CoordinateFrame end z=Y end return g end return b

local function absoluteAngleBetween3dVectors(vec1, vec2)
	return math.acos(vec1:Dot(vec2) / (vec1.magnitude * vec2.magnitude))
end

local humanoidCache = {}
local function findPlayerHumanoid(player)
	local character = player and player.Character
	if character then
		local resultHumanoid = humanoidCache[player]
		if resultHumanoid and resultHumanoid.Parent == character then
			return resultHumanoid
		else
			humanoidCache[player] = nil -- Bust Old Cache
			for _, child in pairs(character:GetChildren()) do
				if child:IsA('Humanoid') then
					humanoidCache[player] = child
					return child
				end
			end
		end
	end
end

local function CreateFollowCamera()
	local module = RootCameraCreator()
	
	local tweenAcceleration = math.rad(250)
	local tweenSpeed = math.rad(0)
	local tweenMaxSpeed = math.rad(250)
	
	local lastUpdate = tick()
	function module:Update()
		local now = tick()
		local userPanningTheCamera = (self.UserPanningTheCamera == true)

		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 = self.TurningLeft and -120 or 0
			angle = angle + (self.TurningRight and 120 or 0)
			if angle ~= 0 then userPanningTheCamera = true end
			self:RotateCamera(self:GetCameraLook(), Vector2.new(math.rad(angle * delta), 0))
		end
		
		-- Reset tween speed if user is panning
		if userPanningTheCamera then
			tweenSpeed = 0
		end
		

		local camera = 	workspace.CurrentCamera
		local player = PlayersService.LocalPlayer
		local subjectPosition = self:GetSubjectPosition()
		
		if subjectPosition and player and camera then
			local zoom = self:GetCameraZoom()
			if zoom <= 0 then
				zoom = 0.1
			end
			
			if self:GetShiftLock() and not self:IsInFirstPerson() then
				local offset = ((self:GetCameraLook() * XZ_VECTOR):Cross(UP_VECTOR).unit * 1.75)
				-- Check for NaNs
				if IsFiniteVector3(offset) then
					subjectPosition = subjectPosition + offset
				end
				zoom = math.max(zoom, 5)
			else
				if self.LastCameraTransform and not userPanningTheCamera then
					local humanoid = findPlayerHumanoid(player)
					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 (isClimbing or isInVehicle or isOnASkateboard) and lastUpdate and humanoid and humanoid.Torso 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) then
							self:RotateCamera(self:GetCameraLook(), Vector3.new(y * percent, 0, 0))
						end
					elseif not self:IsInFirstPerson() then
						local lastVec = -(self.LastCameraTransform.p - subjectPosition)
						local y = findAngleBetweenXZVectors(lastVec, self:GetCameraLook())
						-- Check for NaNs
						if IsFinite(y)then
							self:RotateCamera(self:GetCameraLook(), Vector3.new(y, 0, 0))
						end
					end
				end
			end
			
			camera.Focus = CFrame.new(subjectPosition)
			camera.CoordinateFrame = CFrame.new(camera.Focus.p - (zoom * self:GetCameraLook()), camera.Focus.p)
			self.LastCameraTransform = camera.CoordinateFrame
		end
		
		lastUpdate = now
	end
	
	return module
end

return CreateFollowCamera
