function BaseCamera.OnCurrentCameraChanged(n)if UserInputService.TouchEnabled then if n.viewportSizeChangedConn then n.viewportSizeChangedConn:Disconnect()n.viewportSizeChangedConn=nil end local R=game.Workspace.CurrentCamera if R then n:OnViewportSizeChanged()n.viewportSizeChangedConn=(R:GetPropertyChangedSignal("\086\105\101\119\112\111\114\116\083\105\122\101")):Connect(function()n:OnViewportSizeChanged()end)end end if n.cameraSubjectChangedConn then n.cameraSubjectChangedConn:Disconnect()n.cameraSubjectChangedConn=nil end local R=game.Workspace.CurrentCamera if R then n.cameraSubjectChangedConn=(R:GetPropertyChangedSignal("\067\097\109\101\114\097\083\117\098\106\101\099\116")):Connect(function()n:OnNewCameraSubject()end)n:OnNewCameraSubject()end end function BaseCamera.OnDynamicThumbstickEnabled(n)if UserInputService.TouchEnabled then n.isDynamicThumbstickEnabled=true end end function BaseCamera.OnDynamicThumbstickDisabled(n)n.isDynamicThumbstickEnabled=false end function BaseCamera.OnGameSettingsTouchMovementModeChanged(n)if Players.LocalPlayer.DevTouchMovementMode==Enum.DevTouchMovementMode.UserChoice then if UserGameSettings.TouchMovementMode.Name=="\068\121\110\097\109\105\099\084\104\117\109\098\115\116\105\099\107"then n:OnDynamicThumbstickEnabled()else n:OnDynamicThumbstickDisabled()end end end function BaseCamera.OnDevTouchMovementModeChanged(n)if Players.LocalPlayer.DevTouchMovementMode.Name=="\068\121\110\097\109\105\099\084\104\117\109\098\115\116\105\099\107"then n:OnDynamicThumbstickEnabled()else n:OnGameSettingsTouchMovementModeChanged()end end function BaseCamera.OnPlayerCameraPropertyChange(n)n:SetCameraToSubjectDistance(n.currentSubjectDistance)end function BaseCamera.GetCameraHeight(n)if VRService.VREnabled and not n.inFirstPerson then return math.sin(VR_ANGLE)*n.currentSubjectDistance end return 0 end function BaseCamera.InputTranslationToCameraAngleChange(Y,n,R)local z=game.Workspace.CurrentCamera if z and(z.ViewportSize.X>0 and(z.ViewportSize.Y>0 and z.ViewportSize.Y>z.ViewportSize.X))then return n*Vector2.new(R.Y,R.X)end return n*R end function BaseCamera.Enable(R,n)if R.enabled~=n then R.enabled=n if R.enabled then R:ConnectInputEvents()if FFlagPlayerScriptsBindAtPriority then R:BindContextActions()end if Players.LocalPlayer.CameraMode==Enum.CameraMode.LockFirstPerson then R.currentSubjectDistance=.5 if not R.inFirstPerson then R:EnterFirstPerson()end end else R:DisconnectInputEvents()if FFlagPlayerScriptsBindAtPriority then R:UnbindContextActions()end R:Cleanup()end end end function BaseCamera.GetEnabled(n)return n.enabled end function BaseCamera.OnInputBegan(Y,n,R)if n.UserInputType==Enum.UserInputType.Touch then Y:OnTouchBegan(n,R)elseif n.UserInputType==Enum.UserInputType.MouseButton2 then Y:OnMouse2Down(n,R)elseif n.UserInputType==Enum.UserInputType.MouseButton3 then Y:OnMouse3Down(n,R)end if not FFlagPlayerScriptsBindAtPriority then if n.UserInputType==Enum.UserInputType.Keyboard then Y:OnKeyDown(n,R)end end end function BaseCamera.OnInputChanged(Y,n,R)if n.UserInputType==Enum.UserInputType.Touch then Y:OnTouchChanged(n,R)elseif n.UserInputType==Enum.UserInputType.MouseMovement then Y:OnMouseMoved(n,R)elseif n.UserInputType==Enum.UserInputType.MouseWheel then Y:OnMouseWheel(n,R)end end function BaseCamera.OnInputEnded(Y,n,R)if n.UserInputType==Enum.UserInputType.Touch then Y:OnTouchEnded(n,R)elseif n.UserInputType==Enum.UserInputType.MouseButton2 then Y:OnMouse2Up(n,R)elseif n.UserInputType==Enum.UserInputType.MouseButton3 then Y:OnMouse3Up(n,R)end if not FFlagPlayerScriptsBindAtPriority then if n.UserInputType==Enum.UserInputType.Keyboard then Y:OnKeyUp(n,R)end end end function BaseCamera.ConnectInputEvents(n)n.inputBeganConn=UserInputService.InputBegan:Connect(function(R,Y)n:OnInputBegan(R,Y)end)n.inputChangedConn=UserInputService.InputChanged:Connect(function(R,Y)n:OnInputChanged(R,Y)end)n.inputEndedConn=UserInputService.InputEnded:Connect(function(R,Y)n:OnInputEnded(R,Y)end)n.touchActivateConn=UserInputService.TouchTapInWorld:Connect(function(R,Y)n:OnTouchTap(R)end)n.menuOpenedConn=GuiService.MenuOpened:connect(function()n:ResetInputStates()end)n.gamepadConnectedConn=UserInputService.GamepadDisconnected:connect(function(R)if n.activeGamepad~=R then return end n.activeGamepad=nil n:AssignActivateGamepad()end)n.gamepadDisconnectedConn=UserInputService.GamepadConnected:connect(function(R)if n.activeGamepad==nil then n:AssignActivateGamepad()end end)if not FFlagPlayerScriptsBindAtPriority then n:BindGamepadInputActions()end n:AssignActivateGamepad()n:UpdateMouseBehavior()end function BaseCamera.BindContextActions(n)n:BindGamepadInputActions()n:BindKeyboardInputActions()end function BaseCamera.AssignActivateGamepad(n)local R=UserInputService:GetConnectedGamepads()if#R>0 then for Y=1,#R,1 do if n.activeGamepad==nil then n.activeGamepad=R[Y]elseif R[Y].Value<n.activeGamepad.Value then n.activeGamepad=R[Y]end end end if n.activeGamepad==nil then n.activeGamepad=Enum.UserInputType.Gamepad1 end end function BaseCamera.DisconnectInputEvents(n)if n.inputBeganConn then n.inputBeganConn:Disconnect()n.inputBeganConn=nil end if n.inputChangedConn then n.inputChangedConn:Disconnect()n.inputChangedConn=nil end if n.inputEndedConn then n.inputEndedConn:Disconnect()n.inputEndedConn=nil end end function BaseCamera.UnbindContextActions(n)for R=1,#n.boundContextActions,1 do ContextActionService:UnbindAction(n.boundContextActions[R])end n.boundContextActions={}end function BaseCamera.Cleanup(n)if n.menuOpenedConn then n.menuOpenedConn:Disconnect()n.menuOpenedConn=nil end if n.mouseLockToggleConn then n.mouseLockToggleConn:Disconnect()n.mouseLockToggleConn=nil end if n.gamepadConnectedConn then n.gamepadConnectedConn:Disconnect()n.gamepadConnectedConn=nil end if n.gamepadDisconnectedConn then n.gamepadDisconnectedConn:Disconnect()n.gamepadDisconnectedConn=nil end if n.subjectStateChangedConn then n.subjectStateChangedConn:Disconnect()n.subjectStateChangedConn=nil end if n.viewportSizeChangedConn then n.viewportSizeChangedConn:Disconnect()n.viewportSizeChangedConn=nil end if n.touchActivateConn then n.touchActivateConn:Disconnect()n.touchActivateConn=nil end n.turningLeft=false n.turningRight=false n.lastCameraTransform=nil n.lastSubjectCFrame=nil n.userPanningTheCamera=false n.rotateInput=Vector2.new()n.gamepadPanningCamera=Vector2.new(0,0)n.startPos=nil n.lastPos=nil n.panBeginLook=nil n.isRightMouseDown=false n.isMiddleMouseDown=false n.fingerTouches={}n.numUnsunkTouches=0 n.startingDiff=nil n.pinchBeginZoom=nil if UserInputService.MouseBehavior~=Enum.MouseBehavior.LockCenter then UserInputService.MouseBehavior=Enum.MouseBehavior.Default end end

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 Players.LocalPlayer.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice then
		if UserGameSettings.TouchMovementMode.Name == "DynamicThumbstick" then
			self:OnDynamicThumbstickEnabled()
		else
			self:OnDynamicThumbstickDisabled()
		end
	end
end

function BaseCamera:OnDevTouchMovementModeChanged()
	if Players.LocalPlayer.DevTouchMovementMode.Name == "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)
	local camera = game.Workspace.CurrentCamera
	if camera and camera.ViewportSize.X > 0 and camera.ViewportSize.Y > 0 and (camera.ViewportSize.Y > camera.ViewportSize.X) then
		-- Screen has portrait orientation, swap X and Y sensitivity
		return translationVector * Vector2.new( sensitivity.Y, sensitivity.X)
	end
	return translationVector * sensitivity
end

function BaseCamera:Enable(enable)
	if self.enabled ~= enable then
		self.enabled = enable
		if self.enabled then
			self:ConnectInputEvents()
			if FFlagPlayerScriptsBindAtPriority then
				self:BindContextActions()
			end

			if Players.LocalPlayer.CameraMode == Enum.CameraMode.LockFirstPerson then
				self.currentSubjectDistance = 0.5
				if not self.inFirstPerson then
					self:EnterFirstPerson()
				end
			end
		else
			self:DisconnectInputEvents()
			if FFlagPlayerScriptsBindAtPriority then
				self:UnbindContextActions()
			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:OnInputBegan(input, processed)
	if input.UserInputType == Enum.UserInputType.Touch then
		self:OnTouchBegan(input, processed)
	elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
		self:OnMouse2Down(input, processed)
	elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
		self:OnMouse3Down(input, processed)
	end
	-- Keyboard
	if not FFlagPlayerScriptsBindAtPriority then
		if input.UserInputType == Enum.UserInputType.Keyboard then
			self:OnKeyDown(input, processed)
		end
	end
end

function BaseCamera:OnInputChanged(input, processed)
	if input.UserInputType == Enum.UserInputType.Touch then
		self:OnTouchChanged(input, processed)
	elseif input.UserInputType == Enum.UserInputType.MouseMovement then
		self:OnMouseMoved(input, processed)
	elseif input.UserInputType == Enum.UserInputType.MouseWheel then
		self:OnMouseWheel(input, processed)
	end
end

function BaseCamera:OnInputEnded(input, processed)
	if input.UserInputType == Enum.UserInputType.Touch then
		self:OnTouchEnded(input, processed)
	elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
		self:OnMouse2Up(input, processed)
	elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
		self:OnMouse3Up(input, processed)
	end
	-- Keyboard
	if not FFlagPlayerScriptsBindAtPriority then
		if input.UserInputType == Enum.UserInputType.Keyboard then
			self:OnKeyUp(input, processed)
		end
	end
end

function BaseCamera:ConnectInputEvents()
	self.inputBeganConn = UserInputService.InputBegan:Connect(function(input, processed)
		self:OnInputBegan(input, processed)
	end)

	self.inputChangedConn = UserInputService.InputChanged:Connect(function(input, processed)
		self:OnInputChanged(input, processed)
	end)

	self.inputEndedConn = UserInputService.InputEnded:Connect(function(input, processed)
		self:OnInputEnded(input, processed)
	end)

	self.touchActivateConn = UserInputService.TouchTapInWorld:Connect(function(touchPos, processed)
		self:OnTouchTap(touchPos)
	end)

	self.menuOpenedConn = GuiService.MenuOpened:connect(function()
		self:ResetInputStates()
	end)

	self.gamepadConnectedConn = UserInputService.GamepadDisconnected:connect(function(gamepadEnum)
		if self.activeGamepad ~= gamepadEnum then return end
		self.activeGamepad = nil
		self:AssignActivateGamepad()
	end)

	self.gamepadDisconnectedConn = UserInputService.GamepadConnected:connect(function(gamepadEnum)
		if self.activeGamepad == nil then
			self:AssignActivateGamepad()
		end
	end)

	if not FFlagPlayerScriptsBindAtPriority then
		self:BindGamepadInputActions()
	end
	self:AssignActivateGamepad()
	self:UpdateMouseBehavior()
end

function BaseCamera:BindContextActions()
	self:BindGamepadInputActions()
	self:BindKeyboardInputActions()
end

function BaseCamera:AssignActivateGamepad()
	local connectedGamepads = UserInputService:GetConnectedGamepads()
	if #connectedGamepads > 0 then
		for i = 1, #connectedGamepads do
			if self.activeGamepad == nil then
				self.activeGamepad = connectedGamepads[i]
			elseif connectedGamepads[i].Value < self.activeGamepad.Value then
				self.activeGamepad = connectedGamepads[i]
			end
		end
	end

	if self.activeGamepad == nil then -- nothing is connected, at least set up for gamepad1
		self.activeGamepad = Enum.UserInputType.Gamepad1
	end
end

function BaseCamera:DisconnectInputEvents()
	if self.inputBeganConn then
		self.inputBeganConn:Disconnect()
		self.inputBeganConn = nil
	end
	if self.inputChangedConn then
		self.inputChangedConn:Disconnect()
		self.inputChangedConn = nil
	end
	if self.inputEndedConn then
		self.inputEndedConn:Disconnect()
		self.inputEndedConn = nil
	end
end

function BaseCamera:UnbindContextActions()
	for i = 1, #self.boundContextActions do
		ContextActionService:UnbindAction(self.boundContextActions[i])
	end 
	self.boundContextActions = {}
end

function BaseCamera:Cleanup()
	if self.menuOpenedConn then
		self.menuOpenedConn:Disconnect()
		self.menuOpenedConn = nil
	end
	if self.mouseLockToggleConn then
		self.mouseLockToggleConn:Disconnect()
		self.mouseLockToggleConn = nil
	end
	if self.gamepadConnectedConn then
		self.gamepadConnectedConn:Disconnect()
		self.gamepadConnectedConn = nil
	end
	if self.gamepadDisconnectedConn then
		self.gamepadDisconnectedConn:Disconnect()
		self.gamepadDisconnectedConn = nil
	end
	if self.subjectStateChangedConn then
		self.subjectStateChangedConn:Disconnect()
		self.subjectStateChangedConn = nil
	end
	if self.viewportSizeChangedConn then
		self.viewportSizeChangedConn:Disconnect()
		self.viewportSizeChangedConn = nil
	end
	if self.touchActivateConn then
		self.touchActivateConn:Disconnect()
		self.touchActivateConn = nil
	end

	self.turningLeft = false
	self.turningRight = false
	self.lastCameraTransform = nil
	self.lastSubjectCFrame = nil
	self.userPanningTheCamera = false
	self.rotateInput = Vector2.new()
	self.gamepadPanningCamera = Vector2.new(0,0)

	-- Reset input states
	self.startPos = nil
	self.lastPos = nil
	self.panBeginLook = nil
	self.isRightMouseDown = false
	self.isMiddleMouseDown = false

	self.fingerTouches = {}
	self.numUnsunkTouches = 0

	self.startingDiff = nil
	self.pinchBeginZoom = 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
