local function D(D,R)return math.atan2(R.X*D.Z-R.Z*D.X,R.X*D.X+R.Z*D.Z)end local function R(D,R)local j=(R:Cross(D)).unit local Y=(D:Cross(j)).unit D=D.unit return CFrame.new(0,0,0,j.x,Y.x,D.x,j.y,Y.y,D.y,j.z,Y.z,D.z)end local function j()local j=RootCameraCreator()local Y=math.rad(220)local f=math.rad(0)local A=math.rad(250)local P=2 local T=nil local e=.7 local k=0 local N=.1 local W=1 local B=Vector2.new(0,0)local Z=.8 local s=nil local h=tick()j.LastUserPanCamera=tick()function j.Update(T)local e=tick()local k=T.UserPanningTheCamera==true local N=workspace.CurrentCamera local W=PlayersService.LocalPlayer local B=T:GetHumanoid()local Z=N and N.CameraSubject local s=Z and Z:IsA("\086\101\104\105\099\108\101\083\101\097\116")local g=Z and Z:IsA("\083\107\097\116\101\098\111\097\114\100\080\108\097\116\102\111\114\109")if h==nil or e-h>1 then j:ResetCameraLook()T.LastCameraTransform=nil end if h then local D=math.min(.5,e-h)local R=0 if not(s or g)then R=R+(T.TurningLeft and-120 or 0)R=R+(T.TurningRight and 120 or 0)end local j=T:UpdateGamepad()if j~=Vector2.new(0,0)then k=true T.RotateInput=T.RotateInput+j end if R~=0 then k=true T.RotateInput=T.RotateInput+Vector2.new(math.rad(R*D),0)end end if k then f=0 j.LastUserPanCamera=tick()end local J=e-j.LastUserPanCamera<P local E=T:GetSubjectPosition()if E and(W and N)then local j=T:GetCameraZoom()if j<.5 then j=.5 end if T:GetShiftLock()and not T:IsInFirstPerson()then local D=T:RotateCamera(T:GetCameraLook(),T.RotateInput)local R=((D*XZ_VECTOR):Cross(UP_VECTOR)).unit*1.75 if IsFiniteVector3(R)then E=E+R end else if T.LastCameraTransform and not k then local R=T:IsInFirstPerson()if(s or g)and(h and(B and B.Torso))then if R then if T.LastSubjectCFrame and((s or g)and Z:IsA("\066\097\115\101\080\097\114\116"))then local R=-D(T.LastSubjectCFrame.lookVector,Z.CFrame.lookVector)if IsFinite(R)then T.RotateInput=T.RotateInput+Vector2.new(R,0)end f=0 end elseif not J then local R=B.Torso.CFrame.lookVector if g then R=Z.CFrame.lookVector end local j=e-h f=clamp(0,A,f+Y*j)local P=clamp(0,1,f*j)if T:IsInFirstPerson()then P=1 end local k=D(R,T:GetCameraLook())if IsFinite(k)and math.abs(k)>.0001 then T.RotateInput=T.RotateInput+Vector2.new(k*P,0)end end end end end local P=T:RotateCamera(T:GetCameraLook(),T.RotateInput)T.RotateInput=Vector2.new()N.Focus=CFrame.new(E)local W=(T.DownVector or{Value=Vector3.new(0,-1,0)}).Value local c=R(-P,-W)N.CoordinateFrame=(CFrame.new(N.Focus.p)*c)*CFrame.new(0,0,j)T.LastCameraTransform=N.CoordinateFrame if s or g and Z:IsA("\066\097\115\101\080\097\114\116")then T.LastSubjectCFrame=Z.CFrame else T.LastSubjectCFrame=nil end end h=e end return j end return j

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 GetOrientationZY(backVector, upVector)
	local rightVector = upVector:Cross(backVector).unit;
	local upVector = backVector:Cross(rightVector).unit;
	backVector = backVector.unit;
	return CFrame.new(0, 0, 0, rightVector.x, upVector.x, backVector.x, rightVector.y, upVector.y, backVector.y, rightVector.z, upVector.z, backVector.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 = Vector2.new(0,0)
	local ySensitivity = 0.8
	local lastVelocity = nil

	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 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
				-- 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
							local timeDelta = (now - lastUpdate)
							
							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 = Vector2.new()
			
			camera.Focus = CFrame.new(subjectPosition)
			local downVector = (self.DownVector or {Value=Vector3.new(0, -1, 0)}).Value;
			local camOrientation = GetOrientationZY(-newLookVector, -downVector);
			camera.CoordinateFrame = CFrame.new(camera.Focus.p) * camOrientation * CFrame.new(0, 0, zoom);
			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 CreateClassicCamera
