local B={}B.__index=B function B.new()local K=setmetatable({},B)K.FIRST_PERSON_DISTANCE_THRESHOLD=FIRST_PERSON_DISTANCE_THRESHOLD K.cameraType=nil K.cameraMovementMode=nil K.lastCameraTransform=nil K.lastUserPanCamera=tick()K.humanoidRootPart=nil K.humanoidCache={}K.lastSubject=nil K.lastSubjectPosition=Vector3.new(0,5,0)K.lastSubjectCFrame=CFrame.new(K.lastSubjectPosition)K.defaultSubjectDistance=math.clamp(DEFAULT_DISTANCE,player.CameraMinZoomDistance,player.CameraMaxZoomDistance)K.currentSubjectDistance=math.clamp(DEFAULT_DISTANCE,player.CameraMinZoomDistance,player.CameraMaxZoomDistance)K.inFirstPerson=false K.inMouseLockedMode=false K.portraitMode=false K.isSmallTouchScreen=false K.resetCameraAngle=true K.enabled=false K.PlayerGui=nil K.cameraChangedConn=nil K.viewportSizeChangedConn=nil K.shouldUseVRRotation=false K.VRRotationIntensityAvailable=false K.lastVRRotationIntensityCheckTime=0 K.lastVRRotationTime=0 K.vrRotateKeyCooldown={}K.cameraTranslationConstraints=Vector3.new(1,1,1)K.humanoidJumpOrigin=nil K.trackingHumanoid=nil K.cameraFrozen=false K.subjectStateChangedConn=nil K.gamepadZoomPressConnection=nil K.mouseLockOffset=ZERO_VECTOR3 if player.Character then K:OnCharacterAdded(player.Character)end player.CharacterAdded:Connect(function(B)K:OnCharacterAdded(B)end)if K.cameraChangedConn then K.cameraChangedConn:Disconnect()end K.cameraChangedConn=(workspace:GetPropertyChangedSignal("\067\117\114\114\101\110\116\067\097\109\101\114\097")):Connect(function()K:OnCurrentCameraChanged()end)K:OnCurrentCameraChanged()if K.playerCameraModeChangeConn then K.playerCameraModeChangeConn:Disconnect()end K.playerCameraModeChangeConn=(player:GetPropertyChangedSignal("\067\097\109\101\114\097\077\111\100\101")):Connect(function()K:OnPlayerCameraPropertyChange()end)if K.minDistanceChangeConn then K.minDistanceChangeConn:Disconnect()end K.minDistanceChangeConn=(player:GetPropertyChangedSignal("\067\097\109\101\114\097\077\105\110\090\111\111\109\068\105\115\116\097\110\099\101")):Connect(function()K:OnPlayerCameraPropertyChange()end)if K.maxDistanceChangeConn then K.maxDistanceChangeConn:Disconnect()end K.maxDistanceChangeConn=(player:GetPropertyChangedSignal("\067\097\109\101\114\097\077\097\120\090\111\111\109\068\105\115\116\097\110\099\101")):Connect(function()K:OnPlayerCameraPropertyChange()end)if K.playerDevTouchMoveModeChangeConn then K.playerDevTouchMoveModeChangeConn:Disconnect()end K.playerDevTouchMoveModeChangeConn=(player:GetPropertyChangedSignal("\068\101\118\084\111\117\099\104\077\111\118\101\109\101\110\116\077\111\100\101")):Connect(function()K:OnDevTouchMovementModeChanged()end)K:OnDevTouchMovementModeChanged()if K.gameSettingsTouchMoveMoveChangeConn then K.gameSettingsTouchMoveMoveChangeConn:Disconnect()end K.gameSettingsTouchMoveMoveChangeConn=(UserGameSettings:GetPropertyChangedSignal("\084\111\117\099\104\077\111\118\101\109\101\110\116\077\111\100\101")):Connect(function()K:OnGameSettingsTouchMovementModeChanged()end)K:OnGameSettingsTouchMovementModeChanged()UserGameSettings:SetCameraYInvertVisible()UserGameSettings:SetGamepadCameraSensitivityVisible()K.hasGameLoaded=game:IsLoaded()if not K.hasGameLoaded then K.gameLoadedConn=game.Loaded:Connect(function()K.hasGameLoaded=true K.gameLoadedConn:Disconnect()K.gameLoadedConn=nil end)end K:OnPlayerCameraPropertyChange()return K end function B.GetModuleName(B)return"\066\097\115\101\067\097\109\101\114\097"end function B.OnCharacterAdded(K,B)K.resetCameraAngle=K.resetCameraAngle or K:GetEnabled()K.humanoidRootPart=nil if UserInputService.TouchEnabled then K.PlayerGui=player:WaitForChild("\080\108\097\121\101\114\071\117\105")for B,o in ipairs(B:GetChildren())do if o:IsA("\084\111\111\108")then K.isAToolEquipped=true end end B.ChildAdded:Connect(function(B)if B:IsA("\084\111\111\108")then K.isAToolEquipped=true end end)B.ChildRemoved:Connect(function(B)if B:IsA("\084\111\111\108")then K.isAToolEquipped=false end end)end end function B.GetHumanoidRootPart(B)if not B.humanoidRootPart then if player.Character then local K=player.Character:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")if K then B.humanoidRootPart=K.RootPart end end end return B.humanoidRootPart end function B.GetBodyPartToFollow(o,B,K)if B:GetState()==Enum.HumanoidStateType.Dead then local K=B.Parent if K and K:IsA("\077\111\100\101\108")then return K:FindFirstChild("\072\101\097\100")or B.RootPart end end return B.RootPart end function B.GetSubjectCFrame(B)local K=B.lastSubjectCFrame local o=workspace.CurrentCamera local U=o and o.CameraSubject if not U then return K end if U:IsA("\072\117\109\097\110\111\105\100")then local o=U local m=o:GetState()==Enum.HumanoidStateType.Dead if VRService.VREnabled and(m and o==B.lastSubject)then K=B.lastSubjectCFrame else local B=o.RootPart if m then if o.Parent and o.Parent:IsA("\077\111\100\101\108")then B=o.Parent:FindFirstChild("\072\101\097\100")or B end end if B and B:IsA("\066\097\115\101\080\097\114\116")then local U if o.RigType==Enum.HumanoidRigType.R15 then if o.AutomaticScalingEnabled then U=R15_HEAD_OFFSET local K=o.RootPart if B==K then local B=(K.Size.Y-HUMANOID_ROOT_PART_SIZE.Y)/2 U=U+Vector3.new(0,B,0)end else U=R15_HEAD_OFFSET_NO_SCALING end else U=HEAD_OFFSET end if m then U=ZERO_VECTOR3 end K=B.CFrame*CFrame.new(U+o.CameraOffset)end end elseif U:IsA("\066\097\115\101\080\097\114\116")then K=U.CFrame elseif U:IsA("\077\111\100\101\108")then if U.PrimaryPart then K=U:GetPrimaryPartCFrame()else K=CFrame.new()end end if K then B.lastSubjectCFrame=K end return K end function B.GetSubjectVelocity(B)local K=workspace.CurrentCamera local o=K and K.CameraSubject if not o then return ZERO_VECTOR3 end if o:IsA("\066\097\115\101\080\097\114\116")then return o.Velocity elseif o:IsA("\072\117\109\097\110\111\105\100")then local B=o.RootPart if B then return B.Velocity end elseif o:IsA("\077\111\100\101\108")then local B=o.PrimaryPart if B then return B.Velocity end end return ZERO_VECTOR3 end function B.GetSubjectRotVelocity(B)local K=workspace.CurrentCamera local o=K and K.CameraSubject if not o then return ZERO_VECTOR3 end if o:IsA("\066\097\115\101\080\097\114\116")then return o.RotVelocity elseif o:IsA("\072\117\109\097\110\111\105\100")then local B=o.RootPart if B then return B.RotVelocity end elseif o:IsA("\077\111\100\101\108")then local B=o.PrimaryPart if B then return B.RotVelocity end end return ZERO_VECTOR3 end function B.StepZoom(B)local K=B.currentSubjectDistance local o=CameraInput.getZoomDelta()if math.abs(o)>0 then local U if o>0 then U=K+o*(1+K*ZOOM_SENSITIVITY_CURVATURE)U=math.max(U,B.FIRST_PERSON_DISTANCE_THRESHOLD)else U=(K+o)/(1-o*ZOOM_SENSITIVITY_CURVATURE)U=math.max(U,FIRST_PERSON_DISTANCE_MIN)end if U<B.FIRST_PERSON_DISTANCE_THRESHOLD then U=FIRST_PERSON_DISTANCE_MIN end B:SetCameraToSubjectDistance(U)end return ZoomController.GetZoomRadius()end function B.GetSubjectPosition(B)local K=B.lastSubjectPosition local o=game.Workspace.CurrentCamera local U=o and o.CameraSubject if U then if U:IsA("\072\117\109\097\110\111\105\100")then local o=U local m=o:GetState()==Enum.HumanoidStateType.Dead if VRService.VREnabled and(m and o==B.lastSubject)then K=B.lastSubjectPosition else local B=o.RootPart if m then if o.Parent and o.Parent:IsA("\077\111\100\101\108")then B=o.Parent:FindFirstChild("\072\101\097\100")or B end end if B and B:IsA("\066\097\115\101\080\097\114\116")then local U if o.RigType==Enum.HumanoidRigType.R15 then if o.AutomaticScalingEnabled then U=R15_HEAD_OFFSET if B==o.RootPart then local B=o.RootPart.Size.Y/2-HUMANOID_ROOT_PART_SIZE.Y/2 U=U+Vector3.new(0,B,0)end else U=R15_HEAD_OFFSET_NO_SCALING end else U=HEAD_OFFSET end if m then U=ZERO_VECTOR3 end K=B.CFrame.p+B.CFrame:vectorToWorldSpace(U+o.CameraOffset)end end elseif U:IsA("\086\101\104\105\099\108\101\083\101\097\116")then local B=SEAT_OFFSET if VRService.VREnabled then B=VR_SEAT_OFFSET end K=U.CFrame.p+U.CFrame:vectorToWorldSpace(B)elseif U:IsA("\083\107\097\116\101\098\111\097\114\100\080\108\097\116\102\111\114\109")then K=U.CFrame.p+SEAT_OFFSET elseif U:IsA("\066\097\115\101\080\097\114\116")then K=U.CFrame.p elseif U:IsA("\077\111\100\101\108")then if U.PrimaryPart then K=(U:GetPrimaryPartCFrame()).p else K=(U:GetModelCFrame()).p end end else return end B.lastSubject=U B.lastSubjectPosition=K return K end function B.UpdateDefaultSubjectDistance(B)if B.portraitMode then B.defaultSubjectDistance=math.clamp(PORTRAIT_DEFAULT_DISTANCE,player.CameraMinZoomDistance,player.CameraMaxZoomDistance)else B.defaultSubjectDistance=math.clamp(DEFAULT_DISTANCE,player.CameraMinZoomDistance,player.CameraMaxZoomDistance)end end function B.OnViewportSizeChanged(B)local K=game.Workspace.CurrentCamera local o=K.ViewportSize B.portraitMode=o.X<o.Y B.isSmallTouchScreen=UserInputService.TouchEnabled and(o.Y<500 or o.X<700)B:UpdateDefaultSubjectDistance()end
--
local BaseCamera = {}
BaseCamera.__index = BaseCamera

function BaseCamera.new()
	local self = setmetatable({}, BaseCamera)

	-- So that derived classes have access to this
	self.FIRST_PERSON_DISTANCE_THRESHOLD = FIRST_PERSON_DISTANCE_THRESHOLD

	self.cameraType = nil
	self.cameraMovementMode = nil

	self.lastCameraTransform = nil
	self.lastUserPanCamera = tick()

	self.humanoidRootPart = nil
	self.humanoidCache = {}

	-- Subject and position on last update call
	self.lastSubject = nil
	self.lastSubjectPosition = Vector3.new(0, 5, 0)
	self.lastSubjectCFrame = CFrame.new(self.lastSubjectPosition)

	-- These subject distance members refer to the nominal camera-to-subject follow distance that the camera
	-- is trying to maintain, not the actual measured value.
	-- The default is updated when screen orientation or the min/max distances change,
	-- to be sure the default is always in range and appropriate for the orientation.
	self.defaultSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
	self.currentSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)

	self.inFirstPerson = false
	self.inMouseLockedMode = false
	self.portraitMode = false
	self.isSmallTouchScreen = false

	-- Used by modules which want to reset the camera angle on respawn.
	self.resetCameraAngle = true

	self.enabled = false

	-- Input Event Connections

	self.PlayerGui = nil

	self.cameraChangedConn = nil
	self.viewportSizeChangedConn = nil

	-- VR Support
	self.shouldUseVRRotation = false
	self.VRRotationIntensityAvailable = false
	self.lastVRRotationIntensityCheckTime = 0
	self.lastVRRotationTime = 0
	self.vrRotateKeyCooldown = {}
	self.cameraTranslationConstraints = Vector3.new(1, 1, 1)
	self.humanoidJumpOrigin = nil
	self.trackingHumanoid = nil
	self.cameraFrozen = false
	self.subjectStateChangedConn = nil

	self.gamepadZoomPressConnection = nil

	-- Mouse locked formerly known as shift lock mode
	self.mouseLockOffset = ZERO_VECTOR3

	-- Initialization things used to always execute at game load time, but now these camera modules are instantiated
	-- when needed, so the code here may run well after the start of the game

	if player.Character then
		self:OnCharacterAdded(player.Character)
	end

	player.CharacterAdded:Connect(function(char)
		self:OnCharacterAdded(char)
	end)

	if self.cameraChangedConn then self.cameraChangedConn:Disconnect() end
	self.cameraChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
		self:OnCurrentCameraChanged()
	end)
	self:OnCurrentCameraChanged()

	if self.playerCameraModeChangeConn then self.playerCameraModeChangeConn:Disconnect() end
	self.playerCameraModeChangeConn = player:GetPropertyChangedSignal("CameraMode"):Connect(function()
		self:OnPlayerCameraPropertyChange()
	end)

	if self.minDistanceChangeConn then self.minDistanceChangeConn:Disconnect() end
	self.minDistanceChangeConn = player:GetPropertyChangedSignal("CameraMinZoomDistance"):Connect(function()
		self:OnPlayerCameraPropertyChange()
	end)

	if self.maxDistanceChangeConn then self.maxDistanceChangeConn:Disconnect() end
	self.maxDistanceChangeConn = player:GetPropertyChangedSignal("CameraMaxZoomDistance"):Connect(function()
		self:OnPlayerCameraPropertyChange()
	end)

	if self.playerDevTouchMoveModeChangeConn then self.playerDevTouchMoveModeChangeConn:Disconnect() end
	self.playerDevTouchMoveModeChangeConn = player:GetPropertyChangedSignal("DevTouchMovementMode"):Connect(function()
		self:OnDevTouchMovementModeChanged()
	end)
	self:OnDevTouchMovementModeChanged() -- Init

	if self.gameSettingsTouchMoveMoveChangeConn then self.gameSettingsTouchMoveMoveChangeConn:Disconnect() end
	self.gameSettingsTouchMoveMoveChangeConn = UserGameSettings:GetPropertyChangedSignal("TouchMovementMode"):Connect(function()
		self:OnGameSettingsTouchMovementModeChanged()
	end)
	self:OnGameSettingsTouchMovementModeChanged() -- Init

	UserGameSettings:SetCameraYInvertVisible()
	UserGameSettings:SetGamepadCameraSensitivityVisible()

	self.hasGameLoaded = game:IsLoaded()
	if not self.hasGameLoaded then
		self.gameLoadedConn = game.Loaded:Connect(function()
			self.hasGameLoaded = true
			self.gameLoadedConn:Disconnect()
			self.gameLoadedConn = nil
		end)
	end

	self:OnPlayerCameraPropertyChange()

	return self
end

function BaseCamera:GetModuleName()
	return "BaseCamera"
end

function BaseCamera:OnCharacterAdded(char)
	self.resetCameraAngle = self.resetCameraAngle or self:GetEnabled()
	self.humanoidRootPart = nil
	if UserInputService.TouchEnabled then
		self.PlayerGui = player:WaitForChild("PlayerGui")
		for _, child in ipairs(char:GetChildren()) do
			if child:IsA("Tool") then
				self.isAToolEquipped = true
			end
		end
		char.ChildAdded:Connect(function(child)
			if child:IsA("Tool") then
				self.isAToolEquipped = true
			end
		end)
		char.ChildRemoved:Connect(function(child)
			if child:IsA("Tool") then
				self.isAToolEquipped = false
			end
		end)
	end
end

function BaseCamera:GetHumanoidRootPart()
	if not self.humanoidRootPart then
		if player.Character then
			local humanoid = player.Character:FindFirstChildOfClass("Humanoid")
			if humanoid then
				self.humanoidRootPart = humanoid.RootPart
			end
		end
	end
	return self.humanoidRootPart
end

function BaseCamera:GetBodyPartToFollow(humanoid, isDead)
	-- If the humanoid is dead, prefer the head part if one still exists as a sibling of the humanoid
	if humanoid:GetState() == Enum.HumanoidStateType.Dead then
		local character = humanoid.Parent
		if character and character:IsA("Model") then
			return character:FindFirstChild("Head") or humanoid.RootPart
		end
	end

	return humanoid.RootPart
end

function BaseCamera:GetSubjectCFrame()
	local result = self.lastSubjectCFrame
	local camera = workspace.CurrentCamera
	local cameraSubject = camera and camera.CameraSubject

	if not cameraSubject then
		return result
	end

	if cameraSubject:IsA("Humanoid") then
		local humanoid = cameraSubject
		local humanoidIsDead = humanoid:GetState() == Enum.HumanoidStateType.Dead

		if VRService.VREnabled and humanoidIsDead and humanoid == self.lastSubject then
			result = self.lastSubjectCFrame
		else
			local bodyPartToFollow = humanoid.RootPart

			-- If the humanoid is dead, prefer their head part as a follow target, if it exists
			if humanoidIsDead then
				if humanoid.Parent and humanoid.Parent:IsA("Model") then
					bodyPartToFollow = humanoid.Parent:FindFirstChild("Head") or bodyPartToFollow
				end
			end

			if bodyPartToFollow and bodyPartToFollow:IsA("BasePart") then
				local heightOffset
				if humanoid.RigType == Enum.HumanoidRigType.R15 then
					if humanoid.AutomaticScalingEnabled then
						heightOffset = R15_HEAD_OFFSET

						local rootPart = humanoid.RootPart
						if bodyPartToFollow == rootPart then
							local rootPartSizeOffset = (rootPart.Size.Y - HUMANOID_ROOT_PART_SIZE.Y)/2
							heightOffset = heightOffset + Vector3.new(0, rootPartSizeOffset, 0)
						end
					else
						heightOffset = R15_HEAD_OFFSET_NO_SCALING
					end
				else
					heightOffset = HEAD_OFFSET
				end

				if humanoidIsDead then
					heightOffset = ZERO_VECTOR3
				end

				result = bodyPartToFollow.CFrame*CFrame.new(heightOffset + humanoid.CameraOffset)
			end
		end

	elseif cameraSubject:IsA("BasePart") then
		result = cameraSubject.CFrame

	elseif cameraSubject:IsA("Model") then
		-- Model subjects are expected to have a PrimaryPart to determine orientation
		if cameraSubject.PrimaryPart then
			result = cameraSubject:GetPrimaryPartCFrame()
		else
			result = CFrame.new()
		end
	end

	if result then
		self.lastSubjectCFrame = result
	end

	return result
end

function BaseCamera:GetSubjectVelocity()
	local camera = workspace.CurrentCamera
	local cameraSubject = camera and camera.CameraSubject

	if not cameraSubject then
		return ZERO_VECTOR3
	end

	if cameraSubject:IsA("BasePart") then
		return cameraSubject.Velocity

	elseif cameraSubject:IsA("Humanoid") then
		local rootPart = cameraSubject.RootPart

		if rootPart then
			return rootPart.Velocity
		end

	elseif cameraSubject:IsA("Model") then
		local primaryPart = cameraSubject.PrimaryPart

		if primaryPart then
			return primaryPart.Velocity
		end
	end

	return ZERO_VECTOR3
end

function BaseCamera:GetSubjectRotVelocity()
	local camera = workspace.CurrentCamera
	local cameraSubject = camera and camera.CameraSubject

	if not cameraSubject then
		return ZERO_VECTOR3
	end

	if cameraSubject:IsA("BasePart") then
		return cameraSubject.RotVelocity

	elseif cameraSubject:IsA("Humanoid") then
		local rootPart = cameraSubject.RootPart

		if rootPart then
			return rootPart.RotVelocity
		end

	elseif cameraSubject:IsA("Model") then
		local primaryPart = cameraSubject.PrimaryPart

		if primaryPart then
			return primaryPart.RotVelocity
		end
	end

	return ZERO_VECTOR3
end

function BaseCamera:StepZoom()
	local zoom = self.currentSubjectDistance
	local zoomDelta = CameraInput.getZoomDelta()

	if math.abs(zoomDelta) > 0 then
		local newZoom

		if zoomDelta > 0 then
			newZoom = zoom + zoomDelta*(1 + zoom*ZOOM_SENSITIVITY_CURVATURE)
			newZoom = math.max(newZoom, self.FIRST_PERSON_DISTANCE_THRESHOLD)
		else
			newZoom = (zoom + zoomDelta)/(1 - zoomDelta*ZOOM_SENSITIVITY_CURVATURE)
			newZoom = math.max(newZoom, FIRST_PERSON_DISTANCE_MIN)
		end

		if newZoom < self.FIRST_PERSON_DISTANCE_THRESHOLD then
			newZoom = FIRST_PERSON_DISTANCE_MIN
		end

		self:SetCameraToSubjectDistance(newZoom)
	end
	
	return ZoomController.GetZoomRadius()
end

function BaseCamera:GetSubjectPosition()
	local result = self.lastSubjectPosition
	local camera = game.Workspace.CurrentCamera
	local cameraSubject = camera and camera.CameraSubject

	if cameraSubject then
		if cameraSubject:IsA("Humanoid") then
			local humanoid = cameraSubject
			local humanoidIsDead = humanoid:GetState() == Enum.HumanoidStateType.Dead

			if VRService.VREnabled and humanoidIsDead and humanoid == self.lastSubject then
				result = self.lastSubjectPosition
			else
				local bodyPartToFollow = humanoid.RootPart

				-- If the humanoid is dead, prefer their head part as a follow target, if it exists
				if humanoidIsDead then
					if humanoid.Parent and humanoid.Parent:IsA("Model") then
						bodyPartToFollow = humanoid.Parent:FindFirstChild("Head") or bodyPartToFollow
					end
				end

				if bodyPartToFollow and bodyPartToFollow:IsA("BasePart") then
					local heightOffset
					if humanoid.RigType == Enum.HumanoidRigType.R15 then
						if humanoid.AutomaticScalingEnabled then
							heightOffset = R15_HEAD_OFFSET
							if bodyPartToFollow == humanoid.RootPart then
								local rootPartSizeOffset = (humanoid.RootPart.Size.Y/2) - (HUMANOID_ROOT_PART_SIZE.Y/2)
								heightOffset = heightOffset + Vector3.new(0, rootPartSizeOffset, 0)
							end
						else
							heightOffset = R15_HEAD_OFFSET_NO_SCALING
						end
					else
						heightOffset = HEAD_OFFSET
					end

					if humanoidIsDead then
						heightOffset = ZERO_VECTOR3
					end

					result = bodyPartToFollow.CFrame.p + bodyPartToFollow.CFrame:vectorToWorldSpace(heightOffset + humanoid.CameraOffset)
				end
			end

		elseif cameraSubject:IsA("VehicleSeat") then
			local offset = SEAT_OFFSET
			if VRService.VREnabled then
				offset = VR_SEAT_OFFSET
			end
			result = cameraSubject.CFrame.p + cameraSubject.CFrame:vectorToWorldSpace(offset)
		elseif cameraSubject:IsA("SkateboardPlatform") then
			result = cameraSubject.CFrame.p + SEAT_OFFSET
		elseif cameraSubject:IsA("BasePart") then
			result = cameraSubject.CFrame.p
		elseif cameraSubject:IsA("Model") then
			if cameraSubject.PrimaryPart then
				result = cameraSubject:GetPrimaryPartCFrame().p
			else
				result = cameraSubject:GetModelCFrame().p
			end
		end
	else
		-- cameraSubject is nil
		-- Note: Previous RootCamera did not have this else case and let self.lastSubject and self.lastSubjectPosition
		-- both get set to nil in the case of cameraSubject being nil. This function now exits here to preserve the
		-- last set valid values for these, as nil values are not handled cases
		return
	end

	self.lastSubject = cameraSubject
	self.lastSubjectPosition = result

	return result
end

function BaseCamera:UpdateDefaultSubjectDistance()
	if self.portraitMode then
		self.defaultSubjectDistance = math.clamp(PORTRAIT_DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
	else
		self.defaultSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
	end
end

function BaseCamera:OnViewportSizeChanged()
	local camera = game.Workspace.CurrentCamera
	local size = camera.ViewportSize
	self.portraitMode = size.X < size.Y
	self.isSmallTouchScreen = UserInputService.TouchEnabled and (size.Y < 500 or size.X < 700)

	self:UpdateDefaultSubjectDistance()
end
