function BaseCamera.OnCurrentCameraChanged(k)if UserInputService.TouchEnabled then if k.viewportSizeChangedConn then k.viewportSizeChangedConn:Disconnect()k.viewportSizeChangedConn=nil end local K=game.Workspace.CurrentCamera if K then k:OnViewportSizeChanged()k.viewportSizeChangedConn=(K:GetPropertyChangedSignal("\086\105\101\119\112\111\114\116\083\105\122\101")):Connect(function()k:OnViewportSizeChanged()end)end end if k.cameraSubjectChangedConn then k.cameraSubjectChangedConn:Disconnect()k.cameraSubjectChangedConn=nil end local K=game.Workspace.CurrentCamera if K then k.cameraSubjectChangedConn=(K:GetPropertyChangedSignal("\067\097\109\101\114\097\083\117\098\106\101\099\116")):Connect(function()k:OnNewCameraSubject()end)k:OnNewCameraSubject()end end function BaseCamera.OnDynamicThumbstickEnabled(k)if UserInputService.TouchEnabled then k.isDynamicThumbstickEnabled=true end end function BaseCamera.OnDynamicThumbstickDisabled(k)k.isDynamicThumbstickEnabled=false end function BaseCamera.OnGameSettingsTouchMovementModeChanged(k)if player.DevTouchMovementMode==Enum.DevTouchMovementMode.UserChoice then if UserGameSettings.TouchMovementMode==Enum.TouchMovementMode.DynamicThumbstick or UserGameSettings.TouchMovementMode==Enum.TouchMovementMode.Default then k:OnDynamicThumbstickEnabled()else k:OnDynamicThumbstickDisabled()end end end function BaseCamera.OnDevTouchMovementModeChanged(k)if player.DevTouchMovementMode==Enum.DevTouchMovementMode.DynamicThumbstick then k:OnDynamicThumbstickEnabled()else k:OnGameSettingsTouchMovementModeChanged()end end function BaseCamera.OnPlayerCameraPropertyChange(k)k:SetCameraToSubjectDistance(k.currentSubjectDistance)end function BaseCamera.GetCameraHeight(k)if VRService.VREnabled and not k.inFirstPerson then return math.sin(VR_ANGLE)*k.currentSubjectDistance end return 0 end function BaseCamera.InputTranslationToCameraAngleChange(V,k,K)return k*K end function BaseCamera.GamepadZoomPress(k)local K=k:GetCameraToSubjectDistance()if K>(GAMEPAD_ZOOM_STEP_2+GAMEPAD_ZOOM_STEP_3)/2 then k:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_2)elseif K>(GAMEPAD_ZOOM_STEP_1+GAMEPAD_ZOOM_STEP_2)/2 then k:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_1)else k:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_3)end end function BaseCamera.Enable(K,k)if K.enabled~=k then K.enabled=k if K.enabled then CameraInput.setInputEnabled(true)K.gamepadZoomPressConnection=CameraInput.gamepadZoomPress:Connect(function()K:GamepadZoomPress()end)if player.CameraMode==Enum.CameraMode.LockFirstPerson then K.currentSubjectDistance=.5 if not K.inFirstPerson then K:EnterFirstPerson()end end else CameraInput.setInputEnabled(false)if K.gamepadZoomPressConnection then K.gamepadZoomPressConnection:Disconnect()K.gamepadZoomPressConnection=nil end K:Cleanup()end end end function BaseCamera.GetEnabled(k)return k.enabled end function BaseCamera.Cleanup(k)if k.subjectStateChangedConn then k.subjectStateChangedConn:Disconnect()k.subjectStateChangedConn=nil end if k.viewportSizeChangedConn then k.viewportSizeChangedConn:Disconnect()k.viewportSizeChangedConn=nil end k.lastCameraTransform=nil k.lastSubjectCFrame=nil if UserInputService.MouseBehavior~=Enum.MouseBehavior.LockCenter then UserInputService.MouseBehavior=Enum.MouseBehavior.Default end end function BaseCamera.UpdateMouseBehavior(k)if k.isCameraToggle then CameraUI.setCameraModeToastEnabled(true)CameraInput.enableCameraToggleInput()CameraToggleStateController(k.inFirstPerson)else CameraUI.setCameraModeToastEnabled(false)CameraInput.disableCameraToggleInput()if k.inFirstPerson or k.inMouseLockedMode then UserGameSettings.RotationType=Enum.RotationType.MovementRelative UserInputService.MouseBehavior=Enum.MouseBehavior.LockCenter else UserGameSettings.RotationType=Enum.RotationType.MovementRelative UserInputService.MouseBehavior=Enum.MouseBehavior.Default end end end function BaseCamera.UpdateForDistancePropertyChange(k)k:SetCameraToSubjectDistance(k.currentSubjectDistance)end function BaseCamera.SetCameraToSubjectDistance(K,k)local V=K.currentSubjectDistance if player.CameraMode==Enum.CameraMode.LockFirstPerson then K.currentSubjectDistance=.5 if not K.inFirstPerson then K:EnterFirstPerson()end else local V=math.clamp(k,player.CameraMinZoomDistance,player.CameraMaxZoomDistance)if V<FIRST_PERSON_DISTANCE_THRESHOLD then K.currentSubjectDistance=.5 if not K.inFirstPerson then K:EnterFirstPerson()end else K.currentSubjectDistance=V if K.inFirstPerson then K:LeaveFirstPerson()end end end ZoomController.SetZoomParameters(K.currentSubjectDistance,math.sign(k-V))return K.currentSubjectDistance end function BaseCamera.SetCameraType(K,k)K.cameraType=k end function BaseCamera.GetCameraType(k)return k.cameraType end
-- Listener for changes to workspace.CurrentCamera
function BaseCamera:OnCurrentCameraChanged()
	if UserInputService.TouchEnabled then
		if self.viewportSizeChangedConn then
			self.viewportSizeChangedConn:Disconnect()
			self.viewportSizeChangedConn = nil
		end

		local newCamera = game.Workspace.CurrentCamera

		if newCamera then
			self:OnViewportSizeChanged()
			self.viewportSizeChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
				self:OnViewportSizeChanged()
			end)
		end
	end

	-- VR support additions
	if self.cameraSubjectChangedConn then
		self.cameraSubjectChangedConn:Disconnect()
		self.cameraSubjectChangedConn = nil
	end

	local camera = game.Workspace.CurrentCamera
	if camera then
		self.cameraSubjectChangedConn = camera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
			self:OnNewCameraSubject()
		end)
		self:OnNewCameraSubject()
	end
end

function BaseCamera:OnDynamicThumbstickEnabled()
	if UserInputService.TouchEnabled then
		self.isDynamicThumbstickEnabled = true
	end
end

function BaseCamera:OnDynamicThumbstickDisabled()
	self.isDynamicThumbstickEnabled = false
end

function BaseCamera:OnGameSettingsTouchMovementModeChanged()
	if player.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice then
		if
			UserGameSettings.TouchMovementMode == Enum.TouchMovementMode.DynamicThumbstick
			or UserGameSettings.TouchMovementMode == Enum.TouchMovementMode.Default
		then
			self:OnDynamicThumbstickEnabled()
		else
			self:OnDynamicThumbstickDisabled()
		end
	end
end

function BaseCamera:OnDevTouchMovementModeChanged()
	if player.DevTouchMovementMode == Enum.DevTouchMovementMode.DynamicThumbstick then
		self:OnDynamicThumbstickEnabled()
	else
		self:OnGameSettingsTouchMovementModeChanged()
	end
end

function BaseCamera:OnPlayerCameraPropertyChange()
	-- This call forces re-evaluation of player.CameraMode and clamping to min/max distance which may have changed
	self:SetCameraToSubjectDistance(self.currentSubjectDistance)
end

function BaseCamera:GetCameraHeight()
	if VRService.VREnabled and not self.inFirstPerson then
		return math.sin(VR_ANGLE) * self.currentSubjectDistance
	end
	return 0
end

function BaseCamera:InputTranslationToCameraAngleChange(translationVector, sensitivity)
	return translationVector * sensitivity
end

function BaseCamera:GamepadZoomPress()
	local dist = self:GetCameraToSubjectDistance()

	if dist > (GAMEPAD_ZOOM_STEP_2 + GAMEPAD_ZOOM_STEP_3) / 2 then
		self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_2)
	elseif dist > (GAMEPAD_ZOOM_STEP_1 + GAMEPAD_ZOOM_STEP_2) / 2 then
		self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_1)
	else
		self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_3)
	end
end

function BaseCamera:Enable(enable)
	if self.enabled ~= enable then
		self.enabled = enable
		if self.enabled then
			CameraInput.setInputEnabled(true)

			self.gamepadZoomPressConnection = CameraInput.gamepadZoomPress:Connect(function()
				self:GamepadZoomPress()
			end)

			if player.CameraMode == Enum.CameraMode.LockFirstPerson then
				self.currentSubjectDistance = 0.5
				if not self.inFirstPerson then
					self:EnterFirstPerson()
				end
			end
		else
			CameraInput.setInputEnabled(false)

			if self.gamepadZoomPressConnection then
				self.gamepadZoomPressConnection:Disconnect()
				self.gamepadZoomPressConnection = nil
			end
			-- Clean up additional event listeners and reset a bunch of properties
			self:Cleanup()
		end
	end
end

function BaseCamera:GetEnabled()
	return self.enabled
end

function BaseCamera:Cleanup()
	if self.subjectStateChangedConn then
		self.subjectStateChangedConn:Disconnect()
		self.subjectStateChangedConn = nil
	end
	if self.viewportSizeChangedConn then
		self.viewportSizeChangedConn:Disconnect()
		self.viewportSizeChangedConn = nil
	end

	self.lastCameraTransform = nil
	self.lastSubjectCFrame = nil

	-- Unlock mouse for example if right mouse button was being held down
	if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
		UserInputService.MouseBehavior = Enum.MouseBehavior.Default
	end
end

function BaseCamera:UpdateMouseBehavior()
	if self.isCameraToggle then
		CameraUI.setCameraModeToastEnabled(true)
		CameraInput.enableCameraToggleInput()
		CameraToggleStateController(self.inFirstPerson)
	else
		CameraUI.setCameraModeToastEnabled(false)
		CameraInput.disableCameraToggleInput()

		-- first time transition to first person mode or mouse-locked third person
		if self.inFirstPerson or self.inMouseLockedMode then
			UserGameSettings.RotationType = Enum.RotationType.MovementRelative
			UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
		else
			UserGameSettings.RotationType = Enum.RotationType.MovementRelative
			UserInputService.MouseBehavior = Enum.MouseBehavior.Default
		end
	end
end

function BaseCamera:UpdateForDistancePropertyChange()
	-- Calling this setter with the current value will force checking that it is still
	-- in range after a change to the min/max distance limits
	self:SetCameraToSubjectDistance(self.currentSubjectDistance)
end

function BaseCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
	local lastSubjectDistance = self.currentSubjectDistance

	-- By default, camera modules will respect LockFirstPerson and override the currentSubjectDistance with 0
	-- regardless of what Player.CameraMinZoomDistance is set to, so that first person can be made
	-- available by the developer without needing to allow players to mousewheel dolly into first person.
	-- Some modules will override this function to remove or change first-person capability.
	if player.CameraMode == Enum.CameraMode.LockFirstPerson then
		self.currentSubjectDistance = 0.5
		if not self.inFirstPerson then
			self:EnterFirstPerson()
		end
	else
		local newSubjectDistance =
			math.clamp(desiredSubjectDistance, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
		if newSubjectDistance < FIRST_PERSON_DISTANCE_THRESHOLD then
			self.currentSubjectDistance = 0.5
			if not self.inFirstPerson then
				self:EnterFirstPerson()
			end
		else
			self.currentSubjectDistance = newSubjectDistance
			if self.inFirstPerson then
				self:LeaveFirstPerson()
			end
		end
	end

	-- Pass target distance and zoom direction to the zoom controller
	ZoomController.SetZoomParameters(
		self.currentSubjectDistance,
		math.sign(desiredSubjectDistance - lastSubjectDistance)
	)

	-- Returned only for convenience to the caller to know the outcome
	return self.currentSubjectDistance
end

function BaseCamera:SetCameraType(cameraType)
	--Used by derived classes
	self.cameraType = cameraType
end

function BaseCamera:GetCameraType()
	return self.cameraType
end
