local function E(E,R)return math.atan2(R.X*E.Z-R.Z*E.X,R.X*E.X+R.Z*E.Z)end local function R()local R=RootCameraCreator()local S=math.rad(220)local l=math.rad(0)local g=math.rad(250)local j=2 local r=nil local X=.7 local n=0 local c=.1 local d=1 local s=ZERO_VECTOR2 local C=.8 local F=nil local t=tick()R.LastUserPanCamera=tick()function R.Update(r)local X=tick()local n=X-t local c=r.UserPanningTheCamera==true local d=workspace.CurrentCamera local s=PlayersService.LocalPlayer local C=r:GetHumanoid()local F=d and d.CameraSubject local O=F and F:IsA("\086\101\104\105\099\108\101\083\101\097\116")local q=F and F:IsA("\083\107\097\116\101\098\111\097\114\100\080\108\097\116\102\111\114\109")if t==nil or X-t>1 then R:ResetCameraLook()r.LastCameraTransform=nil end if t then local E=r:UpdateGamepad()if r:ShouldUseVRRotation()then r.RotateInput=r.RotateInput+r:GetVRRotationInput()else local R=math.min(.1,X-t)if E~=ZERO_VECTOR2 then c=true r.RotateInput=r.RotateInput+E*R end local S=0 if not(O or q)then S=S+(r.TurningLeft and-120 or 0)S=S+(r.TurningRight and 120 or 0)end if S~=0 then r.RotateInput=r.RotateInput+Vector2.new(math.rad(S*R),0)c=true end end end if c then l=0 R.LastUserPanCamera=tick()end local z=X-R.LastUserPanCamera<j local h=r:GetSubjectPosition()if h and(s and d)then local R=r:GetCameraZoom()if R<.5 then R=.5 end if r:GetShiftLock()and not r:IsInFirstPerson()then local E=r:RotateCamera(r:GetCameraLook(),r.RotateInput)local R=((E*XZ_VECTOR):Cross(UP_VECTOR)).unit*1.75 if IsFiniteVector3(R)then h=h+R end else if r.LastCameraTransform and not c then local R=r:IsInFirstPerson()if(O or q)and(t and(C and C.Torso))then if R then if r.LastSubjectCFrame and((O or q)and F:IsA("\066\097\115\101\080\097\114\116"))then local R=-E(r.LastSubjectCFrame.lookVector,F.CFrame.lookVector)if IsFinite(R)then r.RotateInput=r.RotateInput+Vector2.new(R,0)end l=0 end elseif not z then local R=C.Torso.CFrame.lookVector if q then R=F.CFrame.lookVector end l=clamp(0,g,l+S*n)local j=clamp(0,1,l*n)if r:IsInFirstPerson()then j=1 end local X=E(R,r:GetCameraLook())if IsFinite(X)and math.abs(X)>.0001 then r.RotateInput=r.RotateInput+Vector2.new(X*j,0)end end end end end local j=r:RotateCamera(r:GetCameraLook(),r.RotateInput)r.RotateInput=ZERO_VECTOR2 d.Focus=UserInputService.VREnabled and r:GetVRFocus(h,n)or CFrame.new(h)d.CoordinateFrame=CFrame.new(d.Focus.p-R*j,d.Focus.p)+Vector3.new(0,r:GetCameraHeight(),0)r.LastCameraTransform=d.CoordinateFrame r.LastCameraFocus=d.Focus if O or q and F:IsA("\066\097\115\101\080\097\114\116")then r.LastSubjectCFrame=F.CFrame else r.LastSubjectCFrame=nil end end t=X end return R end return R
-- May return NaN or inf or -inf
-- This is a way of finding the angle between the two vectors:
local function findAngleBetweenXZVectors(vec2, vec1)
	return math.atan2(vec1.X * vec2.Z - vec1.Z * vec2.X, vec1.X * vec2.X + vec1.Z * vec2.Z)
end

local function CreateClassicCamera()
	local module = RootCameraCreator()

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

	local lastThumbstickRotate = nil
	local numOfSeconds = 0.7
	local currentSpeed = 0
	local maxSpeed = 0.1
	local thumbstickSensitivity = 1
	local lastThumbstickPos = ZERO_VECTOR2
	local ySensitivity = 0.8
	local lastVelocity = nil

	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 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
			local gamepadRotation = self:UpdateGamepad()

			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)

				if gamepadRotation ~= ZERO_VECTOR2 then
					userPanningTheCamera = true
					self.RotateInput = self.RotateInput + (gamepadRotation * delta)
				end

				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

				if angle ~= 0 then
					self.RotateInput = self.RotateInput + Vector2.new(math.rad(angle * delta), 0)
					userPanningTheCamera = true
				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
				-- We need to use the right vector of the camera after rotation, not before
				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 (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 self:IsInFirstPerson() then
								percent = 1
							end

							local y = findAngleBetweenXZVectors(forwardVector, self:GetCameraLook())
							if IsFinite(y) and math.abs(y) > 0.0001 then
								self.RotateInput = self.RotateInput + Vector2.new(y * percent, 0)
							end
						end
					end
				end
			end

			local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput)
			self.RotateInput = ZERO_VECTOR2

			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 CreateClassicCamera
