Prompt: -- Setup gesture area that camera uses while DynamicThumbstick is enabled

Response:
local function OnCharacterAdded(character)
	if UserInputService.TouchEnabled then
		if PlayerGui then
			local ScreenGui = Instance.new("ScreenGui")
			ScreenGui.Name = "GestureArea"
			ScreenGui.Parent = PlayerGui

			GestureArea = Instance.new("Frame")
			GestureArea.BackgroundTransparency = 1.0
			GestureArea.Visible = true
			GestureArea.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
			layoutGestureArea(PortraitMode)
			GestureArea.Parent = ScreenGui

		end
		for _, child in ipairs(LocalPlayer.Character:GetChildren()) do
			if child:IsA("Tool") then
				IsAToolEquipped = true
			end
		end
		character.ChildAdded:Connect(function(child)
			if child:IsA("Tool") then
				IsAToolEquipped = true
			end
		end)
		character.ChildRemoved:Connect(function(child)
			if child:IsA("Tool") then
				IsAToolEquipped = false
			end
		end)
	end
end

if LocalPlayer then
	if LocalPlayer.Character ~= nil then
		OnCharacterAdded(LocalPlayer.Character)
	end
	LocalPlayer.CharacterAdded:connect(function(character)
		OnCharacterAdded(character)
	end)
end

local function positionIntersectsGuiObject(position, guiObject)
	if position.X < guiObject.AbsolutePosition.X + guiObject.AbsoluteSize.X
		and position.X > guiObject.AbsolutePosition.X
		and position.Y < guiObject.AbsolutePosition.Y + guiObject.AbsoluteSize.Y
		and position.Y > guiObject.AbsolutePosition.Y then
		return true
	end
	return false
end

local function GetRenderCFrame(part)
	return part:GetRenderCFrame()
end

local function CreateCamera()
	local this = {}
	local R15HeadHeight = R15_HEAD_OFFSET
	function this:GetActivateValue()
		return 0.7
	end

	function this:IsPortraitMode()
		return PortraitMode
	end

	function this:GetRotateAmountValue(vrRotationIntensity)
		vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
		if vrRotationIntensity then
			if vrRotationIntensity == "Low" then
				return VR_LOW_INTENSITY_ROTATION
			elseif vrRotationIntensity == "High" then
				return VR_HIGH_INTENSITY_ROTATION
			end
		end
		return ZERO_VECTOR2
	end
	function this:GetRepeatDelayValue(vrRotationIntensity)
		vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
		if vrRotationIntensity then
			if vrRotationIntensity == "Low" then
				return VR_LOW_INTENSITY_REPEAT
			elseif vrRotationIntensity == "High" then
				return VR_HIGH_INTENSITY_REPEAT
			end
		end
		return 0
	end

	this.ShiftLock = false
	this.Enabled = false
	local isFirstPerson = false
	local isRightMouseDown = false
	local isMiddleMouseDown = false
	this.RotateInput = ZERO_VECTOR2
	this.DefaultZoom = LANDSCAPE_DEFAULT_ZOOM
	this.activeGamepad = nil

	local tweens = {}

	this.lastSubject = nil
	this.lastSubjectPosition = Vector3.new(0, 5, 0)

	local lastVRRotation = 0
	local vrRotateKeyCooldown = {}

	local isDynamicThumbstickEnabled = false

	-- Check for changes in ViewportSize to decide if PortraitMode
	local CameraChangedConn = nil
	local workspaceCameraChangedConn = nil
	local function onWorkspaceCameraChanged()
		if UserInputService.TouchEnabled then
			if CameraChangedConn then
				CameraChangedConn:Disconnect()
				CameraChangedConn = nil
			end
			local newCamera = workspace.CurrentCamera
			if newCamera then
				local size = newCamera.ViewportSize
				PortraitMode = size.X < size.Y
				layoutGestureArea(PortraitMode)
				DefaultZoom = PortraitMode and PORTRAIT_DEFAULT_ZOOM or LANDSCAPE_DEFAULT_ZOOM
				CameraChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
					size = newCamera.ViewportSize
					PortraitMode = size.X < size.Y
					layoutGestureArea(PortraitMode)
					DefaultZoom = PortraitMode and PORTRAIT_DEFAULT_ZOOM or LANDSCAPE_DEFAULT_ZOOM
				end)
			end
		end
	end
	workspaceCameraChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(onWorkspaceCameraChanged)
	if workspace.CurrentCamera then
		onWorkspaceCameraChanged()
	end

	function this:GetShiftLock()
		return ShiftLockController:IsShiftLocked()
	end

	function this:GetHumanoid()
		local player = PlayersService.LocalPlayer
		return findPlayerHumanoid(player)
	end

	function this:GetHumanoidRootPart()
		local humanoid = this:GetHumanoid()
		return humanoid and humanoid.Torso
	end

	function this:GetRenderCFrame(part)
		GetRenderCFrame(part)
	end

	local STATE_DEAD = Enum.HumanoidStateType.Dead

	-- HumanoidRootPart when alive, Head part when dead
	local function getHumanoidPartToFollow(humanoid, humanoidStateType)
		if humanoidStateType == STATE_DEAD then
			local character = humanoid.Parent
			if character then
				return character:FindFirstChild("Head") or humanoid.Torso
			else
				return humanoid.Torso
			end
		else
			return humanoid.Torso
		end
	end

	local HUMANOID_STATE_DEAD = Enum.HumanoidStateType.Dead

	function this:GetSubjectPosition()
		local result = nil
		local camera = workspace.CurrentCamera
		local cameraSubject = camera and camera.CameraSubject
		if cameraSubject then
			if cameraSubject:IsA('Humanoid') then
				local humanoidStateType = cameraSubject:GetState()
				if VRService.VREnabled and humanoidStateType == STATE_DEAD and cameraSubject == this.lastSubject then
					result = this.lastSubjectPosition
				else
					local humanoidRootPart = getHumanoidPartToFollow(cameraSubject, humanoidStateType)
					if humanoidRootPart and humanoidRootPart:IsA('BasePart') then
						local subjectCFrame = GetRenderCFrame(humanoidRootPart)
						local heightOffset = ZERO_VECTOR3
						if humanoidStateType ~= STATE_DEAD then
							heightOffset = cameraSubject.RigType == Enum.HumanoidRigType.R15 and R15HeadHeight or HEAD_OFFSET
						end

						result = subjectCFrame.p +
							subjectCFrame:vectorToWorldSpace(heightOffset + cameraSubject.CameraOffset)
					end
				end
			elseif cameraSubject:IsA('VehicleSeat') then
				local subjectCFrame = GetRenderCFrame(cameraSubject)
				local offset = SEAT_OFFSET
				if VRService.VREnabled then
					offset = VR_SEAT_OFFSET
				end
				result = subjectCFrame.p + subjectCFrame:vectorToWorldSpace(offset)
			elseif cameraSubject:IsA('SkateboardPlatform') then
				local subjectCFrame = GetRenderCFrame(cameraSubject)
				result = subjectCFrame.p + SEAT_OFFSET
			elseif cameraSubject:IsA('BasePart') then
				local subjectCFrame = GetRenderCFrame(cameraSubject)
				result = subjectCFrame.p
			elseif cameraSubject:IsA('Model') then
				result = cameraSubject:GetModelCFrame().p
			end
		end
		this.lastSubject = cameraSubject
		this.lastSubjectPosition = result
		return result
	end

	function this:ResetCameraLook()
	end

	function this:GetCameraLook()
		return workspace.CurrentCamera and workspace.CurrentCamera.CoordinateFrame.lookVector or Vector3.new(0,0,1)
	end

	function this:GetCameraZoom()
		if this.currentZoom == nil then
			local player = PlayersService.LocalPlayer
			this.currentZoom = player and clamp(player.CameraMinZoomDistance, player.CameraMaxZoomDistance, this.DefaultZoom) or this.DefaultZoom
		end
		return this.currentZoom
	end

	function this:GetCameraActualZoom()
		local camera = workspace.CurrentCamera
		if camera then
			return (camera.CoordinateFrame.p - camera.Focus.p).magnitude
		end
	end

	function this:GetCameraHeight()
		if VRService.VREnabled and not this:IsInFirstPerson() then
			local zoom = this:GetCameraZoom()
			return math.sin(VR_ANGLE) * zoom
		end
		return 0
	end

	function this:ViewSizeX()
		local result = 1024
		local camera = workspace.CurrentCamera
		if camera then
			result = camera.ViewportSize.X
		end
		return result
	end

	function this:ViewSizeY()
		local result = 768
		local camera = workspace.CurrentCamera
		if camera then
			result = camera.ViewportSize.Y
		end
		return result
	end

	local math_asin = math.asin
	local math_atan2 = math.atan2
	local math_floor = math.floor
	local math_max = math.max
	local math_pi = math.pi
	local Vector2_new = Vector2.new
	local Vector3_new = Vector3.new
	local CFrame_Angles = CFrame.Angles
	local CFrame_new = CFrame.new

	function this:ScreenTranslationToAngle(translationVector)
		local screenX = this:ViewSizeX()
		local screenY = this:ViewSizeY()
		local xTheta = (translationVector.x / screenX)
		local yTheta = (translationVector.y / screenY)
		return Vector2_new(xTheta, yTheta)
	end

	function this:MouseTranslationToAngle(translationVector)
		local xTheta = (translationVector.x / 1920)
		local yTheta = (translationVector.y / 1200)
		return Vector2_new(xTheta, yTheta)
	end

	function this:RotateVector(startVector, xyRotateVector)
		local startCFrame = CFrame_new(ZERO_VECTOR3, startVector)
		local resultLookVector = (CFrame_Angles(0, -xyRotateVector.x, 0) * startCFrame * CFrame_Angles(-xyRotateVector.y,0,0)).lookVector
		return resultLookVector, Vector2_new(xyRotateVector.x, xyRotateVector.y)
	end

	function this:RotateCamera(startLook, xyRotateVector)
		if VRService.VREnabled then
			local yawRotatedVector, xyRotateVector = self:RotateVector(startLook, Vector2.new(xyRotateVector.x, 0))
			return Vector3_new(yawRotatedVector.x, 0, yawRotatedVector.z).unit, xyRotateVector
		else
			local startVertical = math_asin(startLook.y)
			local yTheta = clamp(-MAX_Y + startVertical, -MIN_Y + startVertical, xyRotateVector.y)
			return self:RotateVector(startLook, Vector2_new(xyRotateVector.x, yTheta))
		end
	end

	function this:IsInFirstPerson()
		return isFirstPerson
	end

	-- there are several cases to consider based on the state of input and camera rotation mode
	function this:UpdateMouseBehavior()
		-- first time transition to first person mode or shiftlock
		if isFirstPerson or self:GetShiftLock() then
			pcall(function() GameSettings.RotationType = Enum.RotationType.CameraRelative end)
			if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
				UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
			end
		else
			pcall(function() GameSettings.RotationType = Enum.RotationType.MovementRelative end)
			if isRightMouseDown or isMiddleMouseDown then
				UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
			else
				UserInputService.MouseBehavior = Enum.MouseBehavior.Default
			end
		end
	end

	function this:ZoomCamera(desiredZoom)
		local player = PlayersService.LocalPlayer
		if player then
			if player.CameraMode == Enum.CameraMode.LockFirstPerson then
				this.currentZoom = 0
			else
				this.currentZoom = clamp(player.CameraMinZoomDistance, player.CameraMaxZoomDistance, desiredZoom)
			end
		end

		isFirstPerson = self:GetCameraZoom() < 2

		ShiftLockController:SetIsInFirstPerson(isFirstPerson)
		-- set mouse behavior
		self:UpdateMouseBehavior()
		return self:GetCameraZoom()
	end

	function this:rk4Integrator(position, velocity, t)
		local direction = velocity < 0 and -1 or 1
		local function acceleration(p, v)
			local accel = direction * math_max(1, (p / 3.3) + 0.5)
			return accel
		end

		local p1 = position
		local v1 = velocity
		local a1 = acceleration(p1, v1)
		local p2 = p1 + v1 * (t / 2)
		local v2 = v1 + a1 * (t / 2)
		local a2 = acceleration(p2, v2)
		local p3 = p1 + v2 * (t / 2)
		local v3 = v1 + a2 * (t / 2)
		local a3 = acceleration(p3, v3)
		local p4 = p1 + v3 * t
		local v4 = v1 + a3 * t
		local a4 = acceleration(p4, v4)

		local positionResult = position + (v1 + 2 * v2 + 2 * v3 + v4) * (t / 6)
		local velocityResult = velocity + (a1 + 2 * a2 + 2 * a3 + a4) * (t / 6)
		return positionResult, velocityResult
	end

	function this:ZoomCameraBy(zoomScale)
		local zoom = this:GetCameraActualZoom()

		if zoom then
			-- Can break into more steps to get more accurate integration
            zoom = self:rk4Integrator(zoom, zoomScale, 1)
			self:ZoomCamera(zoom)
		end
		return self:GetCameraZoom()
	end

	function this:ZoomCameraFixedBy(zoomIncrement)
		return self:ZoomCamera(self:GetCameraZoom() + zoomIncrement)
	end

	function this:Update()
	end

	----- VR STUFF ------

	function this:ApplyVRTransform()
		if not VRService.VREnabled then
			return
		end
		--we only want this to happen in first person VR
		local player = PlayersService.LocalPlayer
		if not (player and player.Character
				and player.Character:FindFirstChild("HumanoidRootPart")
				and player.Character.HumanoidRootPart:FindFirstChild("RootJoint")) then
			return
		end

		local camera = workspace.CurrentCamera
		local cameraSubject = camera.CameraSubject
		local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')

		if this:IsInFirstPerson() and not isInVehicle then
			local vrFrame = VRService:GetUserCFrame(Enum.UserCFrame.Head)
			local vrRotation = vrFrame - vrFrame.p
			local rootJoint = player.Character.HumanoidRootPart.RootJoint
			rootJoint.C0 = CFrame.new(vrRotation:vectorToObjectSpace(vrFrame.p)) * CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
		else
			local rootJoint = player.Character.HumanoidRootPart.RootJoint
			rootJoint.C0 = CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
		end
	end

	local vrRotationIntensityExists = true
	local lastVrRotationCheck = 0
	function this:ShouldUseVRRotation()
		if not VRService.VREnabled then
			return false
		end
		if not vrRotationIntensityExists and tick() - lastVrRotationCheck < 1 then return false end

		local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)
		vrRotationIntensityExists = success and vrRotationIntensity ~= nil
		lastVrRotationCheck = tick()

		return success and vrRotationIntensity ~= nil and vrRotationIntensity ~= "Smooth"
	end

	function this:GetVRRotationInput()
		local vrRotateSum = ZERO_VECTOR2

		local vrRotationIntensity = StarterGui:GetCore("VRRotationIntensity")

		local vrGamepadRotation = self.GamepadPanningCamera or ZERO_VECTOR2
		local delayExpired = (tick() - lastVRRotation) >= self:GetRepeatDelayValue(vrRotationIntensity)

		if math.abs(vrGamepadRotation.x) >= self:GetActivateValue() then
			if (delayExpired or not vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2]) then
				local sign = 1
				if vrGamepadRotation.x < 0 then
					sign = -1
				end
				vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity) * sign
				vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = true
			end
		elseif math.abs(vrGamepadRotation.x) < self:GetActivateValue() - 0.1 then
			vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = nil
		end
		if self.TurningLeft then
			if delayExpired or not vrRotateKeyCooldown[Enum.KeyCode.Left] then
				vrRotateSum = vrRotateSum - self:GetRotateAmountValue(vrRotationIntensity)
				vrRotateKeyCooldown[Enum.KeyCode.Left] = true
			end
		else
			vrRotateKeyCooldown[Enum.KeyCode.Left] = nil
		end
		if self.TurningRight then
			if (delayExpired or not vrRotateKeyCooldown[Enum.KeyCode.Right]) then
				vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity)
				vrRotateKeyCooldown[Enum.KeyCode.Right] = true
			end
		else
			vrRotateKeyCooldown[Enum.KeyCode.Right] = nil
		end

		if vrRotateSum ~= ZERO_VECTOR2 then
			lastVRRotation = tick()
		end

		return vrRotateSum
	end

	local cameraTranslationConstraints = Vector3.new(1, 1, 1)
	local humanoidJumpOrigin = nil
	local trackingHumanoid = nil
	local cameraFrozen = false
	local subjectStateChangedConn = nil
	local cameraSubjectChangedConn = nil
	local workspaceChangedConn = nil
	local humanoidChildAddedConn = nil
	local humanoidChildRemovedConn = nil

	local function cancelCameraFreeze(keepConstraints)
		if not keepConstraints then
			cameraTranslationConstraints = Vector3.new(cameraTranslationConstraints.x, 1, cameraTranslationConstraints.z)
		end
		if cameraFrozen then
			trackingHumanoid = nil
			cameraFrozen = false
		end
	end

	local function startCameraFreeze(subjectPosition, humanoidToTrack)
		if not cameraFrozen then
			humanoidJumpOrigin = subjectPosition
			trackingHumanoid = humanoidToTrack
			cameraTranslationConstraints = Vector3.new(cameraTranslationConstraints.x, 0, cameraTranslationConstraints.z)
			cameraFrozen = true
		end
	end

	local function rescaleCameraOffset(newScaleFactor)
		R15HeadHeight = R15_HEAD_OFFSET*newScaleFactor
	end

	local function onHumanoidSubjectChildAdded(child)
		if child.Name == "BodyHeightScale" and child:IsA("NumberValue") then
			if heightScaleChangedConn then
				heightScaleChangedConn:disconnect()
			end
			heightScaleChangedConn = child.Changed:connect(rescaleCameraOffset)
			rescaleCameraOffset(child.Value)
		end
	end

	local function onHumanoidSubjectChildRemoved(child)
		if child.Name == "BodyHeightScale" then
			rescaleCameraOffset(1)
			if heightScaleChangedConn then
				heightScaleChangedConn:disconnect()
				heightScaleChangedConn = nil
			end
		end
	end

	local function onNewCameraSubject()
		if subjectStateChangedConn then
			subjectStateChangedConn:disconnect()
			subjectStateChangedConn = nil
		end
		if humanoidChildAddedConn then
			humanoidChildAddedConn:disconnect()
			humanoidChildAddedConn = nil
		end
		if humanoidChildRemovedConn then
			humanoidChildRemovedConn:disconnect()
			humanoidChildRemovedConn = nil
		end
		if heightScaleChangedConn then
			heightScaleChangedConn:disconnect()
			heightScaleChangedConn = nil
		end

		local humanoid = workspace.CurrentCamera and workspace.CurrentCamera.CameraSubject
		if trackingHumanoid ~= humanoid then
			cancelCameraFreeze()
		end
		if humanoid and humanoid:IsA('Humanoid') then
			humanoidChildAddedConn = humanoid.ChildAdded:connect(onHumanoidSubjectChildAdded)
			humanoidChildRemovedConn = humanoid.ChildRemoved:connect(onHumanoidSubjectChildRemoved)
			for _, child in pairs(humanoid:GetChildren()) do
				onHumanoidSubjectChildAdded(child)
			end

			subjectStateChangedConn = humanoid.StateChanged:connect(function(oldState, newState)
				if VRService.VREnabled and newState == Enum.HumanoidStateType.Jumping and not this:IsInFirstPerson() then
					startCameraFreeze(this:GetSubjectPosition(), humanoid)
				elseif newState ~= Enum.HumanoidStateType.Jumping and newState ~= Enum.HumanoidStateType.Freefall then
					cancelCameraFreeze(true)
				end
			end)
		end
	end

 	local function onCurrentCameraChanged()
		if cameraSubjectChangedConn then
			cameraSubjectChangedConn:disconnect()
			cameraSubjectChangedConn = nil
		end
  		local camera = workspace.CurrentCamera
  		if camera then
			cameraSubjectChangedConn = camera:GetPropertyChangedSignal("CameraSubject"):connect(onNewCameraSubject)
			onNewCameraSubject()
  		end
  	end

	function this:GetVRFocus(subjectPosition, timeDelta)
		local newFocus = nil

		local camera = workspace.CurrentCamera
		local lastFocus = self.LastCameraFocus or subjectPosition
		if not cameraFrozen then
			cameraTranslationConstraints = Vector3.new(cameraTranslationConstraints.x, math.min(1, cameraTranslationConstraints.y + 0.42 * timeDelta), cameraTranslationConstraints.z)
		end
		if cameraFrozen and humanoidJumpOrigin and humanoidJumpOrigin.y > lastFocus.y then
			newFocus = CFrame.new(Vector3.new(subjectPosition.x, math.min(humanoidJumpOrigin.y, lastFocus.y + 5 * timeDelta), subjectPosition.z))
		else
			newFocus = CFrame.new(Vector3.new(subjectPosition.x, lastFocus.y, subjectPosition.z):lerp(subjectPosition, cameraTranslationConstraints.y))
		end

		if cameraFrozen then
			-- No longer in 3rd person
			if self:IsInFirstPerson() then -- not VRService.VREnabled
				cancelCameraFreeze()
			end
			-- This case you jumped off a cliff and want to keep your character in view
			-- 0.5 is to fix floating point error when not jumping off cliffs
			if humanoidJumpOrigin and subjectPosition.y < (humanoidJumpOrigin.y - 0.5) then
				cancelCameraFreeze()
			end
		end

		return newFocus
	end

	------------------------

	---- Input Events ----
	local startPos = nil
	local lastPos = nil
	local panBeginLook = nil
	local lastTapTime = nil

	local fingerTouches = {}
	local NumUnsunkTouches = 0

	local inputStartPositions = {}
	local inputStartTimes = {}

	local StartingDiff = nil
	local pinchBeginZoom = nil

	this.ZoomEnabled = true
	this.PanEnabled = true
	this.KeyPanEnabled = true

	local function OnTouchBegan(input, processed)
		--If isDynamicThumbstickEnabled, then only process TouchBegan event if it starts in GestureArea
		if (not touchWorkspaceEventEnabled and not isDynamicThumbstickEnabled) or positionIntersectsGuiObject(input.Position, GestureArea) then
			fingerTouches[input] = processed
			if not processed then
				inputStartPositions[input] = input.Position
				inputStartTimes[input] = tick()
				NumUnsunkTouches = NumUnsunkTouches + 1
			end
		end
	end

	local function OnTouchChanged(input, processed)

		if fingerTouches[input] == nil then
			if isDynamicThumbstickEnabled then
				return
			end
			fingerTouches[input] = processed
			if not processed then
				NumUnsunkTouches = NumUnsunkTouches + 1
			end
		end

		if NumUnsunkTouches == 1 then
			if fingerTouches[input] == false then
				panBeginLook = panBeginLook or this:GetCameraLook()
				startPos = startPos or input.Position
				lastPos = lastPos or startPos
				this.UserPanningTheCamera = true

				local delta = input.Position - lastPos

				delta = Vector2.new(delta.X, delta.Y * GameSettings:GetCameraYInvertValue())

				if this.PanEnabled then
					local desiredXYVector = this:ScreenTranslationToAngle(delta) * TOUCH_SENSITIVTY
					this.RotateInput = this.RotateInput + desiredXYVector
				end

				lastPos = input.Position
			end
		else
			panBeginLook = nil
			startPos = nil
			lastPos = nil
			this.UserPanningTheCamera = false
		end
		if NumUnsunkTouches == 2 then
			local unsunkTouches = {}
			for touch, wasSunk in pairs(fingerTouches) do
				if not wasSunk then
					table.insert(unsunkTouches, touch)
				end
			end
			if #unsunkTouches == 2 then
				local difference = (unsunkTouches[1].Position - unsunkTouches[2].Position).magnitude
				if StartingDiff and pinchBeginZoom then
					local scale = difference / math_max(0.01, StartingDiff)
					local clampedScale = clamp(0.1, 10, scale)
					if this.ZoomEnabled then
						this:ZoomCamera(pinchBeginZoom / clampedScale)
					end
				else
					StartingDiff = difference
					pinchBeginZoom = this:GetCameraActualZoom()
				end
			end
		else
			StartingDiff = nil
			pinchBeginZoom = nil
		end
	end

	local function calcLookBehindRotateInput(torso)
		if torso then
			local newDesiredLook = (torso.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
			local horizontalShift = findAngleBetweenXZVectors(newDesiredLook, this:GetCameraLook())
			local vertShift = math.asin(this:GetCameraLook().y) - math.asin(newDesiredLook.y)
			if not IsFinite(horizontalShift) then
				horizontalShift = 0
			end
			if not IsFinite(vertShift) then
				vertShift = 0
			end

			return Vector2.new(horizontalShift, vertShift)
		end
		return nil
	end

	local OnTouchTap = nil
	if not touchWorkspaceEventEnabled then
		OnTouchTap = function(position)
			if isDynamicThumbstickEnabled and not IsAToolEquipped then
				if lastTapTime and tick() - lastTapTime < MAX_TIME_FOR_DOUBLE_TAP then
					local tween = {
						from = this:GetCameraZoom(),
						to = DefaultZoom,
						start = tick(),
						duration = 0.2,
						func = function(from, to, alpha)
							this:ZoomCamera(from + (to - from)*alpha)
							return to
						end
					}
					tweens["Zoom"] = tween
				else
					local humanoid = this:GetHumanoid()
					if humanoid then
						local player = PlayersService.LocalPlayer
						if player and player.Character then
							if humanoid and humanoid.Torso then
								local tween = {
									from = this.RotateInput,
									to = calcLookBehindRotateInput(humanoid.Torso),
									start = tick(),
									duration = 0.2,
									func = function(from, to, alpha)
										to = calcLookBehindRotateInput(humanoid.Torso)
										if to then
											this.RotateInput = from + (to - from)*alpha
										end
										return to
									end
								}
								tweens["Rotate"] = tween

								-- reset old camera info so follow cam doesn't rotate us
								this.LastCameraTransform = nil
							end
						end
					end
				end
				lastTapTime = tick()
			end
		end
	end

	local function IsTouchTap(input)
		-- We can't make the assumption that the input exists in the inputStartPositions because we may have switched from a different camera type.
		if inputStartPositions[input] then
			local posDelta = (inputStartPositions[input] - input.Position).magnitude
			if posDelta < MAX_TAP_POS_DELTA then
				local timeDelta = inputStartTimes[input] - tick()
				if timeDelta < MAX_TAP_TIME_DELTA then
					return true
				end
			end
		end
		return false
	end

	local function OnTouchEnded(input, processed)
		if fingerTouches[input] == false then
			if NumUnsunkTouches == 1 then
				panBeginLook = nil
				startPos = nil
				lastPos = nil
				this.UserPanningTheCamera = false
				if not touchWorkspaceEventEnabled and IsTouchTap(input) then
					OnTouchTap(input.Position)
				end
			elseif NumUnsunkTouches == 2 then
				StartingDiff = nil
				pinchBeginZoom = nil
			end
		end

		if fingerTouches[input] ~= nil and fingerTouches[input] == false then
			NumUnsunkTouches = NumUnsunkTouches - 1
		end
		fingerTouches[input] = nil
		inputStartPositions[input] = nil
		inputStartTimes[input] = nil
	end

	local function OnMousePanButtonPressed(input, processed)
		if processed then return end
		this:UpdateMouseBehavior()
		panBeginLook = panBeginLook or this:GetCameraLook()
		startPos = startPos or input.Position
		lastPos = lastPos or startPos
		this.UserPanningTheCamera = true
	end

	local function OnMousePanButtonReleased(input, processed)
		this:UpdateMouseBehavior()
		if not (isRightMouseDown or isMiddleMouseDown) then
			panBeginLook = nil
			startPos = nil
			lastPos = nil
			this.UserPanningTheCamera = false
		end
	end

	local function OnMouse2Down(input, processed)
		if processed then return end

		isRightMouseDown = true
		OnMousePanButtonPressed(input, processed)
	end

	local function OnMouse2Up(input, processed)
		isRightMouseDown = false
		OnMousePanButtonReleased(input, processed)
	end

	local function OnMouse3Down(input, processed)
		if processed then return end

		isMiddleMouseDown = true
		OnMousePanButtonPressed(input, processed)
	end

	local function OnMouse3Up(input, processed)
		isMiddleMouseDown = false
		OnMousePanButtonReleased(input, processed)
	end

	local function OnMouseMoved(input, processed)
		if not hasGameLoaded and VRService.VREnabled then
			return
		end

		local inputDelta = input.Delta
		inputDelta = Vector2.new(inputDelta.X, inputDelta.Y * GameSettings:GetCameraYInvertValue())

		if startPos and lastPos and panBeginLook then
			local currPos = lastPos + input.Delta
			local totalTrans = currPos - startPos
			if this.PanEnabled then
				local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
				this.RotateInput = this.RotateInput + desiredXYVector
			end
			lastPos = currPos
		elseif this:IsInFirstPerson() or this:GetShiftLock() then
			if this.PanEnabled then
				local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
				this.RotateInput = this.RotateInput + desiredXYVector
			end
		end
	end

	local function OnMouseWheel(input, processed)
		if not hasGameLoaded and VRService.VREnabled then
			return
		end
		if not processed then
			if this.ZoomEnabled then
                this:ZoomCameraBy(clamp(-1, 1, -input.Position.Z) * 1.4)
			end
		end
	end

	local function round(num)
		return math_floor(num + 0.5)
	end

	local eight2Pi = math_pi / 4

	local function rotateVectorByAngleAndRound(camLook, rotateAngle, roundAmount)
		if camLook ~= ZERO_VECTOR3 then
			camLook = camLook.unit
			local currAngle = math_atan2(camLook.z, camLook.x)
			local newAngle = round((math_atan2(camLook.z, camLook.x) + rotateAngle) / roundAmount) * roundAmount
			return newAngle - currAngle
		end
		return 0
	end

	local function OnKeyDown(input, processed)
		if not hasGameLoaded and VRService.VREnabled then
			return
		end
		if processed then return end
		if this.ZoomEnabled then
			if input.KeyCode == Enum.KeyCode.I then
			--	this:ZoomCameraBy(-5)
			elseif input.KeyCode == Enum.KeyCode.O then
			--	this:ZoomCameraBy(5)
			end
		end
		if panBeginLook == nil and this.KeyPanEnabled then
			if input.KeyCode == Enum.KeyCode.Left then
				this.TurningLeft = true
			elseif input.KeyCode == Enum.KeyCode.Right then
				this.TurningRight = true
			elseif input.KeyCode == Enum.KeyCode.Comma then
				local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), -eight2Pi * (3/4), eight2Pi)
				if angle ~= 0 then
					this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
					this.LastUserPanCamera = tick()
					this.LastCameraTransform = nil
				end
			elseif input.KeyCode == Enum.KeyCode.Period then
				local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), eight2Pi * (3/4), eight2Pi)
				if angle ~= 0 then
					this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
					this.LastUserPanCamera = tick()
					this.LastCameraTransform = nil
				end
			elseif input.KeyCode == Enum.KeyCode.PageUp then
			--elseif input.KeyCode == Enum.KeyCode.Home then
				this.RotateInput = this.RotateInput + Vector2.new(0,math.rad(15))
				this.LastCameraTransform = nil
			elseif input.KeyCode == Enum.KeyCode.PageDown then
			--elseif input.KeyCode == Enum.KeyCode.End then
				this.RotateInput = this.RotateInput + Vector2.new(0,math.rad(-15))
				this.LastCameraTransform = nil
			end
		end
	end

	local function OnKeyUp(input, processed)
		if input.KeyCode == Enum.KeyCode.Left then
			this.TurningLeft = false
		elseif input.KeyCode == Enum.KeyCode.Right then
			this.TurningRight = false
		end
	end

	local lastThumbstickRotate = nil
	local numOfSeconds = 0.7
	local currentSpeed = 0
	local maxSpeed = 6
	local vrMaxSpeed = 4
	local lastThumbstickPos = Vector2.new(0,0)
	local ySensitivity = 0.65
	local lastVelocity = nil

	-- K is a tunable parameter that changes the shape of the S-curve
	-- the larger K is the more straight/linear the curve gets
	local k = 0.35
	local lowerK = 0.8
	local function SCurveTranform(t)
		t = clamp(-1,1,t)
		if t >= 0 then
			return (k*t) / (k - t + 1)
		end
		return -((lowerK*-t) / (lowerK + t + 1))
	end

	-- DEADZONE
	local DEADZONE = 0.1
	local function toSCurveSpace(t)
		return (1 + DEADZONE) * (2*math.abs(t) - 1) - DEADZONE
	end

	local function fromSCurveSpace(t)
		return t/2 + 0.5
	end

	local function gamepadLinearToCurve(thumbstickPosition)
		local function onAxis(axisValue)
			local sign = 1
			if axisValue < 0 then
				sign = -1
			end
			local point = fromSCurveSpace(SCurveTranform(toSCurveSpace(math.abs(axisValue))))
			point = point * sign
			return clamp(-1, 1, point)
		end
		return Vector2_new(onAxis(thumbstickPosition.x), onAxis(thumbstickPosition.y))
	end

	function this:UpdateGamepad()
		local gamepadPan = this.GamepadPanningCamera
		if gamepadPan and (hasGameLoaded or not VRService.VREnabled) then
			gamepadPan = gamepadLinearToCurve(gamepadPan)
			local currentTime = tick()
			if gamepadPan.X ~= 0 or gamepadPan.Y ~= 0 then
				this.userPanningTheCamera = true
			elseif gamepadPan == ZERO_VECTOR2 then
				lastThumbstickRotate = nil
				if lastThumbstickPos == ZERO_VECTOR2 then
					currentSpeed = 0
				end
			end

			local finalConstant = 0

			if lastThumbstickRotate then
				if VRService.VREnabled then
					currentSpeed = vrMaxSpeed
				else
					local elapsedTime = (currentTime - lastThumbstickRotate) * 10
					currentSpeed = currentSpeed + (maxSpeed * ((elapsedTime*elapsedTime)/numOfSeconds))

					if currentSpeed > maxSpeed then currentSpeed = maxSpeed end

					if lastVelocity then
						local velocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
						local velocityDeltaMag = (velocity - lastVelocity).magnitude

						if velocityDeltaMag > 12 then
							currentSpeed = currentSpeed * (20/velocityDeltaMag)
							if currentSpeed > maxSpeed then currentSpeed = maxSpeed end
						end
					end
				end

				local success, gamepadCameraSensitivity = pcall(function() return GameSettings.GamepadCameraSensitivity end)
				finalConstant = success and (gamepadCameraSensitivity * currentSpeed) or currentSpeed
				lastVelocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
			end

			lastThumbstickPos = gamepadPan
			lastThumbstickRotate = currentTime

			return Vector2_new( gamepadPan.X * finalConstant, gamepadPan.Y * finalConstant * ySensitivity * GameSettings:GetCameraYInvertValue())
		end

		return ZERO_VECTOR2
	end

	local InputBeganConn, InputChangedConn, InputEndedConn, MenuOpenedConn, ShiftLockToggleConn, GamepadConnectedConn, GamepadDisconnectedConn, TouchActivateConn = nil, nil, nil, nil, nil, nil, nil, nil

	function this:DisconnectInputEvents()
		if InputBeganConn then
			InputBeganConn:disconnect()
			InputBeganConn = nil
		end
		if InputChangedConn then
			InputChangedConn:disconnect()
			InputChangedConn = nil
		end
		if InputEndedConn then
			InputEndedConn:disconnect()
			InputEndedConn = nil
		end
		if MenuOpenedConn then
			MenuOpenedConn:disconnect()
			MenuOpenedConn = nil
		end
		if ShiftLockToggleConn then
			ShiftLockToggleConn:disconnect()
			ShiftLockToggleConn = nil
		end
		if GamepadConnectedConn then
			GamepadConnectedConn:disconnect()
			GamepadConnectedConn = nil
		end
		if GamepadDisconnectedConn then
			GamepadDisconnectedConn:disconnect()
			GamepadDisconnectedConn = nil
		end
		if subjectStateChangedConn then
			subjectStateChangedConn:disconnect()
			subjectStateChangedConn = nil
		end
		if workspaceChangedConn then
			workspaceChangedConn:disconnect()
			workspaceChangedConn = nil
		end
		if TouchActivateConn then
			TouchActivateConn:disconnect()
			TouchActivateConn = nil
		end

		this.TurningLeft = false
		this.TurningRight = false
		this.LastCameraTransform = nil
		self.LastSubjectCFrame = nil
		this.UserPanningTheCamera = false
		this.RotateInput = Vector2.new()
		this.GamepadPanningCamera = Vector2.new(0,0)

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

		fingerTouches = {}
		NumUnsunkTouches = 0

		StartingDiff = nil
		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

	function this:ResetInputStates()
		isRightMouseDown = false
		isMiddleMouseDown = false
		OnMousePanButtonReleased() -- this function doesn't seem to actually need parameters

		if UserInputService.TouchEnabled then
			--[[menu opening was causing serious touch issues
			this should disable all active touch events if
			they're active when menu opens.]]
			for inputObject, value in pairs(fingerTouches) do
				fingerTouches[inputObject] = nil
			end
			panBeginLook = nil
			startPos = nil
			lastPos = nil
			this.UserPanningTheCamera = false
			StartingDiff = nil
			pinchBeginZoom = nil
			NumUnsunkTouches = 0
		end
	end

	function this.getGamepadPan(name, state, input)
		if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then

			if state == Enum.UserInputState.Cancel then
				this.GamepadPanningCamera = ZERO_VECTOR2
				return
			end

			local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
			if inputVector.magnitude > THUMBSTICK_DEADZONE then
				this.GamepadPanningCamera = Vector2_new(input.Position.X, -input.Position.Y)
			else
				this.GamepadPanningCamera = ZERO_VECTOR2
			end
		end
	end

	function this.doGamepadZoom(name, state, input)
		if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.ButtonR3 and state == Enum.UserInputState.Begin then
			if this.ZoomEnabled then
				if this:GetCameraZoom() > 0.5 then
					this:ZoomCamera(0)
				else
					this:ZoomCamera(10)
				end
			end
		end
	end

	function this:BindGamepadInputActions()
		ContextActionService:BindAction("RootCamGamepadPan", this.getGamepadPan, false, Enum.KeyCode.Thumbstick2)
		ContextActionService:BindAction("RootCamGamepadZoom", this.doGamepadZoom, false, Enum.KeyCode.ButtonR3)
	end

	function this:ConnectInputEvents()
		InputBeganConn = UserInputService.InputBegan:connect(function(input, processed)
			if input.UserInputType == Enum.UserInputType.Touch then
				OnTouchBegan(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
				OnMouse2Down(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
				OnMouse3Down(input, processed)
			end
			-- Keyboard
			if input.UserInputType == Enum.UserInputType.Keyboard then
				OnKeyDown(input, processed)
			end
		end)

		InputChangedConn = UserInputService.InputChanged:connect(function(input, processed)
			if input.UserInputType == Enum.UserInputType.Touch then
				OnTouchChanged(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseMovement then
				OnMouseMoved(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseWheel then
				OnMouseWheel(input, processed)
			end
		end)

		InputEndedConn = UserInputService.InputEnded:connect(function(input, processed)
			if input.UserInputType == Enum.UserInputType.Touch then
				OnTouchEnded(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
				OnMouse2Up(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
				OnMouse3Up(input, processed)
			end
			-- Keyboard
			if input.UserInputType == Enum.UserInputType.Keyboard then
				OnKeyUp(input, processed)
			end
		end)

		if touchWorkspaceEventEnabled then
			TouchActivateConn = UserInputService.TouchTapInWorld:connect(function(touchPos, processed)
				if isDynamicThumbstickEnabled and not processed and not IsAToolEquipped and positionIntersectsGuiObject(touchPos, GestureArea) then
					if lastTapTime and tick() - lastTapTime < MAX_TIME_FOR_DOUBLE_TAP then
						local tween = {
							from = this:GetCameraZoom(),
							to = DefaultZoom,
							start = tick(),
							duration = 0.2,
							func = function(from, to, alpha)
								this:ZoomCamera(from + (to - from)*alpha)
								return to
							end
						}
						tweens["Zoom"] = tween
					else
						local humanoid = this:GetHumanoid()
						if humanoid then
							local player = PlayersService.LocalPlayer
							if player and player.Character then
								if humanoid and humanoid.Torso then
									local tween = {
										from = this.RotateInput,
										to = calcLookBehindRotateInput(humanoid.Torso),
										start = tick(),
										duration = 0.2,
										func = function(from, to, alpha)
											to = calcLookBehindRotateInput(humanoid.Torso)
											if to then
												this.RotateInput = from + (to - from)*alpha
											end
											return to
										end
									}
									tweens["Rotate"] = tween

									-- reset old camera info so follow cam doesn't rotate us
									this.LastCameraTransform = nil
								end
							end
						end
					end
					lastTapTime = tick()
				end
			end)
		end

		MenuOpenedConn = GuiService.MenuOpened:connect(function()
			this:ResetInputStates()
		end)

		workspaceChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(onCurrentCameraChanged)
		if workspace.CurrentCamera then
			onCurrentCameraChanged()
		end

		ShiftLockToggleConn = ShiftLockController.OnShiftLockToggled.Event:connect(function()
			this:UpdateMouseBehavior()
		end)

		this.RotateInput = Vector2.new()

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

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

		GamepadConnectedConn = UserInputService.GamepadDisconnected:connect(function(gamepadEnum)
			if this.activeGamepad ~= gamepadEnum then return end
			this.activeGamepad = nil
			assignActivateGamepad()
		end)

		GamepadDisconnectedConn = UserInputService.GamepadConnected:connect(function(gamepadEnum)
			if this.activeGamepad == nil then
				assignActivateGamepad()
			end
		end)

		self:BindGamepadInputActions()

		assignActivateGamepad()

		-- set mouse behavior
		self:UpdateMouseBehavior()
	end

	--Process tweens related to tap-to-recenter and double-tap-to-zoom
	--Needs to be called from specific cameras on each update
	function this:ProcessTweens()
		for name, tween in pairs(tweens) do
			local alpha = math.min(1.0, (tick() - tween.start)/tween.duration)
			tween.to = tween.func(tween.from, tween.to, alpha)
			if math.abs(1 - alpha) < 0.0001 then
				tweens[name] = nil
			end
		end
	end

	function this:SetEnabled(newState)
		if newState ~= self.Enabled then
			self.Enabled = newState
			if self.Enabled then
				self:ConnectInputEvents()
				self.cframe = workspace.CurrentCamera.CFrame
			else
				self:DisconnectInputEvents()
			end
		end
	end

	local function OnPlayerAdded(player)
		player.Changed:connect(function(prop)
			if this.Enabled then
				if prop == "CameraMode" or prop == "CameraMaxZoomDistance" or prop == "CameraMinZoomDistance" then
					 this:ZoomCameraFixedBy(0)
				end
			end
		end)

		local function OnCharacterAdded(newCharacter)

			local humanoid = findPlayerHumanoid(player)
			local start = tick()
			while tick() - start < 0.3 and (humanoid == nil or humanoid.Torso == nil) do
				wait()
				humanoid = findPlayerHumanoid(player)
			end

			if humanoid and humanoid.Torso and player.Character == newCharacter then
				local newDesiredLook = (humanoid.Torso.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
				local horizontalShift = findAngleBetweenXZVectors(newDesiredLook, this:GetCameraLook())
				local vertShift = math.asin(this:GetCameraLook().y) - math.asin(newDesiredLook.y)
				if not IsFinite(horizontalShift) then
					horizontalShift = 0
				end
				if not IsFinite(vertShift) then
					vertShift = 0
				end
				this.RotateInput = Vector2.new(horizontalShift, vertShift)

				-- reset old camera info so follow cam doesn't rotate us
				this.LastCameraTransform = nil
			end

			-- Need to wait for camera cframe to update before we zoom in
			-- Not waiting will force camera to original cframe
			wait()
			this:ZoomCamera(this.DefaultZoom)
		end

		player.CharacterAdded:connect(function(character)
			if this.Enabled or SetCameraOnSpawn then
				OnCharacterAdded(character)
				SetCameraOnSpawn = false
			end
		end)
		if player.Character then
			spawn(function() OnCharacterAdded(player.Character) end)
		end
	end
	if PlayersService.LocalPlayer then
		OnPlayerAdded(PlayersService.LocalPlayer)
	end
	PlayersService.ChildAdded:connect(function(child)
		if child and PlayersService.LocalPlayer == child then
			OnPlayerAdded(PlayersService.LocalPlayer)
		end
	end)

	local function OnGameLoaded()
		hasGameLoaded = true
	end

	spawn(function()
		if game:IsLoaded() then
			OnGameLoaded()
		else
			game.Loaded:wait()
			OnGameLoaded()
		end
	end)

	local function OnDynamicThumbstickEnabled()
		if UserInputService.TouchEnabled then
			isDynamicThumbstickEnabled = true
		end
	end

	local function OnDynamicThumbstickDisabled()
		isDynamicThumbstickEnabled = false
	end

	local function OnGameSettingsTouchMovementModeChanged()
		if LocalPlayer.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice then
			if GameSettings.TouchMovementMode.Name == "DynamicThumbstick" then
				OnDynamicThumbstickEnabled()
			else
				OnDynamicThumbstickDisabled()
			end
		end
	end

	local function OnDevTouchMovementModeChanged()
		if LocalPlayer.DevTouchMovementMode.Name == "DynamicThumbstick" then
			OnDynamicThumbstickEnabled()
		else
			OnGameSettingsTouchMovementModeChanged()
		end
	end

	if PlayersService.LocalPlayer then
		PlayersService.LocalPlayer.Changed:Connect(function(prop)
			if prop == "DevTouchMovementMode" then
				OnDevTouchMovementModeChanged()
			end
		end)
		OnDevTouchMovementModeChanged()
	end

	GameSettings.Changed:Connect(function(prop)
		if prop == "TouchMovementMode" then
			OnGameSettingsTouchMovementModeChanged()
		end
	end)
	OnGameSettingsTouchMovementModeChanged()
	GameSettings:SetCameraYInvertVisible()
	pcall(function() GameSettings:SetGamepadCameraSensitivityVisible() end)

	return this
end

return CreateCamera


Prompt: --Setup animation here--

Response:
setup(animation.ControlSurfaces.ElevatorL,0.03)
setup(animation.ControlSurfaces.ElevatorR,0.03)
setup(animation.ControlSurfaces.Rudder,0.03)
setupInside(animation.Wings.WFL.Aileron,0.03)
setupInside(animation.Wings.WFR.Aileron,0.03)
setupInside(animation.Gears.NG.Wheel,0.03)
setup(animation.Wipers.L,0.05)
setup(animation.Wipers.R,0.05)
setup(animation.Engines.E1)
setup(animation.Engines.E2)
setup(animation.Doors.FLD,0.01)
setup(animation.Doors.FRD,0.01)
setup(animation.Doors.RLD,0.01)
setup(animation.Doors.RRD,0.01)
setup(animation.Doors.CD1,0.01)
setup(animation.Doors.CD2,0.01)
setupInside(animation.Doors.FLD.Door,0.01)
setupInside(animation.Doors.FRD.Door,0.01)
setupInside(animation.Doors.RLD.Door,0.01)
setupInside(animation.Doors.RRD.Door,0.01)
setup(animation.Wings.WRL)
setup(animation.Wings.WRR)
setup(animation.Wings.WFL,0.002)
setup(animation.Wings.WFR,0.002)
setup(animation.Gears.NG,0.005)
setup(animation.Gears.LG,0.005)
setup(animation.Gears.RG,0.005)
setupInside(animation.Gears.LG.S,0.005)
setupInside(animation.Gears.RG.S,0.005)
setup(animation.Gears.NGDL1,0.015)
setup(animation.Gears.NGDR1,0.015)
setup(animation.Gears.NGDL2,0.015)
setup(animation.Gears.NGDR2,0.015)
setup(animation.Gears.LGD,0.005)
setup(animation.Gears.RGD,0.005)
setupInside(animation.Gears.LGD.GD2)
setupInside(animation.Gears.RGD.GD2)
setup(animation.Gears.LGBD,0.015)
setup(animation.Gears.RGBD,0.015)
setupInside(animation.Wings.WFL.Flap,0.002)
setupInside(animation.Wings.WRL.Flap,0.002)
setupInside(animation.Wings.WFR.Flap,0.002)
setupInside(animation.Wings.WRR.Flap,0.002)
setupInside(animation.Wings.WFL.Fairing,0.002)
setupInside(animation.Wings.WFR.Fairing,0.002)
setupInside(animation.Wings.WRL.Fairing,0.002)
setupInside(animation.Wings.WRR.Fairing,0.002)
setupInside(animation.Wings.WFL.Slat,0.002)
setupInside(animation.Wings.WRL.Slat,0.002)
setupInside(animation.Wings.WFR.Slat,0.002)
setupInside(animation.Wings.WRR.Slat,0.002)
setupInside(animation.Wings.WFL.Spoiler,0.01)
setupInside(animation.Wings.WRL.Spoiler,0.01)
setupInside(animation.Wings.WFR.Spoiler,0.01)
setupInside(animation.Wings.WRR.Spoiler,0.01)
setup(animation.Light,0.01)
setup(animation.Instruments.GearLever,0.05)
setup(animation.Instruments.Throttle,0.03)
setup(animation.Instruments.JotStick1,0.03)
setupInside(animation.Instruments.JotStick1.Roll,0.03)
setupInside(animation.Instruments.JotStick1.Roll.PitchStick,0.03)
setupInside(animation.Instruments.JotStick1.Roll.PitchStick.RollStick,0.03)
setup(animation.Instruments.JotStick2,0.03)
setupInside(animation.Instruments.JotStick2.Roll,0.03)
setupInside(animation.Instruments.JotStick2.Roll.PitchStick,0.03)
setupInside(animation.Instruments.JotStick2.Roll.PitchStick.RollStick,0.03)
setup(animation.Instruments.Rudder1,0.03)
setup(animation.Instruments.Rudder2,0.03)
setup(animation.Instruments.Spoiler,0.03)
setup(animation.Instruments.Flap,0.03)
setup(animation.Doors.Cockpit,0.05)
setup(animation.Doors.PilotRestroom,0.05)
setup(animation.Doors.Restroom1,0.05)
setup(animation.Doors.Restroom2,0.05)
setup(animation.EmergencyExits.One)
setup(animation.EmergencyExits.Two)
setup(animation.EmergencyExits.Three)
setup(animation.EmergencyExits.Four)
setup(animation.Slides.FL)
setup(animation.Slides.FR)
setup(animation.Slides.RL)
setup(animation.Slides.RR)
setup(animation.Slides.WL)
setup(animation.Slides.WR)
setupInside(animation.Slides.FL.Parts)
setupInside(animation.Slides.FR.Parts)
setupInside(animation.Slides.RL.Parts)
setupInside(animation.Slides.RR.Parts)
setupInside(animation.Slides.WL.Parts)
setupInside(animation.Slides.WR.Parts)
setupInside(animation.Slides.FL.CanCollide)
setupInside(animation.Slides.FR.CanCollide)
setupInside(animation.Slides.RL.CanCollide)
setupInside(animation.Slides.RR.CanCollide)
setupInside(animation.Slides.WL.CanCollide)
setupInside(animation.Slides.WR.CanCollide)
setup(animation.Reversers) -- IAE and neo
wait(2)
animation.Engines.E1.A.Motor.DesiredAngle = 100e100
animation.Engines.E2.A.Motor.DesiredAngle = 100e100
animation.Gears.NGDL1.A.Motor.DesiredAngle = 1
animation.Gears.NGDR1.A.Motor.DesiredAngle = 1
animation.Gears.LGD.A.Motor.DesiredAngle = 1.45
animation.Gears.RGD.A.Motor.DesiredAngle = 1.45
animation.Gears.LGD.GD2.A.Motor.C1 = animation.Gears.LGD.GD2.A.Motor.C1 * CFrame.new(1.5,0,0)
animation.Gears.RGD.GD2.A.Motor.C1 = animation.Gears.RGD.GD2.A.Motor.C1 * CFrame.new(1.5,0,0)
animation.Wings.WFL.Aileron.A.Motor.DesiredAngle = 0.5
animation.Wings.WFR.Aileron.A.Motor.DesiredAngle = -0.5
animation.ControlSurfaces.ElevatorL.A.Motor.DesiredAngle = -0.3
animation.ControlSurfaces.ElevatorR.A.Motor.DesiredAngle = -0.3
animation.ControlSurfaces.Rudder.A.Motor.DesiredAngle = 0.2
wait(5)
unAnchor(script.Parent.AnimationParts)
Chassis.Anchored = false


Prompt: -- Player specific convenience variables

Response:
local MyPlayer = nil
local MyCharacter = nil
local MyHumanoid = nil
local MyTorso = nil
local MyMouse = nil

local RecoilAnim
local RecoilTrack = nil

local ReloadAnim
local ReloadTrack = nil

local IconURL = Tool.TextureId
local DebrisService = game:GetService('Debris')
local PlayersService = game:GetService('Players')

local FireSound

local OnFireConnection = nil
local OnReloadConnection = nil

local DecreasedAimLastShot = false
local LastSpreadUpdate = time()

local flare = script.Parent:WaitForChild("Flare")

local FlashHolder = nil

local WorldToCellFunction = Workspace.Terrain.WorldToCellPreferSolid
local GetCellFunction = Workspace.Terrain.GetCell

function RayIgnoreCheck(hit, pos)
	if hit then
		if hit.Transparency >= 1 or string.lower(hit.Name) == "water" or
				hit.Name == "Effect" or hit.Name == "Rocket" or hit.Name == "Bullet" or
				hit.Name == "Handle" or hit:IsDescendantOf(MyCharacter) then
			return true
		elseif hit:IsA('Terrain') and pos then
			local cellPos = WorldToCellFunction(Workspace.Terrain, pos)
			if cellPos then
				local cellMat = GetCellFunction(Workspace.Terrain, cellPos.x, cellPos.y, cellPos.z)
				if cellMat and cellMat == Enum.CellMaterial.Water then
					return true
				end
			end
		end
	end
	return false
end


Prompt:
----------------------------------
------------FUNCTIONS-------------
----------------------------------

Response:
function Receive(player, action, ...)
	local args = {...}
	if player == User and action == "play" then
		Connector:FireAllClients("play", User, args[1], Settings.SoundSource.Position, Settings.PianoSoundRange, Settings.PianoSounds, args[2], Settings.IsCustom)
		HighlightPianoKey((args[1] > 61 and 61) or (args[1] < 1 and 1) or args[1],args[3])
	elseif player == User and action == "abort" then
		Deactivate()
		if SeatWeld then
			SeatWeld:remove()
		end
	end
end

function Activate(player)
	Connector:FireClient(player, "activate", Settings.CameraCFrame, Settings.PianoSounds, true)
	User = player
	Startup()
end

function Deactivate()
	coroutine.resume(coroutine.create(Shutdown))
	wait()
	if User and User.Parent then
		Connector:FireClient(User, "deactivate")
		User.Character:SetPrimaryPartCFrame(Box.CFrame + Vector3.new(0, 5, 0))
	end
	User = nil
end


Prompt: --[[  Now for the rest  ]]

Response:
local Lighting = game:GetService("Lighting")
local MarketplaceService = game:GetService("MarketplaceService")
local Players = game:GetService("Players")
local gamepassId = 184464896 --Put the ID of the asset here
local toolNames = {"Portal Gun"} --Must be in lighting
local toolsParent = Lighting

local function onPlayerAdded(player)
    local function onCharacterAdded(character)
        if MarketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassId) then
            for i = 1, #toolNames do
                local tool = toolsParent:FindFirstChild(toolNames[i])
                if tool then
                    local clone = tool:Clone()
                    clone.Parent = player.Backpack
                end
            end
        end
    end

    if player.Character then
        onCharacterAdded(player.Character)
    end
    player.CharacterAdded:Connect(onCharacterAdded)
end

Players.PlayerAdded:Connect(onPlayerAdded)


Prompt:
--[=[
	Returns the remaining time before the AccelTween attains the target.
	@readonly
	@prop rtime number
	@within AccelTween
]=]


Prompt: --Precalculated paths

Response:
local t,f,n=true,false,{}
local r={
[58]={{45,44,28,29,31,32,34,35,39,41,30,56,58},t},
[49]={{45,49},t},
[16]={n,f},
[19]={{45,44,28,29,31,32,34,35,39,41,30,56,58,20,19},t},
[59]={{45,44,28,29,31,32,34,35,39,41,59},t},
[63]={{45,44,28,29,31,32,34,35,39,41,30,56,58,23,62,63},t},
[34]={{45,44,28,29,31,32,34},t},
[21]={{45,44,28,29,31,32,34,35,39,41,30,56,58,20,21},t},
[48]={{45,49,48},t},
[27]={{45,44,28,27},t},
[14]={n,f},
[31]={{45,44,28,29,31},t},
[56]={{45,44,28,29,31,32,34,35,39,41,30,56},t},
[29]={{45,44,28,29},t},
[13]={n,f},
[47]={{45,49,48,47},t},
[12]={n,f},
[45]={{45},t},
[57]={{45,44,28,29,31,32,34,35,39,41,30,56,57},t},
[36]={{45,44,28,29,31,32,33,36},t},
[25]={{45,44,28,27,26,25},t},
[71]={{45,44,28,29,31,32,34,35,39,41,59,61,71},t},
[20]={{45,44,28,29,31,32,34,35,39,41,30,56,58,20},t},
[60]={{45,44,28,29,31,32,34,35,39,41,60},t},
[8]={n,f},
[4]={n,f},
[75]={{45,44,28,29,31,32,34,35,39,41,59,61,71,72,76,73,75},t},
[22]={{45,44,28,29,31,32,34,35,39,41,30,56,58,20,21,22},t},
[74]={{45,44,28,29,31,32,34,35,39,41,59,61,71,72,76,73,74},t},
[62]={{45,44,28,29,31,32,34,35,39,41,30,56,58,23,62},t},
[1]={n,f},
[6]={n,f},
[11]={n,f},
[15]={n,f},
[37]={{45,44,28,29,31,32,33,36,37},t},
[2]={n,f},
[35]={{45,44,28,29,31,32,34,35},t},
[53]={{45,49,48,47,52,53},t},
[73]={{45,44,28,29,31,32,34,35,39,41,59,61,71,72,76,73},t},
[72]={{45,44,28,29,31,32,34,35,39,41,59,61,71,72},t},
[33]={{45,44,28,29,31,32,33},t},
[69]={{45,44,28,29,31,32,34,35,39,41,60,69},t},
[65]={{45,44,28,29,31,32,34,35,39,41,30,56,58,20,19,66,64,65},t},
[26]={{45,44,28,27,26},t},
[68]={{45,44,28,29,31,32,34,35,39,41,30,56,58,20,19,66,64,67,68},t},
[76]={{45,44,28,29,31,32,34,35,39,41,59,61,71,72,76},t},
[50]={{45,49,48,47,50},t},
[66]={{45,44,28,29,31,32,34,35,39,41,30,56,58,20,19,66},t},
[10]={n,f},
[24]={{45,44,28,27,26,25,24},t},
[23]={{45,44,28,29,31,32,34,35,39,41,30,56,58,23},t},
[44]={{45,44},t},
[39]={{45,44,28,29,31,32,34,35,39},t},
[32]={{45,44,28,29,31,32},t},
[3]={n,f},
[30]={{45,44,28,29,31,32,34,35,39,41,30},t},
[51]={{45,49,48,47,50,51},t},
[18]={n,f},
[67]={{45,44,28,29,31,32,34,35,39,41,30,56,58,20,19,66,64,67},t},
[61]={{45,44,28,29,31,32,34,35,39,41,59,61},t},
[55]={{45,49,48,47,52,53,54,55},t},
[46]={{45,49,48,47,46},t},
[42]={{45,44,28,29,31,32,34,35,38,42},t},
[40]={{45,44,28,29,31,32,34,35,40},t},
[52]={{45,49,48,47,52},t},
[54]={{45,49,48,47,52,53,54},t},
[43]={n,f},
[7]={n,f},
[9]={n,f},
[41]={{45,44,28,29,31,32,34,35,39,41},t},
[17]={n,f},
[38]={{45,44,28,29,31,32,34,35,38},t},
[28]={{45,44,28},t},
[5]={n,f},
[64]={{45,44,28,29,31,32,34,35,39,41,30,56,58,20,19,66,64},t},
}
return r


Prompt:
--[=[
	@interface KnitOptions
	.ServicePromises boolean?
	.Middleware Middleware?
	.PerServiceMiddleware PerServiceMiddleware?
	@within KnitClient

	- `ServicePromises` defaults to `true` and indicates if service methods use promises.
	- Each service will go through the defined middleware, unless the service
	has middleware defined in `PerServiceMiddleware`.
]=]

Response:
type KnitOptions = {
	ServicePromises: boolean,
	Middleware: Middleware?,
	PerServiceMiddleware: PerServiceMiddleware?,
}

local defaultOptions: KnitOptions = {
	ServicePromises = true,
	Middleware = nil,
	PerServiceMiddleware = {},
}

local selectedOptions = nil


Prompt:
--[[_________________________________________________
	!!!Put this is StarterPack OR IT WILL NOT WORK!!!
	_________________________________________________
--]]
--Do not edit the Below unless you know what you are doing!--
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Response:
wait(1)
while true do
		wait(0.001)
		if script.Parent.TextButton.Text ==("Click to disable Rainbow Trail")then
		fs = Instance.new("Part")
		fs.TopSurface = "Smooth"
		fs.BottomSurface = "Smooth"
		fs.Anchored = true
		fs.FormFactor = "Custom"
		fs.Shape = "Ball"
		fs.Size = Vector3.new(0.9, 1.5, 0.9)
		fs.Transparency = 0.2
		fs.CanCollide = false
		fs.BrickColor = BrickColor.Random()
		fs.CFrame = script.Parent.Parent.Parent.Character:FindFirstChild("Torso").CFrame
		fs.Parent = game.Workspace
		game.Debris:AddItem(fs,1)
		end
	end


Prompt:
---WeldConstraint anything you want to move to the TargetL_Closed and TargetR_Closed MeshParts.
---TargetL_Closed and TargetR_Closed should be anchored but child welded objects that move with TargetL_Closed and TargetR_Closed should not be anchored.
---You may move, rotate and scale the Targets but for a simple door only translation is used.
---IntValue Speed controls the doors speed to open and close.
---After you have finished creating your door turn the transparency of Trigger, TargetL_Closed, TargetL_Open, TargetR_Closed and TargetR_Open to 1 or fully transparent.

Response:
local TweenService = game:GetService("TweenService")

local model = script.Parent

local trigger = model.Trigger

local left = model.TargetL_Closed
local right = model.TargetR_Closed

local doorTrigger = model.DoorTrigger.Value

local tweenInfo = TweenInfo.new (
	model.Speed.Value, --Time/Speed of Door Tween
	Enum.EasingStyle.Quart,  --Easing Style
	Enum.EasingDirection.InOut, --EasingDirection
	0, --Repeat Count
	false, --Reverse true
	0 --Delay
)

local DoorState = {
	["Closed"] = 1,
	["Opening"] = 2,
	["Open"] = 3,
	["Closing"] = 4,
}

local doorState = DoorState.Closed
local playersNear = {}

local tweenL = TweenService:Create(left, tweenInfo, {CFrame = model.TargetL_Open.CFrame})
local tweenR = TweenService:Create(right, tweenInfo, {CFrame = model.TargetR_Open.CFrame})

local tweenLClose = TweenService:Create(left, tweenInfo, {CFrame = model.TargetL_Closed.CFrame})
local tweenRClose = TweenService:Create(right, tweenInfo, {CFrame = model.TargetR_Closed.CFrame})

local function StartOpening()
	doorTrigger.CanTouch = false
	print("False")
	doorState = DoorState.Opening
	model["Door"]:Play()

	tweenL:Play()
	tweenR:Play()
	wait(7)
	doorTrigger.CanTouch = true
	print("True")
end

local function StartClosing()
	doorState = DoorState.Closing
	--model["Door"]:Play()

	tweenLClose:Play()
	tweenRClose:Play()
end

local function tweenOpenCompleted(playbackState)
	if(next(playersNear) == nil) then
		StartClosing()
	else
		doorState = DoorState.Open
	end
end

local function tweenCloseCompleted(playbackState)
	if(next(playersNear) ~= nil) then
		StartOpening()
	else
		doorState = DoorState.Closed
	end
end

tweenL.Completed:Connect(tweenOpenCompleted)
tweenLClose.Completed:Connect(tweenCloseCompleted)

local function touched(otherPart)
	if(otherPart.Name == "HumanoidRootPart" ) then
		local player = game.Players:FindFirstChild(otherPart.Parent.Name)
		if(player) then
			--print("touch")
			playersNear[player] = 1
			if(doorState == DoorState.Closed) then
				StartOpening()
			end
		end
	end
end


Prompt: --[[Susupension]]

Response:
	Tune.SusEnabled		= true		-- works only in with PGSPhysicsSolverEnabled, defaults to false when PGS is disabled

	--Front Suspension
	Tune.FSusDamping	= 50		-- Spring Dampening
	Tune.FSusStiffness	= 9000		-- Spring Force
	Tune.FSusLength		= 1 		-- Suspension length (in studs)
	Tune.FSusMaxExt		= .3		-- Max Extension Travel (in studs)
	Tune.FSusMaxComp	= .3		-- Max Compression Travel (in studs)
	Tune.FSusAngle		= 80		-- Suspension Angle (degrees from horizontal)

	Tune.FWsBoneLen		= 1			-- Wishbone Length
	Tune.FWsBoneAngle	= 10			-- Wishbone angle (degrees from horizontal)
	Tune.FAnchorOffset	= {			-- Suspension anchor point offset (relative to center of wheel)
	 --[[Lateral]]		-.4		,	-- positive = outward
	 --[[Vertical]]		-.5		,	-- positive = upward
	 --[[Forward]]		0		}	-- positive = forward

	--Rear Suspension
	Tune.RSusDamping	= 50		-- Spring Dampening
	Tune.RSusStiffness	= 9000		-- Spring Force
	Tune.RSusLength		= 1			-- Suspension length (in studs)
	Tune.RSusMaxExt		= .3		-- Max Extension Travel (in studs)
	Tune.RSusMaxComp	= .3		-- Max Compression Travel (in studs)
	Tune.RSusAngle		= 80		-- Suspension Angle (degrees from horizontal)

	Tune.RWsBoneLen		= 1			-- Wishbone Length
	Tune.RWsBoneAngle	= 10			-- Wishbone angle (degrees from horizontal)
	Tune.RAnchorOffset	= {			-- Suspension anchor point offset (relative to center of wheel)
	 --[[Lateral]]		-.4		,	-- positive = outward
	 --[[Vertical]]		-.5		,	-- positive = upward
	 --[[Forward]]		0		}	-- positive = forward

	--Aesthetics
	Tune.SusVisible		= false			-- Spring Visible
	Tune.WsBVisible		= false			-- Wishbone Visible
	Tune.SusRadius		= .2			-- Suspension Coil Radius
	Tune.SusThickness	= .1			-- Suspension Coil Thickness
	Tune.SusColor		= "Really black"	-- Suspension Color [BrickColor]
	Tune.SusCoilCount	= 6				-- Suspension Coil Count
	Tune.WsColor		= "Black"		-- Wishbone Color [BrickColor]
	Tune.WsThickness	= .1			-- Wishbone Rod Thickness


Prompt: --Sir_Falstaff wuz here

Response:
local TweenService = game:GetService("TweenService")
local FogTweenInfo = TweenInfo.new(5,Enum.EasingStyle.Linear)


Prompt: -- Make signal strict

Response:
setmetatable(Signal, {
	__index = function(_tb, key)
		error(("Attempt to get Signal::%s (not a valid member)"):format(tostring(key)), 2)
	end,
	__newindex = function(_tb, key, _value)
		error(("Attempt to set Signal::%s (not a valid member)"):format(tostring(key)), 2)
	end,
})

return {
	new = Signal.new,
	Wrap = Signal.Wrap,
	Is = Signal.Is,
}


Prompt:
--[[
	Performs a left-fold of the list with the given initial value and callback.
]]

Response:
local function foldLeft(list, callback, initialValue)
	local accum = initialValue

	for i = 1, #list do
		accum = callback(accum, list[i], i)
	end

	return accum
end

return foldLeft


Prompt:
--		local tix = Instance.new("TextButton", cl)
--		tix.Style = "Custom"
--		tix.Size = UDim2.new(0,35,1,0)
--		tix.TextScaled=true
--		tix.Position = UDim2.new(1,-75,0,0)
--		tix.ZIndex = 2
--		tix.Font = "Arial"
--		tix.FontSize = "Size18"
--		tix.Text = "TIX"
--		tix.BackgroundColor3=Color3.new(1,1,0)
--		tix.BorderSizePixel=0
--		tix.TextColor3=Color3.new(1,1,1)
--		tix.BackgroundTransparency=0.5
--		tix.MouseButton1Click:connect(function()
--			service.MarketPlace:PromptPurchase(localplayer,v.ID,false,Enum.CurrencyType.Tix)
--		end)

Response:
		extraPane.CanvasSize=UDim2.new(0,0,(num+1)/20,0)
		num = num +1
	end
	extraPane.CanvasSize=UDim2.new(0,0,(num+1)/20,0)

	robux.MouseButton1Click:connect(function()
		service.MarketPlace:PromptPurchase(localplayer, pass[1])
	end)

	--tickets.MouseButton1Click:connect(function()
	--	service.MarketPlace:PromptPurchase(localplayer,pass[1],false,Enum.CurrencyType.Tix)
	--end)

	extra.MouseButton1Click:connect(function()
		service.MarketPlace:PromptPurchase(localplayer,304651585)
		--[[
		if not extraPane.Visible then
			extraPane.Visible = true
		else
			extraPane.Visible = false
		end--]]
	end)

	if playerData.isDonor then
		status.Text = "Donated"
		capeTexture.TextTransparency=0
		capeMaterial.TextTransparency=0
		capeColor.TextTransparency=0
		capeColor.BackgroundTransparency=0
		capeToggle.TextTransparency=0

		local ored,ogreen,oblue = 255*currentColor.r,255*currentColor.g,255*currentColor.b
		local newColor = currentColor
		local debounce = false

		local function updateStatus()
			status.Text = "Updating..."
			status.Text = client.Remote.Get("UpdateDonor",playerData.Donor)
			wait(0.5)
			status.Text = "Donated"
		end

		local function showColorPicker()
			color.BackgroundColor3 = currentColor
			red.Text = ored
			green.Text = ogreen
			blue.Text = oblue
			materialPicker.Visible = false
			texturePicker.Visible = false
			colorPicker.Visible = true
		end

		local function showMaterialPicker()
			materialPicker.Visible = true
			texturePicker.Visible = false
			colorPicker.Visible = false
		end

		local function showTexturePicker()
			texturePicker.Frame.ID.Text = currentTexture
			texturePicker.Frame.Image.Image = "rbxassetid://"..currentTexture
			materialPicker.Visible = false
			texturePicker.Visible = true
			colorPicker.Visible = false
		end

		-- Color Picker

		local function updateColor()
			local nred,ngreen,nblue = tonumber(red.Text),tonumber(green.Text),tonumber(blue.Text)
			if not nred then
				nred = 0
			end
			if not ngreen then
				ngreen = 0
			end
			if not nblue then
				nblue = 0
			end
			if nred>255 then nred=255 end
			if ngreen>255 then ngreen=255 end
			if nblue>255 then nblue=255 end
			nred = math.floor(nred)
			ngreen = math.floor(ngreen)
			nblue = math.floor(nblue)
			red.Text = nred
			green.Text = ngreen
			blue.Text = nblue
			nred,ngreen,nblue = nred/255,ngreen/255,nblue/255
			newColor = Color3.new(nred,ngreen,nblue)
			color.BackgroundColor3 = newColor
		end

		red.Changed:connect(updateColor)
		green.Changed:connect(updateColor)
		blue.Changed:connect(updateColor)

		confirmColor.MouseButton1Click:connect(function()
			if not debounce then
				debounce = true
				playerData.Donor.Cape.Color = {newColor.r,newColor.g,newColor.b}
				capeColor.BackgroundColor3 = newColor
				ored,ogreen,oblue = newColor.r*255,newColor.g*255,newColor.b*255
				currentColor = newColor
				colorPicker.Visible = false
				updateStatus()
				debounce = false
			end
		end)

		cancelColor.MouseButton1Click:connect(function()
			newColor = currentColor
			colorPicker.Visible = false
		end)

		-- Material Picker

		local materials = {
			"Brick";
			"Cobblestone";
			"Concrete";
			"CorrodedMetal";
			"DiamondPlate";
			"Fabric";
			"Foil";
			"Granite";
			"Grass";
			"Ice";
			"Marble";
			"Metal";
			"Neon";
			"Pebble";
			"Plastic";
			"Sand";
			"Slate";
			"SmoothPlastic";
			"Wood";
			"WoodPlanks";
			"Glass";
		}

		local materialEntry = materialPicker.Entry
		local matNum = 0
		for i,v in pairs(materials) do
			local NewClone = materialEntry:clone()
			NewClone.Parent = materialPicker.Frame
			NewClone.Position = UDim2.new(0, 0, 0, matNum*20)
			NewClone.Text = v
			NewClone.Visible = true
			NewClone.MouseButton1Click:connect(function()
				if not debounce then
					debounce = true
					capeMaterial.Text = v
					playerData.Donor.Cape.Material = v
					materialPicker.Visible = false
					updateStatus()
					debounce = false
				end
			end)
			matNum = matNum+1
			materialPicker.Frame.CanvasSize = UDim2.new(0, 0, 0, matNum*20)
		end

		-- Texture Picker

		local textureId = texturePicker.Frame.ID
		local confirmId = texturePicker.Frame.Confirm
		local cancelId = texturePicker.Frame.Cancel
		local preview = texturePicker.Frame.Image

		local newId = currentTexture
		textureId.FocusLost:connect(function(enterPressed)
			if enterPressed then
				newId = tonumber(texturePicker.Frame.ID.Text)
				if not newId then
					textureId.Text = "Invalid!"
					wait(1)
					textureId.Text = currentTexture
					preview.Image = "rbxassetid://"..currentTexture
				else
					capeTexture.Text = newId
					preview.Image = "rbxassetid://"..newId
				end
			end
		end)

		confirmId.MouseButton1Click:connect(function()
			if not debounce then
				debounce = true
				capeTexture.Text = newId
				playerData.Donor.Cape.Image = newId
				texturePicker.Visible = false
				updateStatus()
				debounce = false
			end
		end)

		cancelId.MouseButton1Click:connect(function()
			texturePicker.Visible = false
		end)

		-- Other

		capeToggle.MouseButton1Click:connect(function()
			if not debounce then
				debounce = true
				if playerData.Donor.Enabled then
					playerData.Donor.Enabled = false
					capeToggle.Text = "Disabled"
				elseif not playerData.Donor.Enabled then
					playerData.Donor.Enabled = true
					capeToggle.Text = "Enabled"
				end
				updateStatus()
				debounce = false
			end
		end)

		capeMaterial.MouseButton1Click:connect(function()
			if not materialPicker.Visible then
				showMaterialPicker()
			end
		end)

		capeTexture.MouseButton1Click:connect(function()
			if not texturePicker.Visible then
				showTexturePicker()
			end
		end)

		capeColor.MouseButton1Click:connect(function()
			if not colorPicker.Visible then
				showColorPicker()
			end
		end)
	else
		status.Text = "Not a Donor"
		capeTexture.TextTransparency=0.5
		capeMaterial.TextTransparency=0.5
		capeColor.TextTransparency=0.5
		capeColor.BackgroundTransparency=0.5
		capeToggle.TextTransparency=0.5
	end
	-- Keybinds

	local Keybinds = keybindsTab.Keybinds
	local addBind = keybindsTab.Add
	local editBind = keybindsTab.Edit
	local removeBind = keybindsTab.Removeb
	local bindEntry = keybindsTab.Entry

	local makeBind = addBind.AddBind
	local confirmBind = makeBind.Add
	local cancelBind = makeBind.Cancel
	local command = makeBind.Command
	local bindKey = makeBind.BindKey

	local keyEntry = keybindsTab.Entry

	local selectedKey
	local selectedCommand
	local selectedEntry

	local newBind

	local bindListener
	bindKey.MouseButton1Click:connect(function()
		client.Variables.WaitingForBind = true
		bindKey.Text = "Waiting for key press..."
		bindListener = service.UserInputService.InputBegan:connect(function(input)
			if input.KeyCode then
				newBind = input.KeyCode.Value
				client.Variables.WaitingForBind = false
				bindListener:disconnect()
				bindListener = nil
				bindKey.Text = string.char(newBind)
			end
		end)
	end)

	local function getBinds()
		local num = 0
		selectedKey = nil
		selectedCommand = nil
		selectedEntry = nil
		Keybinds:ClearAllChildren()
		for i,v in pairs(client.Variables.KeyBinds) do
			local clone = keyEntry:clone()
			clone.Text = "Key: "..string.char(i).." | Command: "..v
			clone.Position = UDim2.new(0,0,0,num*20)
			clone.Visible = true
			clone.Parent = Keybinds
			clone.MouseButton1Click:connect(function()
				if selectedEntry then
					selectedEntry.BackgroundTransparency = 1
				end
				clone.BackgroundTransparency = 0
				selectedKey = i
				selectedCommand = v
				selectedEntry = clone
			end)
			num = num + 1
		end
		Keybinds.CanvasSize = UDim2.new(0,0,0,num*20)
	end

	confirmBind.MouseButton1Click:connect(function()
		if command.Text~="" and newBind then
			client.Functions.AddKeyBind(newBind,command.Text)
			makeBind.Visible = false
			newBind = nil
			command.Text = ""
			bindKey.Text = "Click to Bind"
			getBinds()
		end
	end)

	cancelBind.MouseButton1Click:connect(function()
		makeBind.Visible = false
		newBind = nil
		command.Text = ""
		bindKey.Text = "Click to Bind"
		client.Variables.WaitingForBind = false
		if bindListener then bindListener:disconnect() end
		bindListener = nil
	end)

	addBind.MouseButton1Click:connect(function()
		if not makeBind.Visible then
			newBind = nil
			command.Text = ""
			bindKey.Text = "Click to Bind"
			makeBind.Visible = true
		end
	end)

	editBind.MouseButton1Click:connect(function()
		if selectedKey and selectedCommand and selectedEntry then
			newBind = nil
			command.Text = selectedCommand
			bindKey.Text = string.char(selectedKey)
			makeBind.Visible = true
		end
	end)

	removeBind.MouseButton1Click:connect(function()
		if selectedKey and selectedCommand and selectedEntry then
			client.Functions.RemoveKeyBind(selectedKey)
			getBinds()
		end
	end)

	getBinds()

	--Settings

	local SettingsScroll = settingsTab.Settings
	local settingData = client.Remote.Get("AllSettings")
	if not 	gui.Parent then return end

	if settingData and type(settingData)=="table" then
		settings.Visible = true

		local sets,descs,order = settingData.Settings,settingData.Descs,settingData.Order

		local num = 1
		local clear = toggleBut:clone()
		clear.Text = "Click 'Clear' to clear all saved settings"
		clear.TextButton.Text = "Clear"
		clear.TextButton.MouseButton1Click:connect(function()
			client.Remote.Send("ClearSavedSettings")
		end)
		clear.Visible = true
		Expand(clear, desc)
		clear.Desc.Value = "Clears all saved settings"
		clear.Position = UDim2.new(0,0,0,0)
		clear.Parent = settingsTab.Settings
		for i,set in pairs(order) do
			local v = sets[set]
			if set == " " then
				local clone = stringBut:Clone()
				clone.Text = " "
				clone.TextBox.Visible = false
				clone.Position = UDim2.new(0,0,0,num*20)
				clone.Visible = true
				clone.Parent = settingsTab.Settings
				num = num+1
			elseif v~=nil then
				if type(v)=="string" or type(v)=="number" then
					if set == "Theme" or set == "MobileTheme" then
						local clone = toggleBut:Clone()
						local materialPicker = materialPicker:Clone()
						materialPicker.Parent = clone
						local materialEntry = materialPicker.Entry

						materialPicker.Position = UDim2.new(1,-200,0,0)

						local function showPicker()
							local matNum = 0
							local themes = {}

							for i,v in pairs(client.Deps.UI:GetChildren()) do
								table.insert(themes,v.Name)
							end

							materialPicker.Frame:ClearAllChildren()
							for i,v in pairs(themes) do
								local NewClone = materialEntry:clone()
								NewClone.Parent = materialPicker.Frame
								NewClone.Position = UDim2.new(0, 0, 0, matNum*20)
								NewClone.Text = v
								NewClone.Visible = true
								NewClone.MouseButton1Click:connect(function()
									materialPicker.Visible = false
									clone.TextButton.Text = v
									sets[set] = v
									client.Remote.Send("SaveSetSetting",set,v)
								end)
								matNum = matNum+1
								materialPicker.Frame.CanvasSize = UDim2.new(0, 0, 0, matNum*20)
							end --// #lazySunday

							materialPicker.Visible = true
						end

						clone.Text = set..": "
						if descs[set] then
							clone.Desc.Value = descs[set]
						else
							clone.Desc.Value = set
						end
						clone.Position = UDim2.new(0,0,0,num*20)
						clone.Visible = true
						Expand(clone,desc)
						clone.TextButton.Text = v
						clone.TextButton.MouseButton1Down:connect(function()
							showPicker()
						end)
						clone.Parent = settingsTab.Settings
						num = num+1
					else
						local clone = stringBut:Clone()
						clone.Text = set..": "
						clone.TextBox.Text = v
						if descs[set] then
							clone.Desc.Value = descs[set]
						else
							clone.Desc.Value = set
						end
						clone.Position = UDim2.new(0,0,0,num*20)
						clone.Visible = true
						Expand(clone,desc)
						clone.TextBox.FocusLost:connect(function(enterPressed)
							if enterPressed then
								if type(v) == "number" then
									if tonumber(clone.TextBox.Text) then
										sets[set] = tonumber(clone.TextBox.Text)
										client.Remote.Send("SaveSetSetting",set,sets[set])
									else
										clone.TextBox.Text = v
									end
								else
									sets[set] = clone.TextBox.Text
									client.Remote.Send("SaveSetSetting",set,sets[set])
								end
							else
								clone.TextBox.Text = v
							end
						end)
						clone.Parent = settingsTab.Settings
						num = num+1
					end
				elseif type(v)=="boolean" then
					local clone = toggleBut:Clone()
					clone.Text = set..": "
					if descs[set] then
						clone.Desc.Value = descs[set]
					else
						clone.Desc.Value = set
					end
					clone.Position = UDim2.new(0,0,0,num*20)
					clone.Visible = true
					Expand(clone,desc)
					local enabled = v
					if enabled then
						clone.TextButton.Text = "Enabled"
					else
						clone.TextButton.Text = "Disabled"
					end
					clone.TextButton.MouseButton1Down:connect(function()
						if enabled then
							sets[set] = false
							enabled = false
							clone.TextButton.Text = "Disabled"
							client.Remote.Send("SaveSetSetting",set,sets[set])
						else
							sets[set] = true
							enabled = true
							clone.TextButton.Text = "Enabled"
							client.Remote.Send("SaveSetSetting",set,sets[set])
						end
					end)
					clone.Parent = settingsTab.Settings
					num = num+1
				elseif type(v)=="userdata" then
					if set=="TopbarColor" then
						local clone = colorBut:Clone()
						clone.Text = set..": "
						if descs[set] then
							clone.Desc.Value = descs[set]
						else
							clone.Desc.Value = set
						end
						clone.Position = UDim2.new(0,0,0,num*20)
						clone.Visible = true
						Expand(clone,desc)
						clone.TextButton.BackgroundColor3 = v

						local ored,ogreen,oblue = v.r*255,v.g*255,v.b*255

						local colorPicker = clone.ColorPicker
						colorPicker.Parent = settingsTab
						colorPicker.Position = UDim2.new(1,20,0,0)

						local red = colorPicker.Frame.R
						local green = colorPicker.Frame.G
						local blue = colorPicker.Frame.B
						local color = colorPicker.Frame.Color
						local confirmColor = colorPicker.Frame.Confirm
						local newColor = v
						local currentColor = v

						local function showColorPicker()
							color.BackgroundColor3 = currentColor
							red.Text = ored
							green.Text = ogreen
							blue.Text = oblue
							colorPicker.Visible = true
						end

						-- Color Picker

						local function updateColor()
							local nred,ngreen,nblue = tonumber(red.Text),tonumber(green.Text),tonumber(blue.Text)
							if not nred then
								nred = 0
							end
							if not ngreen then
								ngreen = 0
							end
							if not nblue then
								nblue = 0
							end
							if nred>255 then nred=255 end
							if ngreen>255 then ngreen=255 end
							if nblue>255 then nblue=255 end
							nred = math.floor(nred)
							ngreen = math.floor(ngreen)
							nblue = math.floor(nblue)
							red.Text = nred
							green.Text = ngreen
							blue.Text = nblue
							nred,ngreen,nblue = nred/255,ngreen/255,nblue/255
							newColor = Color3.new(nred,ngreen,nblue)
							color.BackgroundColor3 = newColor
						end

						red.Changed:connect(updateColor)
						green.Changed:connect(updateColor)
						blue.Changed:connect(updateColor)

						confirmColor.MouseButton1Click:connect(function()
							ored,ogreen,oblue = newColor.r*255,newColor.g*255,newColor.b*255
							currentColor = newColor
							colorPicker.Visible = false
							clone.TextButton.BackgroundColor3 = newColor
							sets[set] = newColor
							client.Remote.Send("SaveSetSetting",set,sets[set])
						end)

						cancelColor.MouseButton1Click:connect(function()
							newColor = currentColor
							colorPicker.Visible = false
						end)

						clone.TextButton.MouseButton1Down:connect(function()
							showColorPicker()
						end)

						clone.Parent = settingsTab.Settings
						num = num+1
					end
				elseif type(v)=="table" then
					local clone = tableBut:Clone()
					clone.Text = set..": "
					clone.TextButton.Text = "Edit"
					if descs[set] then
						clone.Desc.Value = descs[set]
					else
						clone.Desc.Value = set
					end
					clone.Position = UDim2.new(0,0,0,num*20)
					clone.Visible = true
					Expand(clone,desc)

					local function closeAll()
						for k,m in pairs(settingsTab:GetChildren()) do
							if m:IsA("Frame") and m.Name~="Settings" then
								m.Visible = false
							end
						end
					end

					if set=="Ranks" then
						local Ranks = settingsTab.Ranks:clone()
						Ranks.Parent = settingsTab
						local add = Ranks.Add
						local rem = Ranks.Removeb
						local close = Ranks.Close

						local newEntry = add.NewEntry
						local confirm = newEntry.Add
						local cancel = newEntry.Cancel
						local groupId = newEntry.Group
						local rank = newEntry.Rank
						local type = newEntry.Type

						local tab = Ranks.Table

						local entBut = Ranks.Entry

						local selectedEntry
						local selectedRank
						local selectedInd

						local function populate()
							tab:ClearAllChildren()
							local newm = 0
							for k,m in pairs(v) do
								local new = entBut:Clone()
								new.Text = "Group: "..m.Group.." | Rank: "..m.Rank.." | Type: "..m.Type
								new.Position = UDim2.new(0,0,0,newm*20)
								new.Visible = true
								new.ZIndex = 2
								new.Parent = tab
								new.MouseButton1Click:connect(function()
									if selectedEntry then
										selectedEntry.BackgroundTransparency = 1
									end
									new.BackgroundTransparency = 0
									selectedEntry = new
									selectedRank = m
									selectedInd = k
								end)
								newm=newm+1
							end
							tab.CanvasSize = UDim2.new(0,0,0,newm*20)
						end

						confirm.MouseButton1Click:connect(function()
							if groupId.Text~="" and rank.Text~="" and type.Text~="" then
								local tid = groupId.Text
								local tr = rank.Text
								local tt = type.Text

								groupId.Text=""
								rank.Text=""
								type.Text=""

								if tonumber(tid) then
									tid = tonumber(tid)
								end

								table.insert(sets[set],{Group=tid,Rank=tr,Type=tt})
								client.Remote.Send("SaveTableAdd",set,{Group=tid,Rank=tr,Type=tt})
								newEntry.Visible = false
								populate()
							end
						end)

						cancel.MouseButton1Click:connect(function()
							groupId.Text=""
							rank.Text=""
							type.Text=""
							newEntry.Visible = false
						end)

						rem.MouseButton1Click:connect(function()
							if selectedEntry and selectedRank and selectedInd then
								table.remove(sets[set],selectedInd)
								client.Remote.Send("SaveTableRemove",set,selectedRank)
								populate()
							end
						end)

						add.MouseButton1Click:connect(function()
							groupId.Text=""
							rank.Text=""
							type.Text=""
							closeAll()
							if newEntry.Visible then
								newEntry.Visible = false
							else
								newEntry.Visible = true
							end
						end)

						close.MouseButton1Click:connect(function()
							newEntry.Visible = false
							Ranks.Visible = false
						end)

						clone.TextButton.MouseButton1Click:connect(function()
							closeAll()
							if Ranks.Visible then
								Ranks.Visible = false
							else
								Ranks.Visible = true
							end
						end)

						populate()
					elseif set=="MusicList" then
						local Music = settingsTab.Music:clone()
						Music.Parent = settingsTab
						local tab = Music.Table
						local Add = Music.Add
						local Close = Music.Close
						local Entry = Music.Entry
						local Remove = Music.Removeb

						local newEnt = Add.NewEntry
						local newAdd = newEnt.Add
						local newCancel = newEnt.Cancel
						local newId = newEnt.ID
						local newName = newEnt.Nameb
						local newPitch = newEnt.Pitch
						local newVolume = newEnt.Volume

						local selectedEntry
						local selectedValue
						local selectedInd

						local function populate()
							tab:ClearAllChildren()
							local newm = 0
							for k,m in pairs(v) do
								local new = Entry:Clone()
								new.Text = "Name: "..m.Name.." | ID: "..m.Id.." | Pitch: "..(m.Pitch or "N/A").." | Volume: "..(m.Volume or "N/A")
								new.Position = UDim2.new(0,0,0,newm*20)
								new.Visible = true
								new.ZIndex = 2
								new.Parent = tab
								new.MouseButton1Click:connect(function()
									if selectedEntry then
										selectedEntry.BackgroundTransparency = 1
									end
									new.BackgroundTransparency = 0
									selectedEntry = new
									selectedValue = m
									selectedInd = k
								end)
								newm=newm+1
							end
							tab.CanvasSize = UDim2.new(0,0,0,newm*20)
						end

						newAdd.MouseButton1Click:connect(function()
							if newName.Text~="" and newPitch.Text~="" then
								local name = newName.Text
								local id = newId.Text
								local pitch = newPitch.Text
								local volume = newVolume.Text

								newName.Text = ""
								newId.Text = ""
								newPitch.Text = ""
								newVolume.Text = ""

								if pitch=="" then
									pitch = nil
								end
								if volume=="" then
									volume = nil
								end

								table.insert(sets[set],{Name=name,Id=id,Pitch=pitch,Volume=volume})
								client.Remote.Send("SaveTableAdd",set,{Name=name,Id=id,Pitch=pitch,Volume=volume})
								newEnt.Visible = false
								populate()
							end
						end)

						newCancel.MouseButton1Click:connect(function()
							newName.Text = ""
							newId.Text = ""
							newPitch.Text = ""
							newVolume.Text = ""
							newEnt.Visible = false
						end)

						Remove.MouseButton1Click:connect(function()
							if selectedEntry and selectedValue and selectedInd then
								table.remove(sets[set],selectedInd)
								client.Remote.Send("SaveTableRemove",set,selectedValue)
								populate()
							end
						end)

						Add.MouseButton1Click:connect(function()
							newName.Text = ""
							newId.Text = ""
							newPitch.Text = ""
							newVolume.Text = ""
							if newEnt.Visible then
								newEnt.Visible = false
							else
								newEnt.Visible = true
							end
						end)

						Close.MouseButton1Click:connect(function()
							newEnt.Visible = false
							Music.Visible = false
						end)

						clone.TextButton.MouseButton1Click:connect(function()
							closeAll()
							if Music.Visible then
								Music.Visible = false
							else
								Music.Visible = true
							end
						end)

						populate()
					elseif set=="CapeList" then
						local Capes = settingsTab.Capes:clone()
						Capes.Parent = settingsTab
						local tab = Capes.Table
						local Add = Capes.Add
						local Close = Capes.Close
						local Entry = Capes.Entry
						local Remove = Capes.Removeb

						local newEnt = Add.NewEntry
						local newAdd = newEnt.Add
						local newCancel = newEnt.Cancel
						local newId = newEnt.ID
						local newName = newEnt.Nameb
						local newMaterial = newEnt.Material
						local newReflectance = newEnt.Reflectance
						local newColor = newEnt.Color

						local selectedEntry
						local selectedValue
						local selectedInd

						local function populate()
							tab:ClearAllChildren()
							local newm = 0
							for k,m in pairs(v) do
								local new = Entry:Clone()
								new.Text = "Name: "..m.Name.." | Texture: "..(m.ID or "N/A").." | Color: "..(m.Color or "N/A").." | Material: "..(m.Material or "N/A").." | Reflectance: "..(m.Reflectance or "N/A")
								new.Position = UDim2.new(0,0,0,newm*20)
								new.Visible = true
								new.ZIndex = 2
								new.Parent = tab
								new.MouseButton1Click:connect(function()
									if selectedEntry then
										selectedEntry.BackgroundTransparency = 1
									end
									new.BackgroundTransparency = 0
									selectedEntry = new
									selectedValue = m
									selectedInd = k
								end)
								newm=newm+1
							end
							tab.CanvasSize = UDim2.new(0,0,0,newm*20)
						end

						newAdd.MouseButton1Click:connect(function()
							if newName.Text~="" then
								local name = newName.Text
								local id = newId.Text
								local material = newMaterial.Text
								local color = newColor.Text
								local reflectance = newReflectance.Text

								newName.Text = ""
								newId.Text = ""
								newMaterial.Text = ""
								newColor.Text = ""
								newReflectance.Text = ""

								if material=="" then
									material = nil
								end
								if color=="" then
									color = nil
								end
								if id=="" then
									id = nil
								end
								if reflectance=="" then
									reflectance = nil
								end

								table.insert(sets[set],{Name=name,ID=id,Color=color,Material=material,Reflectance=reflectance})
								client.Remote.Send("SaveTableAdd",set,{Name=name,ID=id,Color=color,Material=material,Reflectance=reflectance})
								newEnt.Visible = false
								populate()
							end
						end)

						newCancel.MouseButton1Click:connect(function()
							newName.Text = ""
							newId.Text = ""
							newMaterial.Text = ""
							newColor.Text = ""
							newReflectance.Text = ""
							newEnt.Visible = false
						end)

						Remove.MouseButton1Click:connect(function()
							if selectedEntry and selectedValue and selectedInd then
								table.remove(sets[set],selectedInd)
								client.Remote.Send("SaveTableRemove",set,selectedValue)
								populate()
							end
						end)

						Add.MouseButton1Click:connect(function()
							newName.Text = ""
							newId.Text = ""
							newMaterial.Text = ""
							newColor.Text = ""
							newReflectance.Text = ""
							if newEnt.Visible then
								newEnt.Visible = false
							else
								newEnt.Visible = true
							end
						end)

						Close.MouseButton1Click:connect(function()
							newEnt.Visible = false
							Capes.Visible = false
						end)

						clone.TextButton.MouseButton1Click:connect(function()
							closeAll()
							if Capes.Visible then
								Capes.Visible = false
							else
								Capes.Visible = true
							end
						end)

						populate()
					elseif set=="CommandPermissions" then
						local Perms = settingsTab.Permissions:clone()
						Perms.Parent = settingsTab
						local tab = Perms.Table
						local Add = Perms.Add
						local Close = Perms.Close
						local Entry = Perms.Entry
						local Remove = Perms.Removeb

						local newEnt = Add.NewEntry
						local newAdd = newEnt.Add
						local newCancel = newEnt.Cancel
						local newCommand = newEnt.Command
						local newLevel = newEnt.Level

						local selectedEntry
						local selectedValue
						local selectedInd

						local function populate()
							tab:ClearAllChildren()
							local newm = 0
							for k,m in pairs(v) do
								local new = Entry:Clone()
								new.Text = "Command: "..m.Command.." | Level: "..m.Level
								new.Position = UDim2.new(0,0,0,newm*20)
								new.Visible = true
								new.ZIndex = 2
								new.Parent = tab
								new.MouseButton1Click:connect(function()
									if selectedEntry then
										selectedEntry.BackgroundTransparency = 1
									end
									new.BackgroundTransparency = 0
									selectedEntry = new
									selectedValue = m
									selectedInd = k
								end)
								newm=newm+1
							end
							tab.CanvasSize = UDim2.new(0,0,0,newm*20)
						end

						newAdd.MouseButton1Click:connect(function()
							if newCommand.Text~="" and newLevel.Text~="" then
								local command = newCommand.Text
								local level = newLevel.Text

								newCommand.Text = ""
								newLevel.Text = ""

								table.insert(sets[set],{Command=command,Level=level})
								client.Remote.Send("SaveTableAdd",set,{Command=command,Level=level})
								newEnt.Visible = false
								populate()
							end
						end)

						newCancel.MouseButton1Click:connect(function()
							newCommand.Text = ""
							newLevel.Text = ""
							newEnt.Visible = false
						end)

						Remove.MouseButton1Click:connect(function()
							if selectedEntry and selectedValue and selectedInd then
								table.remove(sets[i],selectedInd)
								client.Remote.Send("SaveTableRemove",set,selectedValue)
								populate()
							end
						end)

						Add.MouseButton1Click:connect(function()
							newCommand.Text = ""
							newLevel.Text = ""
							if newEnt.Visible then
								newEnt.Visible = false
							else
								newEnt.Visible = true
							end
						end)

						Close.MouseButton1Click:connect(function()
							newEnt.Visible = false
							Perms.Visible = false
						end)

						clone.TextButton.MouseButton1Click:connect(function()
							closeAll()
							if Perms.Visible then
								Perms.Visible = false
							else
								Perms.Visible = true
							end
						end)

						populate()
					elseif set=="Leaderstats" then
						local Stats = settingsTab.Leaderstats:clone()
						Stats.Parent = settingsTab
						local tab = Stats.Table
						local Add = Stats.Add
						local Close = Stats.Close
						local Entry = Stats.Entry
						local Remove = Stats.Removeb

						local newEnt = Add.NewEntry
						local newAdd = newEnt.Add
						local newCancel = newEnt.Cancel
						local newStat = newEnt.Nameb
						local newType = newEnt.Type
						local newValue = newEnt.Value

						local selectedEntry
						local selectedValue
						local selectedInd

						local function populate()
							tab:ClearAllChildren()
							local newm = 0
							for k,m in pairs(v) do
								local new = Entry:Clone()
								new.Text = "Name: "..m.Stat.." | Type: "..m.Type.." | Value: "..m.Value
								new.Position = UDim2.new(0,0,0,newm*20)
								new.Visible = true
								new.ZIndex = 2
								new.Parent = tab
								new.MouseButton1Click:connect(function()
									if selectedEntry then
										selectedEntry.BackgroundTransparency = 1
									end
									new.BackgroundTransparency = 0
									selectedEntry = new
									selectedValue = m
									selectedInd = k
								end)
								newm=newm+1
							end
							tab.CanvasSize = UDim2.new(0,0,0,newm*20)
						end

						newAdd.MouseButton1Click:connect(function()
							if newStat.Text~="" and newType.Text~="" then
								local stat = newStat.Text
								local type = newType.Text
								local value = newValue.Text

								newStat.Text = ""
								newType.Text = ""
								newValue.Text = ""

								if type=="" then
									type="String"
								end

								table.insert(sets[set],{Stat=stat,Type=type,Value=value})
								client.Remote.Send("SaveTableAdd",set,{Stat=stat,Type=type,Value=value})
								newEnt.Visible = false
								populate()
							end
						end)

						newCancel.MouseButton1Click:connect(function()
							newStat.Text = ""
							newType.Text = ""
							newValue.Text = ""
							newEnt.Visible = false
						end)

						Remove.MouseButton1Click:connect(function()
							if selectedEntry and selectedValue and selectedInd then
								table.remove(sets[set],selectedInd)
								client.Remote.Send("SaveTableRemove",set,selectedValue)
								populate()
							end
						end)

						Add.MouseButton1Click:connect(function()
							newStat.Text = ""
							newType.Text = ""
							newValue.Text = ""
							if newEnt.Visible then
								newEnt.Visible = false
							else
								newEnt.Visible = true
							end
						end)

						Close.MouseButton1Click:connect(function()
							newEnt.Visible = false
							Stats.Visible = false
						end)

						clone.TextButton.MouseButton1Click:connect(function()
							closeAll()
							if Stats.Visible then
								Stats.Visible = false
							else
								Stats.Visible = true
							end
						end)

						populate()
					else
						local Normal = settingsTab.Normal:clone()
						Normal.Parent = settingsTab
						local tab = Normal.Table
						local Add = Normal.Add
						local Close = Normal.Close
						local Entry = Normal.Entry
						local Remove = Normal.Removeb

						local newEnt = Add.NewEntry
						local newAdd = newEnt.Add
						local newCancel = newEnt.Cancel
						local newValue = newEnt.Value

						local selectedEntry
						local selectedValue
						local selectedInd

						local function populate()
							tab:ClearAllChildren()
							local newm = 0
							for k,m in pairs(v) do
								local new = Entry:Clone()
								new.Text = tostring(m)
								new.Position = UDim2.new(0,0,0,newm*20)
								new.Visible = true
								new.ZIndex = 2
								new.Parent = tab

								new.MouseButton1Click:connect(function()
									if selectedEntry then
										selectedEntry.BackgroundTransparency = 1
									end
									new.BackgroundTransparency = 0
									selectedEntry = new
									selectedValue = m
									selectedInd = k
								end)
								newm=newm+1
							end
							tab.CanvasSize = UDim2.new(0,0,0,newm*20)
						end

						newAdd.MouseButton1Click:connect(function()
							if newValue.Text~="" then
								local value = newValue.Text
								newValue.Text = ""
								table.insert(sets[set],value)
								client.Remote.Send("SaveTableAdd",set,value)
								newEnt.Visible = false
								populate()
							end
						end)

						newCancel.MouseButton1Click:connect(function()
							newValue.Text = ""
							newEnt.Visible = false
						end)

						Remove.MouseButton1Click:connect(function()
							if selectedEntry and selectedValue and selectedInd then
								table.remove(sets[set],selectedInd)
								client.Remote.Send("SaveTableRemove",set,selectedValue)
								populate()
							end
						end)

						Add.MouseButton1Click:connect(function()
							if newEnt.Visible then
								newEnt.Visible = false
							else
								newEnt.Visible = true
							end
						end)

						Close.MouseButton1Click:connect(function()
							newEnt.Visible = false
							Normal.Visible = false
						end)

						clone.TextButton.MouseButton1Click:connect(function()
							closeAll()
							if Normal.Visible then
								Normal.Visible = false
							else
								Normal.Visible = true
							end
						end)

						populate()
					end
					clone.Parent = settingsTab.Settings
					num = num+1
				end
				settingsTab.Settings.CanvasSize = UDim2.new(0,0,0,num*20)
			end
		end
	else
		settings.Visible = false
	end
end


Prompt:
--[[**
	ensures value is an integer

	@param value The value to check against

	@returns True iff the condition is satisfied, false otherwise
**--]]

Response:
function t.integer(value)
	local success, errMsg = t.number(value)
	if not success then
		return false, errMsg or ""
	end
	if value%1 == 0 then
		return true
	else
		return false, string.format("integer expected, got %s", value)
	end
end


Prompt: -- ROBLOX deviation: omitted fileExists and cleanup

Response:
local function toMatchSnapshot(...)
	local args = { ... }
	local this: types.Context = args[1]
	local received: any = args[2]
	local propertiesOrHint: any = args[3]
	local hint: ConfigPath = args[4]

	local matcherName = "toMatchSnapshot"
	local properties

	local length = select("#", ...)
	-- ROBLOX deviation: all the length parameters are one more than upstream because
	-- the this parameter isn't counted in upstream
	if length == 3 and typeof(propertiesOrHint) == "string" then
		hint = propertiesOrHint
	elseif length >= 3 then
		if typeof(propertiesOrHint) ~= "table" or typeof(propertiesOrHint) == nil then
			local options: JestMatcherUtils.MatcherHintOptions = {
				isNot = this.isNot,
				promise = this.promise,
			}
			local printedWithType = printWithType("Expected properties", propertiesOrHint, printExpected)

			if length == 4 then
				options.secondArgument = "hint"
				options.secondArgumentColor = BOLD_WEIGHT

				if propertiesOrHint == nil then
					printedWithType = printedWithType
						.. "\n\nTo provide a hint without properties: toMatchSnapshot('hint')"
				end
			end

			error(AssertionError.new({
				message = matcherErrorMessage(
					matcherHint(matcherName, nil, PROPERTIES_ARG, options),
					"Expected " .. EXPECTED_COLOR("properties") .. " must be an object",
					printedWithType
				),
			}))
		end

		-- Future breaking change: Snapshot hint must be a string
		-- if (arguments.length === 3 && typeof hint !== 'string') {}

		properties = propertiesOrHint
	end

	return _toMatchSnapshot({
		context = this,
		hint = hint,
		isInline = false,
		matcherName = matcherName,
		properties = properties,
		received = received,
	})
end