local function Z(Z,N)return math_atan2(N.X*Z.Z-N.Z*Z.X,N.X*Z.X+N.Z*Z.Z)end local function N()local N=RootCameraCreator()local Q=math_rad(220)local B=math_rad(0)local Y=math_rad(250)local M=2 local A=tick()N.LastUserPanCamera=tick()function N.Update(D)N:ProcessTweens()local f=tick()local L=f-A local C=D.UserPanningTheCamera==true local V=workspace.CurrentCamera local n=PlayersService.LocalPlayer local O=D:GetHumanoid()local p=V and V.CameraSubject local T=p and p:IsA("\086\101\104\105\099\108\101\083\101\097\116")local I=p and p:IsA("\083\107\097\116\101\098\111\097\114\100\080\108\097\116\102\111\114\109")if A==nil or f-A>1 then N:ResetCameraLook()D.LastCameraTransform=nil end if A then local Z=D:UpdateGamepad()if D:ShouldUseVRRotation()then D.RotateInput=D.RotateInput+D:GetVRRotationInput()else local N=math_min(.1,f-A)if Z~=ZERO_VECTOR2 then C=true D.RotateInput=D.RotateInput+Z*N end local Q=0 if not(T or I)then Q=Q+(D.TurningLeft and-120 or 0)Q=Q+(D.TurningRight and 120 or 0)end if Q~=0 then D.RotateInput=D.RotateInput+Vector2.new(math_rad(Q*N),0)C=true end end end if C then B=0 N.LastUserPanCamera=tick()end local q=f-N.LastUserPanCamera<M local w=D:GetSubjectPosition()if w and(n and V)then local N=D:GetCameraZoom()if N<.5 then N=.5 end if D:GetShiftLock()and not D:IsInFirstPerson()then local Z=D:RotateCamera(D:GetCameraLook(),D.RotateInput)local N=((Z*XZ_VECTOR):Cross(UP_VECTOR)).unit*1.75 if IsFiniteVector3(N)then w=w+N end else if not C and D.LastCameraTransform then local N=D:IsInFirstPerson()if(T or I)and(A and(O and O.Torso))then if N then if D.LastSubjectCFrame and((T or I)and p:IsA("\066\097\115\101\080\097\114\116"))then local N=-Z(D.LastSubjectCFrame.lookVector,p.CFrame.lookVector)if IsFinite(N)then D.RotateInput=D.RotateInput+Vector2.new(N,0)end B=0 end elseif not q then local N=O.Torso.CFrame.lookVector if I then N=p.CFrame.lookVector end B=clamp(0,Y,B+Q*L)local M=clamp(0,1,B*L)if D:IsInFirstPerson()then M=1 end local A=Z(N,D:GetCameraLook())if IsFinite(A)and math_abs(A)>.0001 then D.RotateInput=D.RotateInput+Vector2.new(A*M,0)end end end end end local M=VRService.VREnabled V.Focus=M and D:GetVRFocus(w,L)or CFrame_new(w)local f=V.Focus.p if M and not D:IsInFirstPerson()then local Z=D:GetCameraHeight()local Q=w-V.CFrame.p local B=Q.magnitude if B>N or D.RotateInput.x~=0 then local Y=math_min(B,N)Q=D:RotateCamera(Q.unit*XZ_VECTOR,Vector2.new(D.RotateInput.x,0))*Y local M=f-Q local A=V.CFrame.lookVector if D.RotateInput.x~=0 then A=Q end local L=Vector3.new(M.x+A.x,M.y,M.z+A.z)D.RotateInput=ZERO_VECTOR2 V.CFrame=CFrame_new(M,L)+Vector3_new(0,Z,0)end else local Z=D:RotateCamera(D:GetCameraLook(),D.RotateInput)D.RotateInput=ZERO_VECTOR2 V.CFrame=CFrame_new(f-N*Z,f)end D.LastCameraTransform=V.CFrame D.LastCameraFocus=V.Focus if(T or I)and p:IsA("\066\097\115\101\080\097\114\116")then D.LastSubjectCFrame=p.CFrame else D.LastSubjectCFrame=nil end end A=f end return N end return N
-- 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 lastUpdate = tick()
	module.LastUserPanCamera = tick()
	function module:Update()
		module:ProcessTweens()
		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 not userPanningTheCamera and self.LastCameraTransform 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 VREnabled = VRService.VREnabled
			camera.Focus = VREnabled and self:GetVRFocus(subjectPosition, timeDelta) or CFrame_new(subjectPosition)

			local cameraFocusP = camera.Focus.p
			if VREnabled and not self:IsInFirstPerson() then
				local cameraHeight = self:GetCameraHeight()
				local vecToSubject = (subjectPosition - camera.CFrame.p)
				local distToSubject = vecToSubject.magnitude

				-- Only move the camera if it exceeded a maximum distance to the subject in VR
				if distToSubject > zoom or self.RotateInput.x ~= 0 then
					local desiredDist = math_min(distToSubject, zoom)
					vecToSubject = self:RotateCamera(vecToSubject.unit * XZ_VECTOR, Vector2.new(self.RotateInput.x, 0))
						* desiredDist
					local newPos = cameraFocusP - vecToSubject
					local desiredLookDir = camera.CFrame.lookVector
					if self.RotateInput.x ~= 0 then
						desiredLookDir = vecToSubject
					end
					local lookAt = Vector3.new(newPos.x + desiredLookDir.x, newPos.y, newPos.z + desiredLookDir.z)
					self.RotateInput = ZERO_VECTOR2

					camera.CFrame = CFrame_new(newPos, lookAt) + Vector3_new(0, cameraHeight, 0)
				end
			else
				local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput)
				self.RotateInput = ZERO_VECTOR2
				camera.CFrame = CFrame_new(cameraFocusP - (zoom * newLookVector), cameraFocusP)
			end

			self.LastCameraTransform = camera.CFrame
			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
