<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.roblox.com/roblox.xsd" version="4">
	<External>null</External>
	<External>nil</External>
	<Item class="Folder" referent="RBX6e1f616f32ce489fbe9bc9fc0f0e63f9">
		<Properties>
			<string name="Name">TerrainToolPlugin</string>
			<BinaryString name="Tags"></BinaryString>
		</Properties>
		<Item class="ModuleScript" referent="RBXa74b30228b9649d1ae10073f2f55fdbd">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">TerrainBrush</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[--Made by Stickmasterluke

local module = {}

GuiUtilities = require(script.Parent.Libs.GuiUtilities)
CollapsibleTitledSection = require(script.Parent.Libs.CollapsibleTitledSection)
VerticallyScalingListFrame = require(script.Parent.Libs.VerticallyScalingListFrame)
LabeledCheckbox = require(script.Parent.Libs.LabeledCheckbox)
LabeledSlider = require(script.Parent.Libs.LabeledSlider)
StatefulImageButton = require(script.Parent.Libs.StatefulImageButton)
MaterialsListClass = require(script.Parent.MaterialsList)

local on
local kCurrentTool
local plugin
local pluginGui 
local kMaterialsListObject = nil

-- FIXME(dbanks)
-- Clean this up, ugly globals.
local kSizeSliderObj = nil
local kStrengthSliderObj = nil
local kMinSelectionSize = 1
local kMaxSelectionSize = 16
local kSelectionSize = 6
local kStrength = .5

local kShapeButtonRowHeight = 50

local kShapeButtonSphereAssetId = "rbxasset://textures/TerrainTools/icon_shape_sphere.png"
local kShapeButtonCubeAssetId = "rbxasset://textures/TerrainTools/icon_shape_cube.png"

local kBrushShapes = {
	{
		Name = 'Sphere',
		Image = kShapeButtonSphereAssetId,
	},
	{
		Name = 'Box',
		Image = kShapeButtonCubeAssetId,
	},
}

local function MakeNthBrushShapeButton(index, position)
	local config = kBrushShapes[index]

	local shapeButtonObj = StatefulImageButton.new(config.Name, 
		config.Image, 
		UDim2.new(0, GuiUtilities.kShapeButtonSize, 0, GuiUtilities.kShapeButtonSize))
	shapeButtonObj:getButton().AnchorPoint = Vector2.new(0, 0.5)
	shapeButtonObj:getButton().Position = position

	config.ButtonObj = shapeButtonObj
end

local function MakeShapeButtonsRow()
	local row = GuiUtilities.MakeFixedHeightFrame("ShapeButtonRow", kShapeButtonRowHeight)

	local shapeLabel = GuiUtilities.MakeStandardPropertyLabel("Shape") 
	shapeLabel.Parent = row
	shapeLabel.AnchorPoint = Vector2.new(0, 0.5)
	shapeLabel.Position = UDim2.new(0, GuiUtilities.StandardLineLabelLeftMargin, 0, kShapeButtonRowHeight/2)

	MakeNthBrushShapeButton(1, UDim2.new(0, GuiUtilities.StandardLineElementLeftMargin,
		0, kShapeButtonRowHeight/2))
	MakeNthBrushShapeButton(2, UDim2.new(0, GuiUtilities.StandardLineElementLeftMargin + GuiUtilities.kShapeButtonSize + GuiUtilities.kStandardHMargin, 
		0, kShapeButtonRowHeight/2))

	kBrushShapes[1].ButtonObj:getButton().Parent = row
	kBrushShapes[2].ButtonObj:getButton().Parent = row

	return row
end

local function MakeMaterialsSectionWithTitle()
	local vsl = VerticallyScalingListFrame.new("mwt")
	vsl:AddBottomPadding()

	local titleLabel = GuiUtilities.MakeFrameWithSubSectionLabel("Material", "Material")
	vsl:AddChild(titleLabel)

	kMaterialsListObject = MaterialsListClass.new()
	local materialsFrame = kMaterialsListObject:GetMaterialsFrame()
	vsl:AddChild(materialsFrame)

	return vsl:GetFrame()
end

module.FirstTimeSetup = function(theMouse, thePluginGui, theContentFrame)
	mouse = theMouse
	pluginGui = thePluginGui
	contentFrame = theContentFrame
	local changeHistory = game:GetService('ChangeHistoryService')
	local terrain = game.Workspace.Terrain
	local coreGui = game:GetService("CoreGui")
	
	local screenGui = Instance.new("ScreenGui") 
	screenGui.Name = "TerrainBrushGui"

	-- Create top level container.  
	-- It has no title bar.
	local verticallyScalingListFrameObj = VerticallyScalingListFrame.new("BrushFrame")
	local verticallyScalingListFrame = verticallyScalingListFrameObj:GetFrame()
	verticallyScalingListFrame.Parent = contentFrame
	verticallyScalingListFrame.Visible = false

	-- Top level container has 3 collapsible sections:
	-- 1) Brush Settings.
	local brushSettingsObj = CollapsibleTitledSection.new('BrushSettings', 
		'Brush Settings', 
		true, 
		true)
	GuiUtilities.MakeFrameAutoScalingList(brushSettingsObj:GetContentsFrame())

	kSizeSliderObj = LabeledSlider.new("Size", "Size", kMaxSelectionSize, kSelectionSize)
	local sizeSliderRow =kSizeSliderObj:GetFrame()

	kStrengthSliderObj = LabeledSlider.new("Strength", "Strength", 101, kStrength*100)
	local strengthSliderRow = kStrengthSliderObj:GetFrame()

	local shapeButtonsRow = MakeShapeButtonsRow()

	GuiUtilities.AddStripedChildrenToListFrame(brushSettingsObj:GetContentsFrame(), 
		{sizeSliderRow, strengthSliderRow, shapeButtonsRow})
	
	verticallyScalingListFrameObj:AddChild(brushSettingsObj:GetSectionFrame())

	-- 2) Materials Settings.	
	local materialSettingsObj = CollapsibleTitledSection.new("MaterialSettings", 
		"Material Settings",
		true, 
		true)
	GuiUtilities.MakeFrameAutoScalingList(materialSettingsObj:GetContentsFrame())

	local autoColorObj= LabeledCheckbox.new("Auto", 'Auto')

	-- Variable is *not* local, needs to live inside callback below.
	local materialSectionWithTitle = MakeMaterialsSectionWithTitle()

	GuiUtilities.AddStripedChildrenToListFrame(materialSettingsObj:GetContentsFrame(), 
		{autoColorObj:GetFrame(), materialSectionWithTitle})
		
	autoColorObj:SetValueChangedFunction(function()
		-- When 'auto' material selection is on, hide material selection panel.
		materialSectionWithTitle.Visible = (not autoColorObj:GetValue())
	end)

	verticallyScalingListFrameObj:AddChild(materialSettingsObj:GetSectionFrame())

	-- 3) Advanced settings.
	local advancedSettingsObj = CollapsibleTitledSection.new("AdvancedSettings", 
		"Advanced",
		true, 
		true)
	GuiUtilities.MakeFrameAutoScalingList(advancedSettingsObj:GetContentsFrame())

	local planeLockObj = LabeledCheckbox.new("PLock", 'Plane Lock')
	local snapToGridObj = LabeledCheckbox.new("STG", 'Snap to Grid')
	local ignoreWaterObj = LabeledCheckbox.new("IGW", 'Ignore Water', true)
	planeLockObj:SetValueChangedFunction(function(value)
		if not planeLockObj:GetValue() then
			clearGrid()
		end
	end)

	GuiUtilities.AddStripedChildrenToListFrame(advancedSettingsObj:GetContentsFrame(), 
		{planeLockObj:GetFrame(), 
		 snapToGridObj:GetFrame(),
		 ignoreWaterObj:GetFrame()})

	verticallyScalingListFrameObj:AddChild(advancedSettingsObj:GetSectionFrame())





	local userInput = game:GetService('UserInputService')
	local prevCameraType = game.Workspace.CurrentCamera.CameraType

	--SUB SETTINGS--	(Non-userfacing Settings)
	local resolution = 4			--This is the size of voxels on Roblox. Why is this a variable? ;)
	local clickThreshold = .1
	local toolTipShowTime = 3.5
	local materialsTable = MaterialsListClass.MaterialsTable
	----------------

	----SETTINGS----	(Interface Settings)
	local brushShape = 'Sphere'
	----------------

	----Variables----
	local forceMaterial = nil
	local nearMaterial = nil
	local selectionPart = nil
	local selectionObject = nil
	local gridLineParts = {}
	local currentLoopTag = nil
	local lastMainPoint = Vector3.new(0, 0, 0)
	local click = false
	local firstOperation = tick()
	local downKeys = {}
	local lastPlanePoint = Vector3.new(0, 0, 0)
	local lastNormal = Vector3.new(0, 1, 0)
	local lastCursorDistance = 300
	local one256th = 1/256							--This should later be replaced with 0 once smooth terrain doesn't aproximate 1/256 to 0. This is causing small occupancies to become air
	local toolTip1Change = nil
	local toolTip2Change = nil
	local materialAir = Enum.Material.Air
	local materialWater = Enum.Material.Water
	local ceil = math.ceil
	local floor = math.floor
	local abs = math.abs
	local min = math.min
	local max = math.max
	local sqrt = math.sqrt
	local sin = math.sin
	local cos = math.cos
	local pi = math.pi
	
	---------------
	
	kSizeSliderObj:SetValueChangedFunction(function(newValue)
		kSelectionSize = newValue
		if selectionPart then
			selectionPart.Size = Vector3.new(1, 1, 1) * kSelectionSize * resolution + Vector3.new(.1, .1, .1)
		end
	end)

	kStrengthSliderObj:SetValueChangedFunction(function(newValue)
		kStrength = (newValue - 1) / 100
		if selectionObject then
			selectionObject.SurfaceTransparency = .95 - kStrength * .3
		end
	end)

	function setBrushShape(newBrushShape)
		brushShape = newBrushShape
		for _,v in ipairs(kBrushShapes) do
			v.ButtonObj:setSelected(newBrushShape == v.Name)
		end
		clearSelection()
	end

	for _,v in ipairs(kBrushShapes) do
		v.ButtonObj:getButton().MouseButton1Down:connect(function()
			setBrushShape(v.Name)
		end)
	end

	-- If this diabled condition is true, fix checkbox in disabled state
	-- with given override.
	-- Else fix checkbox in enabled state.
	local function updateCheckboxDisabledStateWithOverride(checkboxObj, 
		disabledCondition, 
		disabledOverride)
		if (disabledCondition) then 
			checkboxObj:DisableWithOverrideValue(disabledOverride)
		else
			checkboxObj:SetDisabled(false)
		end
	end

	local function updateUsabilityLocks()
		if kCurrentTool then
			updateCheckboxDisabledStateWithOverride(snapToGridObj, 
				kCurrentTool.UsesMaterials and kMaterialsListObject:GetCurrentMaterialSelection().forceSnapToGrid, 
				true)

			updateCheckboxDisabledStateWithOverride(planeLockObj, 
				kCurrentTool.DisablesPlaneLock, 
				true)

			updateCheckboxDisabledStateWithOverride(autoColorObj, 
				kCurrentTool.DisablesAutoColor, 
					kCurrentTool.Name ~= 'Paint')

			updateCheckboxDisabledStateWithOverride(ignoreWaterObj, 
				(kMaterialsListObject:GetCurrentMaterialSelection().forceIgnoreWater and not autoColorObj:GetValue()) or kCurrentTool.Name == 'Smooth', 
				(kCurrentTool.Name ~= 'Smooth' and kMaterialsListObject:GetCurrentMaterialSelection().forceIgnoreWaterTo))

			autoColorObj:GetFrame().Visible = kCurrentTool.UsesMaterials
			materialSettingsObj:GetSectionFrame().Visible = not not kCurrentTool.UsesMaterials
		end
	end

	kMaterialsListObject:SetCallbackOnSelectionChanged(updateUsabilityLocks)

	-------------

	do
		local runService = game:GetService('RunService').RenderStepped
		function quickWait(waitTime)
			if not waitTime then
				runService:wait()
			elseif waitTime < .033333 then
				local startTick = tick()
				runService:wait()
				local delta = tick() - startTick
				if delta <= waitTime * .5 then
					quickWait(waitTime - delta)
				end
			else
				wait(waitTime)
			end
		end
	end

	function deepCast(origin, endPoint, ignoreList, filterFunction, cubeTerrain)
		local ray = Ray.new(origin, endPoint - origin)
		local hit, pos, normal, material = game.Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList, cubeTerrain)
		if hit and filterFunction(hit) then
			table.insert(ignoreList, hit)
			return deepCast(pos, endPoint, ignoreList, filterFunction, cubeTerrain)
		else
			return hit, pos, normal, material
		end
	end

	function clearSelection()
		if selectionObject then
			selectionObject:Destroy()
			selectionObject = nil
		end
		if selectionPart then
			selectionPart:Destroy()
			selectionPart = nil
		end
	end

	function clearGrid()
		for i, v in pairs(gridLineParts) do
			if v then
				v:Destroy()
			end
			gridLineParts[i] = nil
		end
	end

	function drawGrid(point, normal, transparency, color)
		local transparency = transparency or .95
		local color = BrickColor.new(color or 'Institutional white')--'Pastel light blue')
		local gridCellSize = kSelectionSize * resolution
		local gridSize = 10
		local baseCframe = CFrame.new(point, point + normal)
		local normalSpase = CFrame.new(Vector3.new(0, 0, 0), normal):pointToObjectSpace(point)
		local roundedNormalOffset = (Vector3.new((normalSpase.x / gridCellSize) % 1, (normalSpase.y / gridCellSize) % 1, 0) - Vector3.new(.5, .5, 0)) * -gridCellSize
		for u = 1, gridSize do
			local linePart = gridLineParts[u]
			if not linePart then
				linePart = Instance.new('Part')
				linePart.Transparency = 1
				linePart.TopSurface = 'Smooth'
				linePart.BottomSurface = 'Smooth'
				linePart.Anchored = true
				linePart.CanCollide = false
				local selectionBox = Instance.new('SelectionBox')
				selectionBox.Color = color
				selectionBox.Transparency = transparency
				selectionBox.Adornee = linePart
				selectionBox.Parent = linePart
				linePart.Parent = screenGui
				gridLineParts[u] = linePart
			elseif linePart.SelectionBox.Transparency ~= transparency or linePart.SelectionBox.Color ~= color then
				linePart.SelectionBox.Transparency = transparency
				linePart.SelectionBox.Color = color
			end
			local percent = (u - 1) / (gridSize - 1)
			linePart.Size = Vector3.new(gridCellSize * gridSize * sin(math.acos(percent * 1.8 - .9)), 0, 0)
			linePart.CFrame = baseCframe * CFrame.new(0, (percent - .5) * (gridSize - 1) * gridCellSize, 0) * CFrame.new(roundedNormalOffset)
		end
		for u = 1, gridSize do
			local linePart = gridLineParts[gridSize + u]
			if not linePart then
				linePart = Instance.new('Part')
				linePart.Transparency = 1
				linePart.TopSurface = 'Smooth'
				linePart.BottomSurface = 'Smooth'
				linePart.Anchored = true
				linePart.CanCollide = false
				local selectionBox = Instance.new('SelectionBox')
				selectionBox.Color = color
				selectionBox.Transparency = transparency
				selectionBox.Adornee = linePart
				selectionBox.Parent = linePart
				linePart.Parent = screenGui
				gridLineParts[gridSize + u] = linePart
			elseif linePart.SelectionBox.Transparency ~= transparency or linePart.SelectionBox.Color ~= color then
				linePart.SelectionBox.Transparency = transparency
				linePart.SelectionBox.Color = color
			end
			local percent = (u - 1) / (gridSize - 1)
			linePart.Size = Vector3.new(0, gridCellSize * gridSize * sin(math.acos(percent * 1.8 - .9)), 0)
			linePart.CFrame = baseCframe * CFrame.new((percent - .5) * (gridSize - 1) * gridCellSize, 0, 0) * CFrame.new(roundedNormalOffset)
		end
	end

	local function getCell(list, x, y, z, materialList)
		-- only include materialsList if you want to ignore water
		return (materialList and materialList[x] and materialList[x][y] and materialList[x][y][z]) == materialWater and 0
				or list and list[x] and list[x][y] and list[x][y][z]
	end

	local function getNeighborOccupancies(list, x, y, z, materialsList, includeSelf)
		--only include materialsList if you want to ignore water
		local fullNeighbor = false
		local emptyNeighbor = false
		local neighborOccupancies = includeSelf and getCell(list, x, y, z, materialsList) or 0
		local totalNeighbors = includeSelf and 1 or 0
		local nearMaterial = kMaterialsListObject:GetCurrentMaterialSelection().enum
		for axis = 1, 3 do
			for offset = -1, 1, 2 do
				local neighbor = nil
				local neighborMaterial = nil
				if axis == 1 then
					neighbor = list[x + offset] and list[x + offset][y][z]
				elseif axis == 2 then
					neighbor = list[x][y + offset] and list[x][y + offset][z]
				elseif axis == 3 then
					neighbor = list[x][y][z + offset]
				end
				if neighbor then
					if materialsList then
						if axis == 1 then
							neighborMaterial = materialsList[x + offset] and materialsList[x + offset][y][z]
						elseif axis == 2 then
							neighborMaterial = materialsList[x][y + offset] and materialsList[x][y + offset][z]
						elseif axis == 3 then
							neighborMaterial = materialsList[x][y][z + offset]
						end
						if neighborMaterial == materialWater then
							neighbor = 0
						end
					end
					if neighbor >= 1 then
						fullNeighbor = true
					end
					if neighbor <= 0 then
						emptyNeighbor = true
					end
					totalNeighbors = totalNeighbors + 1
					neighborOccupancies = neighborOccupancies + neighbor
				end
			end
		end
		return neighborOccupancies / (totalNeighbors ~= 0 and totalNeighbors or getCell(list, x, y, z, materialsList)), fullNeighbor, emptyNeighbor
	end

	local function round(n)
		return floor(n + .5)
	end

	function findFace()
		local cameraLookVector = game.Workspace.CurrentCamera.CoordinateFrame.lookVector
		--[[local absx = abs(cameraLookVector.x)		--this code is for 90 plane locking
		local absy = abs(cameraLookVector.y)
		local absz = abs(cameraLookVector.z)
		if absy >= absx and absy >= absz then	--preference towards y axis planes
			return Vector3.new(0, cameraLookVector.y / absy, 0)
		elseif absx >= absz then
			return	Vector3.new(cameraLookVector.x / absx, 0, 0)
		end
		return Vector3.new(0, 0, cameraLookVector.z / absz)]]

		return Vector3.new(round(cameraLookVector.x), round(cameraLookVector.y), round(cameraLookVector.z)).unit	--this code is for 45 degree plane locking
	end

	function lineToPlaneIntersection(linePoint, lineDirection, planePoint, planeNormal)
		local denominator = lineDirection:Dot(planeNormal)
		if denominator == 0 then
			return linePoint
		end
		local distance = ((planePoint - linePoint):Dot(planeNormal)) / denominator
		return linePoint + lineDirection * distance
	end

	function operation(centerPoint)
		local desiredMaterial = autoColorObj:GetValue() and nearMaterial or kMaterialsListObject:GetCurrentMaterialSelection().enum

		local radius = kSelectionSize * .5 * resolution
		local minBounds = Vector3.new(
			floor((centerPoint.x - radius) / resolution) * resolution,
			floor((centerPoint.y - radius) / resolution) * resolution,
			floor((centerPoint.z - radius) / resolution) * resolution)
		local maxBounds = Vector3.new(
			ceil((centerPoint.x + radius) / resolution) * resolution,
			ceil((centerPoint.y + radius) / resolution) * resolution,
			ceil((centerPoint.z + radius) / resolution) * resolution)
		local region = Region3.new(minBounds, maxBounds)

		local materials, occupancies = terrain:ReadVoxels(region, resolution)
		local toolBrushOperation = nil
		for _, toolModule in pairs(kCurrentTool.Modules) do
			if toolModule.BrushOperation then
				toolBrushOperation = toolModule.BrushOperation
				break
			end
		end
		if toolBrushOperation then
			local middle = materials[ceil(#materials * .5)]	--This little section of code sets nearMaterial to middle of matrix
			if middle then	--dig X
				local middle = middle[ceil(#middle * .5)]
				if middle then	--dig Y
					local middle = middle[ceil(#middle * .5)]
					if middle and middle ~= materialAir and middle ~= materialWater then	--dig Z
						nearMaterial = middle
						desiredMaterial = autoColorObj:GetValue() and nearMaterial or desiredMaterial
					end
				end
			end

			toolBrushOperation(centerPoint, materials, occupancies, resolution, kSelectionSize, kStrength, desiredMaterial, brushShape, minBounds, maxBounds)
		else
			local airFillerMaterial = materialAir
			local waterHeight = 0
			if ignoreWaterObj:GetValue() and (kCurrentTool.Name == 'Erode' or kCurrentTool.Name == 'Subtract') then
				--[[local centerPointCell = Vector3.new(floor((centerPoint.x+.5)/resolution) * resolution, floor((centerPoint.y+.5)/resolution) * resolution, floor((centerPoint.z+.5)/resolution) * resolution)
				local sampleRegion = Region3.new(centerPointCell - Vector3.new(resolution,resolution,resolution), centerPointCell + Vector3.new(resolution,resolution,resolution))
				local sampleMaterials, sampleOccupancies = terrain:ReadVoxels(sampleRegion, resolution)]]
				
				for ix,vx in ipairs(materials) do
					for iy,vy in ipairs(vx) do
						for iz, vz in ipairs(vy) do
							if vz == materialWater then
								airFillerMaterial = materialWater
								if iy > waterHeight then
									waterHeight = iy
								end
							end
						end
					end
				end
			end

			for ix, vx in ipairs(occupancies) do
				local cellVectorX = minBounds.x + (ix - .5) * resolution - centerPoint.x

				for iy, vy in pairs(vx) do
					local cellVectorY = minBounds.y + (iy - .5) * resolution - centerPoint.y

					for iz, cellOccupancy in pairs(vy) do
						local cellVectorZ = minBounds.z + (iz - .5) * resolution - centerPoint.z

						local cellMaterial = materials[ix][iy][iz]
						local distance = sqrt(cellVectorX * cellVectorX + cellVectorY * cellVectorY + cellVectorZ * cellVectorZ)
	
						local magnitudePercent = 1
						local brushOccupancy = 1
						if brushShape == 'Sphere' then
							magnitudePercent = cos(min(1, distance / (radius + resolution * .5)) * pi * .5)
							brushOccupancy = max(0, min(1, (radius + .5 * resolution - distance) / resolution))
						elseif brushShape == 'Box' then
							if not snapToGridObj:GetValue() then
								local xOutside = 1 - max(0, abs(cellVectorX / resolution) + .5 - kSelectionSize * .5)
								local yOutside = 1 - max(0, abs(cellVectorY / resolution) + .5 - kSelectionSize * .5)
								local zOutside = 1 - max(0, abs(cellVectorZ / resolution) + .5 - kSelectionSize * .5)
								brushOccupancy = xOutside * yOutside * zOutside
							end
						end

						if cellMaterial ~= materialAir and cellMaterial ~= materialWater and cellMaterial ~= nearMaterial then
							nearMaterial = cellMaterial
							if autoColorObj:GetValue() then
								desiredMaterial = nearMaterial
							end
						end

						if ignoreWaterObj:GetValue() and cellMaterial == materialWater then
							cellMaterial = materialAir
							cellOccupancy = 0
						end
						local airFillerMaterial = waterHeight >= iy and airFillerMaterial or materialAir
	
						if kCurrentTool.Name == 'Add' then
							if kSelectionSize <= 2 then
								if brushOccupancy >= .5 then
									if cellMaterial == materialAir or cellOccupancy <= 0 then
										materials[ix][iy][iz] = desiredMaterial
									end
									occupancies[ix][iy][iz] = 1
								end 
							else
								if brushOccupancy > cellOccupancy then
									occupancies[ix][iy][iz] = brushOccupancy
								end
								if brushOccupancy >= .5 and cellMaterial == materialAir then
									materials[ix][iy][iz] = desiredMaterial
								end
							end
						elseif kCurrentTool.Name == 'Subtract' then
							if cellMaterial ~= materialAir then
								if kSelectionSize <= 2 then
									if brushOccupancy >= .5 then
										occupancies[ix][iy][iz] = airFillerMaterial == materialWater and 1 or 0
										materials[ix][iy][iz] = airFillerMaterial
									end
								else
									local desiredOccupancy = max(0,1 - brushOccupancy)
									if desiredOccupancy < cellOccupancy then
										if desiredOccupancy <= one256th then
											occupancies[ix][iy][iz] = airFillerMaterial == materialWater and 1 or 0
											materials[ix][iy][iz] = airFillerMaterial
										else
											occupancies[ix][iy][iz] = min(cellOccupancy, desiredOccupancy)
										end
									end
								end
							end
						elseif kCurrentTool.Name == 'Grow' then
							if brushOccupancy >= .5 then --working on
								local desiredOccupancy = cellOccupancy
								local neighborOccupancies, fullNeighbor, emptyNeighbor = getNeighborOccupancies(occupancies, ix, iy, iz, ignoreWaterObj:GetValue() and materials)
								if cellOccupancy > 0 or fullNeighbor then	--problem if selection size is small.
									desiredOccupancy = desiredOccupancy + neighborOccupancies * (kStrength + .1) * .25 * brushOccupancy * magnitudePercent
								end
								if cellMaterial == materialAir and desiredOccupancy > 0 then
									materials[ix][iy][iz] = desiredMaterial
								end
								if desiredOccupancy ~= cellOccupancy then
									occupancies[ix][iy][iz] = desiredOccupancy
								end
							end
						elseif kCurrentTool.Name == 'Erode' then
							if cellMaterial ~= materialAir then
								local flippedBrushOccupancy = 1 - brushOccupancy
								if flippedBrushOccupancy <= .5 then
									local desiredOccupancy = cellOccupancy
									local emptyNeighbor = false
									local neighborOccupancies = 6
									for axis = 1, 3 do
										for offset = -1, 1, 2 do
											local neighbor = nil
											local neighborMaterial = nil
											if axis == 1 then
												neighbor = occupancies[ix + offset] and occupancies[ix + offset][iy][iz]
												neighborMaterial = materials[ix + offset] and materials[ix + offset][iy][iz]
											elseif axis == 2 then
												neighbor = occupancies[ix][iy + offset] and occupancies[ix][iy + offset][iz]
												neighborMaterial = materials[ix][iy + offset] and materials[ix][iy + offset][iz]
											elseif axis == 3 then
												neighbor = occupancies[ix][iy][iz + offset]
												neighborMaterial = materials[ix][iy][iz + offset]
											end
											if neighbor then
												if ignoreWaterObj:GetValue() and neighborMaterial == materialWater then
													neighbor = 0
												end
												if neighbor <= 0 then
													emptyNeighbor = true
												end
												neighborOccupancies = neighborOccupancies - neighbor
											end
										end
									end
									if cellOccupancy < 1 or emptyNeighbor then
										desiredOccupancy = max(0,desiredOccupancy - (neighborOccupancies / 6) * (kStrength + .1) * .25 * brushOccupancy * magnitudePercent)
									end
									if desiredOccupancy <= one256th then
										occupancies[ix][iy][iz] = airFillerMaterial == materialWater and 1 or 0
										materials[ix][iy][iz] = airFillerMaterial
									else
										occupancies[ix][iy][iz] = desiredOccupancy
									end
								end
							end
						elseif kCurrentTool.Name == 'Paint' then
							if brushOccupancy > 0 and cellOccupancy > 0 then
								materials[ix][iy][iz] = desiredMaterial
							end
						end
					end
				end
			end
		end
		terrain:WriteVoxels(region, resolution, materials, occupancies)
	end

	
	module.On = function(theTool)
		kCurrentTool = theTool
		screenGui.Parent = coreGui
		verticallyScalingListFrame.Visible = true
		on = true

		updateUsabilityLocks()

		local loopTag = {}	--using table as a unique value for debouncing
		currentLoopTag = loopTag

		while currentLoopTag and currentLoopTag == loopTag do
			local t = tick()
			local radius = kSelectionSize * .5 * resolution
			local cameraPos = mouse.Origin.p
			
			local ignoreModel = nil
			if game.Players.LocalPlayer and game.Players.LocalPlayer.Character then
				ignoreModel = game.Players.LocalPlayer.Character
			end
			local mouseRay = Ray.new(cameraPos, mouse.UnitRay.Direction*10000)
			local hitObject, mainPoint = game.Workspace:FindPartOnRay(mouseRay, ignoreModel, false, ignoreWaterObj:GetValue())

			if kCurrentTool.Name == 'Add' then
				mainPoint = mainPoint - mouse.UnitRay.Direction * .05
			elseif kCurrentTool.Name == 'Subtract' or kCurrentTool.Name == 'Paint' or kCurrentTool.Name == 'Grow' then
				mainPoint = mainPoint + mouse.UnitRay.Direction * .05
			end

			if mouse.Target == nil then	--cage the cursor so that it does not fly away
				mainPoint = cameraPos + mouse.UnitRay.Direction * lastCursorDistance --limits the distance of the mainPoint if the mouse is not hitting an object
			end

			if not mouseDown or click then
				lastPlanePoint = mainPoint
				lastNormal = findFace()
			end

			if planeLockObj:GetValue() then
				mainPoint = lineToPlaneIntersection(cameraPos, mouse.UnitRay.Direction, lastPlanePoint, lastNormal)
			end

			if snapToGridObj:GetValue() then
				local snapOffset = Vector3.new(1, 1, 1) * (radius % resolution)		--in studs
				local tempMainPoint = (mainPoint - snapOffset) / resolution + Vector3.new(.5, .5, .5)	--in voxels
				mainPoint = Vector3.new(floor(tempMainPoint.x), floor(tempMainPoint.y), floor(tempMainPoint.z)) * resolution + snapOffset
			end

			if mouseDown then
				if click then
					firstOperation = t
					lastMainPoint = mainPoint
				end
				
				if click or t > firstOperation + clickThreshold then
					click = false
					if downKeys[Enum.KeyCode.LeftAlt] or downKeys[Enum.KeyCode.RightAlt] then
						--pick color
						local function filterNonTerrain(thing)
							if thing and thing == terrain then
								return false
							end
							return true
						end
						local hit, hitPosition, normal, foundMaterial = deepCast(cameraPos, cameraPos + mouse.UnitRay.Direction*10000, {}, filterNonTerrain, true)
						if hit then
							kMaterialsListObject:SetMaterialSelectionUsingEnum(foundMaterial)
						end
					else
						local difference = mainPoint - lastMainPoint
						local dragDistance = (difference).magnitude
						local crawlDistance = radius * .5			--Maybe adjustable setting? Considering using a different method of crawling, with a percent rather than a finite distance.
						if dragDistance > crawlDistance then
							local differenceVector = difference.unit
							local dragDistance = min(dragDistance, crawlDistance * 2 + 20)	--limiting this so that it does not attempt too many operations within a single drag.
							local samples = ceil(dragDistance / crawlDistance - .1)
							for i = 1, samples do
								operation(lastMainPoint + differenceVector * dragDistance * (i / samples))
							end
							mainPoint = lastMainPoint + differenceVector * dragDistance
						else
							operation(mainPoint)
						end
						lastMainPoint = mainPoint
					end
				end
			end

			if not selectionPart then
				selectionPart = Instance.new('Part')
				selectionPart.Name = 'SelectionPart'
				selectionPart.Transparency = 1
				selectionPart.TopSurface = 'Smooth'
				selectionPart.BottomSurface = 'Smooth'
				selectionPart.Anchored = true
				selectionPart.CanCollide = false
				selectionPart.Size = Vector3.new(1, 1, 1) * kSelectionSize * resolution + Vector3.new(.1, .1, .1)
				selectionPart.Parent = screenGui
			end
			if not selectionObject then
				selectionObject = Instance.new(brushShape == 'Sphere' and 'SelectionSphere' or 'SelectionBox')
				selectionObject.Name = 'SelectionObject'
				selectionObject.Color = BrickColor.new('Toothpaste')
				selectionObject.SurfaceTransparency = .95 - kStrength * .3
				selectionObject.SurfaceColor = BrickColor.new('Toothpaste')
				selectionObject.Adornee = selectionPart
				selectionObject.Parent = selectionPart
			end
			
			if not userInput.TouchEnabled or mouseDown then
				selectionPart.CFrame = CFrame.new(mainPoint)
				
				if planeLockObj:GetValue() then
					local mainPointIntersect = lineToPlaneIntersection(mainPoint, mouse.UnitRay.Direction, lastPlanePoint, lastNormal)	--we need to get this otherwise the plane can shift whiel drawing
					drawGrid(mainPointIntersect, lastNormal, mouseDown and .8)
				end
			end

			lastCursorDistance = max(20 + kSelectionSize * resolution * 1.5,(mainPoint - cameraPos).magnitude)

			quickWait()
		end
	end
	
	module.Off = function()
		currentLoopTag = nil
		screenGui.Parent = script.Parent
		verticallyScalingListFrame.Visible = false
		on = false
		
		clearSelection()
		clearGrid()
		mouseDown = false
	end

	--Touch controls
	local fingerTouches = {}
	local NumUnsunkTouches = 0

	local StartingDiff = nil
	local startingSelectionSize = nil

	local function OnTouchBegan(input, processed)
		fingerTouches[input] = processed
		if not processed then
			click = true
			NumUnsunkTouches = NumUnsunkTouches + 1
		end
	end

	local function OnTouchChanged(input, processed)
		if fingerTouches[input] == nil then
			fingerTouches[input] = processed
			if not processed then
				NumUnsunkTouches = NumUnsunkTouches + 1
			end
		end

		if NumUnsunkTouches == 1 then
			if fingerTouches[input] == false then
				mouseDown = true
			end
		else
			mouseDown = 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 startingSelectionSize then
					local scale = difference/max(0.01, StartingDiff)
					kSelectionSize = max(kMinSelectionSize, min(kMaxSelectionSize, startingSelectionSize/scale))
					kSizeSliderObj:SetValue(kSelectionSize)
				else
					StartingDiff = difference
					startingSelectionSize = kSizeSliderObj:GetValue()
				end
			end
		else
			StartingDiff = nil
			startingSelectionSize = nil
		end
	end

	local function OnTouchEnded(input, processed)
		if fingerTouches[input] == false then
			if NumUnsunkTouches == 1 then
				mouseDown = false
			elseif NumUnsunkTouches == 2 then
				StartingDiff = nil
				startingSelectionSize = nil
				mouseDown = true
			end
		end

		if fingerTouches[input] ~= nil and fingerTouches[input] == false then
			NumUnsunkTouches = NumUnsunkTouches - 1
		end
		fingerTouches[input] = nil
	end
	
	-- Input Handling
	userInput.InputBegan:connect(function(event, soaked)
		downKeys[event.KeyCode] = true
		if event.UserInputType == Enum.UserInputType.MouseButton1 and not soaked and on then
			mouseDown = true
			click = true
		elseif event.UserInputType == Enum.UserInputType.Touch and on then
			OnTouchBegan(event, soaked)
		end
	end)
	
	userInput.InputChanged:connect(function(input, processed)
		if input.UserInputType == Enum.UserInputType.Touch then
			OnTouchChanged(input, processed)
		end
	end)
	
	userInput.InputEnded:connect(function(event, soaked)
		downKeys[event.KeyCode] = nil
		if event.UserInputType == Enum.UserInputType.MouseButton1 and mouseDown then
			mouseDown = false
			if changeHistory then
				changeHistory:SetWaypoint('Terrain '..kCurrentTool.Name)
			end
		elseif event.UserInputType == Enum.UserInputType.Touch then
			OnTouchEnded(event, soaked)
		end
	end)

	function scrollwheel(change)
		if on then
			if downKeys[Enum.KeyCode.LeftShift] or downKeys[Enum.KeyCode.RightShift] then
				kSelectionSize = max(kMinSelectionSize, min(kMaxSelectionSize, kSelectionSize + change))
				kSizeSliderObj:SetValue(kSelectionSize)
			end
			if downKeys[Enum.KeyCode.LeftControl] or downKeys[Enum.KeyCode.RightControl] then
				kStrength = max(0, min(1, kStrength + change * (1/(kMaxSelectionSize-kMinSelectionSize))))
				kStrengthSliderObj:SetValue(round(kStrength * 100 + 1))
			end
		end
	end
	
	mouse.WheelForward:connect(function()
		scrollwheel(1)
	end)
	
	mouse.WheelBackward:connect(function()
		scrollwheel(-1)
	end)

	setBrushShape(brushShape)
	kMaterialsListObject:SetMaterialSelection(kMaterialsListObject:GetCurrentMaterialSelection())

	-- Reset keyboard status on lost focus as key release may never come blocked by popups etc.
	userInput.WindowFocusReleased:connect(function()
		downKeys = {}
	end)
end


return module]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX6ccbe8d0dd544d31944454c4160f272d">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">TerrainRegionEditor</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[--Made by Stickmasterluke
local module = {}

--[[todo:
	-fill region (with material, action)
	-optimize resize scaling code more
]]
local GuiUtilities = require(script.Parent.Libs.GuiUtilities)
local CustomTextButton = require(script.Parent.Libs.CustomTextButton)
local VerticallyScalingListFrame = require(script.Parent.Libs.VerticallyScalingListFrame)
local CollapsibleTitledSection = require(script.Parent.Libs.CollapsibleTitledSection)
local LabeledCheckbox = require(script.Parent.Libs.LabeledCheckbox)
local ImageButtonWithText = require(script.Parent.Libs.ImageButtonWithText)

local MaterialsListClass = require(script.Parent.MaterialsList)

local Terrain = workspace:WaitForChild('Terrain', 86400) or workspace:WaitForChild('Terrain')
while not Terrain.IsSmooth do
	Terrain.Changed:wait()
end

local on = false
local currentTool = 'Create'

local pluginGui = nil
local contentFrame = nil
local terrainRegionFrame = nil
local fillFrame = nil

local kRegionButtonOuterWidth = 45
local kRegionButtonOuterHeight = 62
local kRegionButtonBorderSize = 0
local kRegionButtonActualWidth = (kRegionButtonOuterWidth - 2 * kRegionButtonBorderSize)
local kRegionButtonActualHeight = (kRegionButtonOuterHeight - 2 * kRegionButtonBorderSize)

local kRegionButtonActualSizeUDim2 = UDim2.new(0, kRegionButtonActualWidth, 0, kRegionButtonActualHeight)
local kRegionButtonOuterSizeUDim2 = UDim2.new(0, kRegionButtonOuterWidth, 0, kRegionButtonOuterHeight)

local kFillConfirmButtonWidth = 44
local kFillConfirmButtonHeight = 25
local kMaterialsListObject = nil
local kBottomButtonMargin = 10

local kRegionModeButtonConfigs = 
{
	Select = {
		Text = "Select",
		Name = "ButtonSelect",
		Icon = "rbxasset://textures/TerrainTools/icon_regions_select.png",
		LayoutOrder = 1,
		Mode = 'Select',
		Tool = 'Resize',
	}, 
	Move = {
		Text = "Move",
		Name = "ButtonMove",
		Icon = "rbxasset://textures/TerrainTools/icon_regions_move.png",
		LayoutOrder = 2,
		Mode = 'Edit',
		Tool = 'Move',
	}, 
	Resize = {
		Text = "Resize",
		Name = "ButtonResize",
		Icon = "rbxasset://textures/TerrainTools/icon_regions_resize.png",
		LayoutOrder = 3,
		Mode = 'Edit',
		Tool = 'Resize',
	}, 
	Rotate = {
		Text = "Rotate",
		Name = "ButtonRotate",
		Icon = "rbxasset://textures/TerrainTools/icon_regions_rotate.png",
		LayoutOrder = 4,
		Mode = 'Edit',
		Tool = 'Rotate',
	}, 
}

local kRegionOperationButtonConfigs = 
{
	Copy = {
		Text = "Copy",
		Name = "ButtonCopy",
		Icon = "rbxasset://textures/TerrainTools/icon_regions_copy.png",
		LayoutOrder = 5,
	}, 
	Paste = {
		Text = "Paste",
		Name = "ButtonPaste",
		Icon = "rbxasset://textures/TerrainTools/icon_regions_paste.png",
		LayoutOrder = 6,
	}, 
	Delete = {
		Text = "Delete",
		Name = "ButtonDelete",
		Icon = "rbxasset://textures/TerrainTools/icon_regions_delete.png",
		LayoutOrder = 7,
	}, 
	Fill = {
		Text = "Fill",
		Name = "ButtonFill",
		Icon = "rbxasset://textures/TerrainTools/icon_regions_fill.png",
		LayoutOrder = 8,
	}, 
}

local function createRegionButton(parent, buttonConfig)
	local buttonObj = ImageButtonWithText.new(buttonConfig.Name, 
		buttonConfig.LayoutOrder, 
		buttonConfig.Icon, 
		buttonConfig.Text, 
		kRegionButtonActualSizeUDim2,
		UDim2.new(0,45,0,36), 
		UDim2.new(0,0,0,0),
		UDim2.new(1,0,0,22), 
		UDim2.new(0,0,1,-22))

	buttonObj:getButton().Parent = parent
	buttonConfig.ButtonObj = buttonObj
end

function MakeButtonGridInFrameWithTitle(buttonConfigs, name, title) 
	local vsf = VerticallyScalingListFrame.new(name)
	vsf:AddBottomPadding()

	local labelFrame = GuiUtilities.MakeFrameWithSubSectionLabel("Label", title)
	vsf:AddChild(labelFrame)

	local buttonListFrame = GuiUtilities.MakeFixedHeightFrame("grid", kRegionButtonOuterHeight)
	vsf:AddChild(buttonListFrame)
	buttonListFrame.BackgroundTransparency = 1

	local uiGridLayout = Instance.new("UIGridLayout")
	uiGridLayout.CellSize = kRegionButtonOuterSizeUDim2
	uiGridLayout.CellPadding = UDim2.new(0,0,0,0)
	uiGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left
	uiGridLayout.VerticalAlignment = Enum.VerticalAlignment.Top
	uiGridLayout.Parent = buttonListFrame
	uiGridLayout.SortOrder = Enum.SortOrder.LayoutOrder

	local uiPadding = Instance.new("UIPadding")
	uiPadding.PaddingBottom = UDim.new(0, 0)
	uiPadding.PaddingTop = UDim.new(0, 0)
	uiPadding.PaddingLeft = UDim.new(0, GuiUtilities.kStandardHMargin)
	uiPadding.PaddingRight = UDim.new(0, GuiUtilities.kStandardHMargin)
	uiPadding.Parent = buttonListFrame

	for buttonId, buttonConfig in pairs(buttonConfigs) do
		createRegionButton(buttonListFrame, buttonConfig)
	end

	return vsf:GetFrame()
end

function MakeModeButtonsFrame()
	local frame = MakeButtonGridInFrameWithTitle(kRegionModeButtonConfigs, "ModeFrame", "Tools")
	return frame
end

function MakeOperationButtonsFrame()
	local frame = MakeButtonGridInFrameWithTitle(kRegionOperationButtonConfigs, "OperationFrame", "Edit")
	return frame
end

function AddToFrameInOrder(childElement, parentFrame, order)
	childElement.Parent = parentFrame
	childElement.LayoutOrder = order
end

local function MakeButtonsFrame()
	local frame = GuiUtilities.MakeFixedHeightFrame("Buttons", GuiUtilities.kBottomButtonsFrameHeight + kBottomButtonMargin)
	frame.BackgroundTransparency = 1

	local okButtonObj = CustomTextButton.new("OkButton", "OK")
	okButtonObj:getButton().Parent = frame
	okButtonObj:getButton().Size = UDim2.new(0, GuiUtilities.kBottomButtonsWidth, 0, GuiUtilities.kBottomButtonsHeight)
	okButtonObj:getButton().Position = UDim2.new(0.5, -GuiUtilities.kBottomButtonsWidth/2,
		 1, -GuiUtilities.kBottomButtonsHeight - kBottomButtonMargin)
	
	return frame
end


function MakeFillFrame(pluginGui)
	local vsl = VerticallyScalingListFrame.new("mwt")
	vsl:AddBottomPadding()

	local titleLabel = GuiUtilities.MakeFrameWithSubSectionLabel("Material", "Fill Selection")
	vsl:AddChild(titleLabel)

	kMaterialsListObject = MaterialsListClass.new()
	local materialsFrame = kMaterialsListObject:GetMaterialsFrame()
	vsl:AddChild(materialsFrame)

	local buttonFrame = MakeButtonsFrame()
	vsl:AddChild(buttonFrame)

	vsl:GetFrame().Visible = false
	return vsl:GetFrame()
end

module.FirstTimeSetup = function(theMouse, thePluginGui, theContentFrame)
	----SETTINGS----
	local mode = 'Select'	--Select, Edit
	local tool = 'None'		--None, Resize, Move, Rotate
	local currentButtonId = 'Select'
	
	local fillAir = true
	local fillWater = true
	----------------

	--SUB SETTINGS--
	local resolution = 4			--This is the size of voxels on Roblox. Why is this a variable? ;)
	local textSelectColor = Color3.new(72/255, 145/255, 212/255)
	local white = Color3.new(238/255, 238/255, 238/255)
	local editColor1 = 'Institutional white'
	local editColor2 = 'Light stone grey'
	local rotationInterval = math.pi * .5
	local regionLengthLimit = 125
	----------------

	mouse = theMouse
	pluginGui = thePluginGui
	contentFrame = theContentFrame
	local changeHistory = game:GetService('ChangeHistoryService')
	local terrain = game.Workspace.Terrain
	local coreGui = game:GetService('CoreGui')

	screenGui = Instance.new("ScreenGui")

	-- Size will be updated dynamically.
	local editCollapsibleSectionObj = CollapsibleTitledSection.new("Edit", "Edit Tools", true, true)
	GuiUtilities.MakeFrameAutoScalingList(editCollapsibleSectionObj:GetContentsFrame())
	editCollapsibleSectionObj:GetSectionFrame().Parent = contentFrame

	-- First child: the "merge empty" button(s).
	local checkboxObj = LabeledCheckbox.new("CheckboxFrame", "Merge Empty", fillAir)

	-- Second child: the "Mode" buttons.
	local modeButtonsFrame = MakeModeButtonsFrame()

	-- Third child: the "Operation" buttons.
	local operationButtonsFrame = MakeOperationButtonsFrame()

	-- Fourth child: the "Fill" frame.
	local fillFrame = MakeFillFrame()

	GuiUtilities.AddStripedChildrenToListFrame(editCollapsibleSectionObj:GetContentsFrame(), {
		checkboxObj:GetFrame(), 
		modeButtonsFrame,
		operationButtonsFrame, 
		fillFrame})

	editCollapsibleSectionObj:GetSectionFrame().Visible = false
	terrainRegionFrame = editCollapsibleSectionObj:GetSectionFrame()

	local buttonSelect = modeButtonsFrame.grid.ButtonSelect
	local buttonMove = modeButtonsFrame.grid.ButtonMove
	local buttonResize = modeButtonsFrame.grid.ButtonResize
	local buttonRotate = modeButtonsFrame.grid.ButtonRotate
	local buttonCopy = operationButtonsFrame.grid.ButtonCopy
	local buttonPaste = operationButtonsFrame.grid.ButtonPaste
	local buttonDelete = operationButtonsFrame.grid.ButtonDelete
	local buttonFill = operationButtonsFrame.grid.ButtonFill

	local materialsTable = MaterialsListClass.MaterialsTable
	local buttonFillConfirm = fillFrame.Buttons.OkButton
	--local mouse = plugin:GetMouse()

	--Variables--
	local faceToNormal = {
		[Enum.NormalId.Top] = Vector3.new(0, 1, 0),
		[Enum.NormalId.Bottom] = Vector3.new(0, -1, 0),
		[Enum.NormalId.Left] = Vector3.new(-1, 0, 0),
		[Enum.NormalId.Right] = Vector3.new(1, 0, 0),
		[Enum.NormalId.Front] = Vector3.new(0, 0, -1),
		[Enum.NormalId.Back] = Vector3.new(0, 0, 1),
	}
	local undefined=0/0
	local selectionStart = nil
	local selectionEnd = nil
	local selectionPart = nil
	local selectionObject = nil
	local selectionHandles = nil
	kMaterialsListObject:SetMaterialSelection(materialsTable[5])
	local downLoop = nil
	local clickStart = Vector3.new(0, 0, 0)
	local dragVector = nil
	local dragStart = true
	local lockedMaterials, lockedOccupancies = nil, nil
	local lockedRegion = nil
	local behindThis = nil
	local axis = 'X'
	local materialAir = Enum.Material.Air
	local materialWater = Enum.Material.Water
	local floor = math.floor		--Scaling linear resize
	local ceil = math.ceil
	-------------

	function updateButtonSelectionState(newButtonId, buttonId)
		kRegionModeButtonConfigs[buttonId].ButtonObj:setSelected(newButtonId == buttonId)
	end

	function setButton(newButtonId)
		lockInMap()

		for buttonId, buttonConfig in pairs(kRegionModeButtonConfigs) do
			updateButtonSelectionState(newButtonId, buttonId)
		end

		if (newButtonId ~= nil) then 			
			mode = kRegionModeButtonConfigs[newButtonId].Mode
			tool = kRegionModeButtonConfigs[newButtonId].Tool
		end

		currentButtonId = newButtonId
		renderSelection()
	end

	buttonSelect.MouseButton1Down:connect(function()
		setButton('Select')
	end)
	buttonMove.MouseButton1Down:connect(function()
		setButton('Move')
	end)
	buttonResize.MouseButton1Down:connect(function()
		setButton('Resize')
	end)
	buttonRotate.MouseButton1Down:connect(function()
		setButton('Rotate')
	end)

	checkboxObj:SetValueChangedFunction(function(value) 
		fillAir = value
		if selectionStart and selectionEnd then
			if currentButtonId=='Move' or currentButtonId=='Resize' then
				updateDragOperation()
			elseif currentButtonId=='Rotate' then
				updateRotateOperation()
			end
		end
	end)

	function lockInMap()	--Should call this every time the terrain under your selection changes. Don't for resize though, it uses the original lockin for rescaling.
		if selectionStart and selectionEnd then
			local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
			lockedRegion = region
			lockedMaterials, lockedOccupancies = terrain:ReadVoxels(region, resolution)
		end
	end

	do
		local runService = game:GetService('RunService').RenderStepped
		function quickWait(waitTime)
			if not waitTime then
				runService:wait()
			elseif waitTime < .033333 then
				local startTick = tick()
				runService:wait()
				local delta = tick() - startTick
				if delta <= waitTime * .5 then
					quickWait(waitTime - delta)
				end
			else
				wait(waitTime)
			end
		end
	end

	function clearSelection()
		if selectionArcHandles then
			selectionArcHandles:Destroy()
			selectionArcHandles = nil
		end
		if selectionHandles then
			selectionHandles:Destroy()
			selectionHandles = nil
		end
		if selectionObject then
			selectionObject:Destroy()
			selectionObject = nil
		end
		if selectionPart then
			selectionPart:Destroy()
			selectionPart = nil
		end
	end

	local function round(n)
		return n + .5 - ((n + .5)%1)
	end

	local function positionWorldToVoxel(pos)
		return Vector3.new(ceil(pos.x / resolution), ceil(pos.y / resolution), ceil(pos.z / resolution))
	end

	local function make3DTable(size,fill)
		local size = size or Vector3.new(1,1,1)
		local newTable = {}
		for x = 1, size.x do
			local xt = {}
			for y = 1, size.y do
				local yt = {}
				for z = 1, size.z do
					yt[z] = fill
				end
				xt[y] = yt
			end
			newTable[x] = xt
		end
		return newTable
	end

	local function linInterp(a,b,p)
		return a+(b-a)*p
	end

	local function exaggerate(n,exaggeration)
		return (n-.5)*exaggeration + .5		--optimized
		--return n*exaggeration - exaggeration*.5 + .5
	end
	local function exaggeratedLinInterp(a,b,p,exaggeration)
		local unclamped = (a+(b-a)*p-.5)*exaggeration+.5
		return (unclamped < 0 and 0) or (unclamped > 1 and 1) or unclamped

		--At first I thought this didn't need to be clamped because the terrain clamps that anways.
		--But I then realized I am using this number a bit more before handing it to terrain.
		--After doing some tests. Clamping is necessary for artificial structures being streched. If unclamped, rounding of artificial edges occurs.
		--return (a+(b-a)*p-.5)*exaggeration+.5
		--Maybe this extra dimension of unclamping might be desired for natural terrain, but not artificuial?
	end

	function updateDragOperation()
		local dragVector = dragVector or Vector3.new(0,0,0)
		local temporaryStart = selectionStart
		local temporaryEnd = selectionEnd
		if tool == 'Resize' then
			if dragStart then
				temporaryStart = Vector3.new(
					math.min(
						math.max(temporaryStart.x+dragVector.x,temporaryEnd.x-regionLengthLimit),
						temporaryEnd.x),
					math.min(
						math.max(temporaryStart.y+dragVector.y,temporaryEnd.y-regionLengthLimit),
						temporaryEnd.y),
					math.min(
						math.max(temporaryStart.z+dragVector.z,temporaryEnd.z-regionLengthLimit),
						temporaryEnd.z)
				)
			else
				temporaryEnd = Vector3.new(
					math.max(
						math.min(temporaryEnd.x+dragVector.x,temporaryStart.x+regionLengthLimit),
						temporaryStart.x),
					math.max(
						math.min(temporaryEnd.y+dragVector.y,temporaryStart.y+regionLengthLimit),
						temporaryStart.y),
					math.max(
						math.min(temporaryEnd.z+dragVector.z,temporaryStart.z+regionLengthLimit),
						temporaryStart.z)
				)
			end
			if mode == 'Edit' then
				local region = Region3.new((temporaryStart - Vector3.new(1,1,1)) * resolution, temporaryEnd * resolution)
				if behindThis then
					terrain:WriteVoxels(behindThis.region, resolution, behindThis.materials, behindThis.occupancies)
				else
					if selectionStart and selectionEnd then
						local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
						local regionSize = region.Size / resolution
						terrain:WriteVoxels(region, resolution, make3DTable(regionSize,materialAir), make3DTable(regionSize,0))
					end
				end
				behindThis = {}
				behindThis.region = region
				behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)

				local behindMaterials, behindOccupancies = behindThis.materials, behindThis.occupancies

				local loopx = #lockedMaterials - 1
				local loopy = #lockedMaterials[1] - 1
				local loopz = #lockedMaterials[1][1] - 1
				local tempRegionSize = Vector3.new(1,1,1) + temporaryEnd - temporaryStart
				local tempSizeX = tempRegionSize.x
				local tempSizeY = tempRegionSize.y
				local tempSizeZ = tempRegionSize.z
				local newMat = {}
				local newOcc = {}
				for x=1, tempSizeX do
					local scalex = (x-1)/(tempSizeX-1)*loopx
					if scalex ~= scalex then
						scalex = 0
					end
					local startx = floor(scalex)+1
					local endx = startx+1
					local interpScalex = scalex-startx+1
					if startx > loopx then
						endx = startx
					end
		
					local xtm = {}
					local xto = {}
					for y=1, tempSizeY do
						local scaley = (y-1)/(tempSizeY-1)*loopy
						if scaley ~= scaley then
							scaley = 0
						end
						local starty = floor(scaley)+1
						local endy = starty+1
						local interpScaley = scaley-starty+1
						if starty > loopy then
							endy = starty
						end
		
						local ytm = {}
						local yto = {}
						for z=1, tempSizeZ do
							local scalez = (z-1)/(tempSizeZ-1)*loopz	--consider adding 1 here and removing +1's elsewhere
							if scalez ~= scalez then		--undefined check
								scalez = 0
							end
							local startz = floor(scalez)+1
							local endz = startz+1
							local interpScalez = scalez-startz+1
							if startz > loopz then
								endz = startz
							end
		
							local interpz1 = exaggeratedLinInterp(lockedOccupancies[startx][starty][startz],lockedOccupancies[startx][starty][endz],interpScalez, tempSizeZ/(loopz+1))
							local interpz2 = exaggeratedLinInterp(lockedOccupancies[startx][endy][startz],lockedOccupancies[startx][endy][endz],interpScalez, tempSizeZ/(loopz+1))
							local interpz3 = exaggeratedLinInterp(lockedOccupancies[endx][starty][startz],lockedOccupancies[endx][starty][endz],interpScalez, tempSizeZ/(loopz+1))
							local interpz4 = exaggeratedLinInterp(lockedOccupancies[endx][endy][startz],lockedOccupancies[endx][endy][endz],interpScalez, tempSizeZ/(loopz+1))
		
							local interpy1 = exaggeratedLinInterp(interpz1,interpz2,interpScaley, tempSizeY/(loopy+1))
							local interpy2 = exaggeratedLinInterp(interpz3,interpz4,interpScaley, tempSizeY/(loopy+1))
		
							local interpx1 = exaggeratedLinInterp(interpy1,interpy2,interpScalex, tempSizeX/(loopx+1))
		
							local newMaterial = lockedMaterials[round(scalex)+1][round(scaley)+1][round(scalez)+1]
		
							if fillAir and newMaterial == materialAir then
								ytm[z]=behindMaterials[x][y][z]
								yto[z]=behindOccupancies[x][y][z]
							elseif fillWater and newMaterial == materialWater and behindMaterials[x][y][z] ~= materialAir then
								ytm[z]=behindMaterials[x][y][z]
								yto[z]=behindOccupancies[x][y][z]
							else
								ytm[z]=newMaterial
								yto[z]=interpx1
							end
						end
						xtm[y] = ytm
						xto[y] = yto
					end
					newMat[x] = xtm
					newOcc[x] = xto
				end
		
				terrain:WriteVoxels(region, resolution, newMat, newOcc)
			else
				behindThis = nil
			end
		elseif tool == 'Move' then
			temporaryStart = temporaryStart + dragVector
			temporaryEnd = temporaryEnd + dragVector
			if mode == 'Edit' then
				local region = Region3.new((temporaryStart - Vector3.new(1,1,1)) * resolution, temporaryEnd * resolution)
				if behindThis then
					terrain:WriteVoxels(behindThis.region, resolution, behindThis.materials, behindThis.occupancies)
				else
					if selectionStart and selectionEnd then
						local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
						local regionSize = region.Size / resolution
						terrain:WriteVoxels(region, resolution, make3DTable(regionSize,materialAir), make3DTable(regionSize,0))
					end
				end
				behindThis = {}
				behindThis.region = region
				behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)

				local behindMaterials, behindOccupancies = behindThis.materials, behindThis.occupancies
		
				if not (fillAir or fillWater) then
					terrain:WriteVoxels(region, resolution, lockedMaterials, lockedOccupancies)
				else
					local newMat = {}
					local newOcc = {}
		
					for x,xv in ipairs(lockedMaterials) do
						local xtm = {}
						local xto = {}
						for y,yv in ipairs(xv) do
							local ytm = {}
							local yto = {}
							for z,zv in ipairs(yv) do
								if fillAir and zv == materialAir then
									ytm[z]=behindMaterials[x][y][z]
									yto[z]=behindOccupancies[x][y][z]
								elseif fillWater and zv == materialWater and behindMaterials[x][y][z] ~= materialAir then
									ytm[z]=behindMaterials[x][y][z]
									yto[z]=behindOccupancies[x][y][z]
								else
									ytm[z]=lockedMaterials[x][y][z]
									yto[z]=lockedOccupancies[x][y][z]
								end
							end
							xtm[y] = ytm
							xto[y] = yto
						end
						newMat[x] = xtm
						newOcc[x] = xto
					end
					terrain:WriteVoxels(region, resolution, newMat, newOcc)
				end
			end
		end
		renderSelection(temporaryStart,temporaryEnd)
	end

	function dragHandles(face, delta)
		local normal = faceToNormal[face]
		local delta = delta
		local newDragVector = normal * floor((delta + .5) / resolution)
		dragStart = normal.x < 0 or normal.y < 0 or normal.z < 0	--This determines if we are dragging a side on the min or max bounds
		if newDragVector ~= dragVector then
			dragVector = newDragVector
			updateDragOperation()
		end
	end

	local function rotate(mx,x,my,y,rotation)
		if rotation == 1 then
			return my + 1 - y, x 
		elseif rotation == 2 then
			return mx + 1 - x, my + 1 - y
		elseif rotation == 3 then
			return y, mx + 1 - x
		end
		return x,y
	end

	function updateRotateOperation()
		local dragAngle = dragAngle or 0
		local rotationCFrame = CFrame.Angles(
			axis ~= 'X' and 0 or dragAngle * rotationInterval,
			axis ~= 'Y' and 0 or dragAngle * rotationInterval,
			axis ~= 'Z' and 0 or dragAngle * rotationInterval
		)
		local temporarySize = Vector3.new(1,1,1) + selectionEnd - selectionStart
		local centerOffset = Vector3.new(ceil(temporarySize.x * .5),
			ceil(temporarySize.y * .5), 
			ceil(temporarySize.z * .5))

		temporarySize = rotationCFrame * temporarySize
		local temporarySizeX = round(math.abs(temporarySize.x))	--I need to round these because of floating point imprecision
		local temporarySizeY = round(math.abs(temporarySize.y))
		local temporarySizeZ = round(math.abs(temporarySize.z))
		centerOffset = centerOffset - Vector3.new(ceil(temporarySizeX * .5), ceil(temporarySizeY * .5), ceil(temporarySizeZ * .5))

		local temporaryEnd = selectionStart + centerOffset + Vector3.new(temporarySizeX, temporarySizeY, temporarySizeZ) - Vector3.new(1, 1, 1)
		local temporaryStart = selectionStart + centerOffset

		if mode == 'Edit' then
			local region = Region3.new((temporaryStart - Vector3.new(1,1,1)) * resolution, temporaryEnd * resolution)
			if behindThis then
				terrain:WriteVoxels(behindThis.region, resolution, behindThis.materials, behindThis.occupancies)
			else
				if selectionStart and selectionEnd then
					local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
					local regionSize = region.Size / resolution
					terrain:WriteVoxels(region, resolution, make3DTable(regionSize,materialAir), make3DTable(regionSize,0))
				end
				--local regionSize = lockedRegion.Size / resolution
				--terrain:WriteVoxels(lockedRegion, resolution, make3DTable(regionSize,materialAir), make3DTable(regionSize,0))
			end
			behindThis = {}
			behindThis.region = region
			behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)

			local newMat = {}
			local newOcc = {}

			for x=1, temporarySizeX do
				local xtm = {}
				local xto = {}
				for y=1, temporarySizeY do
					local ytm = {}
					local yto = {}
					for z=1, temporarySizeZ do
						local targetx = x
						local targety = y
						local targetz = z
						if axis == 'Y' then	--prioritize y because I know this is the primary rotation axis
							targetx, targetz = rotate(temporarySizeX, x, temporarySizeZ, z, dragAngle)
						elseif axis == 'X' then
							targetz, targety = rotate(temporarySizeZ, z, temporarySizeY, y, dragAngle)
						elseif axis == 'Z' then
							targety, targetx = rotate(temporarySizeY, y, temporarySizeX, x, dragAngle)
						end
						local newMaterial = lockedMaterials[targetx][targety][targetz]

						if fillAir and newMaterial == materialAir then
							ytm[z]=behindThis.materials[x][y][z]
							yto[z]=behindThis.occupancies[x][y][z]
						elseif fillWater and newMaterial == materialWater and behindThis.materials[x][y][z] ~= materialAir then
							ytm[z]=behindThis.materials[x][y][z]
							yto[z]=behindThis.occupancies[x][y][z]
						else
							ytm[z]=newMaterial
							yto[z]=lockedOccupancies[targetx][targety][targetz]
						end
					end
					xtm[y] = ytm
					xto[y] = yto
				end
				newMat[x] = xtm
				newOcc[x] = xto
			end

			terrain:WriteVoxels(region, resolution, newMat, newOcc)
		end
		renderSelection(temporaryStart,temporaryEnd,rotationCFrame)
	end

	function dragArcHandles(rotationAxis,relativeAngle,deltaRadius)
		axis = rotationAxis.Name
		local newDragAngle = round(relativeAngle / rotationInterval) % 4
		if newDragAngle ~= dragAngle then
			dragAngle = newDragAngle
			updateRotateOperation()
		end
	end

	buttonCopy.MouseButton1Down:connect(function()
		if selectionStart and selectionEnd then
			local selectionStartInt16=Vector3int16.new(selectionStart.x-1,selectionStart.y-1,selectionStart.z-1)
			local selectionEndInt16=Vector3int16.new(selectionEnd.x-1,selectionEnd.y-1,selectionEnd.z-1)
			local region = Region3int16.new(selectionStartInt16,selectionEndInt16)
			copyRegion = terrain:CopyRegion(region)
			selectionEffect(nil,nil,'New Yeller',1,1.2,.5)
		end
	end)

	buttonPaste.MouseButton1Down:connect(function()
		if copyRegion then
			selectionEnd=selectionStart+copyRegion.SizeInCells-Vector3.new(1,1,1)

			local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
			behindThis = {}
			behindThis.region = region
			behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)

			terrain:PasteRegion(copyRegion,Vector3int16.new(selectionStart.x-1,selectionStart.y-1,selectionStart.z-1),true)
			setButton('Move')
			changeHistory:SetWaypoint('Terrain Paste')
			selectionEffect(nil,nil,'Lime green',1.2,1,.5)
		end
	end)

	buttonDelete.MouseButton1Down:connect(function()
		if selectionStart and selectionEnd then
			local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
			local regionSize = region.Size / resolution
			local emptyMaterialMap = make3DTable(regionSize,materialAir)
			local emptyOccupancyMap = make3DTable(regionSize,0)

			--[[behindThis = {}
			behindThis.region = region
			behindThis.materials, behindThis.occupancies = emptyMaterialMap, emptyOccupancyMap

			terrain:WriteVoxels(region, resolution, emptyMaterialMap, emptyOccupancyMap)]]
			if behindThis then
				terrain:WriteVoxels(behindThis.region, resolution, behindThis.materials, behindThis.occupancies)
			else
				if selectionStart and selectionEnd then
					terrain:WriteVoxels(region, resolution, emptyMaterialMap, emptyOccupancyMap)
				end
			end
			behindThis = {}
			behindThis.region = region
			behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)

			--[[lockedRegion = region
			lockedMaterials, lockedOccupancies = emptyMaterialMap, emptyOccupancyMap]]
			local oldStart, oldEnd = selectionStart, selectionEnd
			selectionStart, selectionEnd = nil, nil
			setButton('Select')

			changeHistory:SetWaypoint('Terrain Delete')
			selectionEffect(oldStart,oldEnd,'Really red',1,1.2,.5)
		end
	end)

	buttonFill.MouseButton1Down:connect(function()
		fillFrame.Visible = not fillFrame.Visible
		-- Toggle button state as well.
		kRegionOperationButtonConfigs["Fill"].ButtonObj:setSelected(fillFrame.Visible)
	end)

	buttonFillConfirm.MouseButton1Down:connect(function()
		if selectionStart and selectionEnd then
			local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
			local regionSize = region.Size / resolution
			local emptyMaterialMap = make3DTable(regionSize,materialAir)
			local emptyOccupancyMap = make3DTable(regionSize,0)

			local beforeMaterialMap, beforeOccupancyMap = terrain:ReadVoxels(region, resolution)
			local newMaterialMap = {}
			local newOccupancyMap = {}
			for x = 1, regionSize.x do
				local xtm = {}
				local xto = {}
				for y = 1, regionSize.y do
					local ytm = {}
					local yto = {}
					for z = 1, regionSize.z do
						local beforeMaterial = beforeMaterialMap[x][y][z]

						if beforeMaterial == materialAir or beforeOccupancyMap[x][y][z] == 0 or not fillAir then	--'fillAir' variable is actually 'Merge Empty' to the user
							ytm[z] = kMaterialsListObject:GetCurrentMaterialSelection().enum
						else
							ytm[z] = beforeMaterial
						end

						yto[z] = 1
					end
					xtm[y] = ytm
					xto[y] = yto
				end
				newMaterialMap[x] = xtm
				newOccupancyMap[x] = xto
			end


			terrain:WriteVoxels(region, resolution, newMaterialMap, newOccupancyMap)

			behindThis = {}
			behindThis.region = region
			behindThis.materials, behindThis.occupancies = emptyMaterialMap, emptyOccupancyMap

			fillFrame.Visible = false

			changeHistory:SetWaypoint('Terrain Fill')
			selectionEffect(nil,nil,'Lime green',1.2,1,.5)
		end
	end)

	function selectionEffect(temporaryStart, temporaryEnd, color, sizeFrom, sizeTo, effectTime)
		local temporaryStart = temporaryStart or selectionStart
		local temporaryEnd = temporaryEnd or selectionEnd

		local effectPart = Instance.new('Part')
		effectPart.Name = 'EffectPart'
		effectPart.Transparency = 1
		effectPart.TopSurface = 'Smooth'
		effectPart.BottomSurface = 'Smooth'
		effectPart.Anchored = true
		effectPart.CanCollide = false
		effectPart.Parent = screenGui

		local selectionEffectObject = Instance.new('SelectionBox')
		selectionEffectObject.Name = 'SelectionObject'
		selectionEffectObject.Transparency = 1
		selectionEffectObject.SurfaceTransparency = .75
		selectionEffectObject.SurfaceColor = BrickColor.new(color)
		selectionEffectObject.Adornee = effectPart
		selectionEffectObject.Parent = effectPart

		local baseSize = ((temporaryEnd - temporaryStart + Vector3.new(1,1,1)) * resolution + Vector3.new(.21,.21,.21))
		effectPart.CFrame = CFrame.new((temporaryStart + temporaryEnd - Vector3.new(1, 1, 1)) * .5 * resolution)
		effectPart.Size = baseSize * sizeFrom
		local endTick=tick()+effectTime
		while endTick>tick() do
			local percent=1-(endTick-tick())/effectTime
			selectionEffectObject.SurfaceTransparency = .75 + percent*.25
			effectPart.Size = baseSize * (sizeFrom+(sizeTo-sizeFrom)*percent)
			wait()
		end
		effectPart:Destroy()
	end

	function renderSelection(temporaryStart,temporaryEnd,rotation)
		local temporaryStart = temporaryStart or selectionStart
		local temporaryEnd = temporaryEnd or selectionEnd
		local seeable = false
		if temporaryStart and temporaryEnd and selectionPart then
			seeable = true
			local temporarySize = ((temporaryEnd - temporaryStart + Vector3.new(1,1,1)) * resolution + Vector3.new(.2,.2,.2))
			if rotation then
				local rotatedSize = rotation * temporarySize
				temporarySize = Vector3.new(math.abs(rotatedSize.x), math.abs(rotatedSize.y), math.abs(rotatedSize.z))
			end
			selectionPart.Size = temporarySize
			selectionPart.CFrame = CFrame.new((temporaryStart + temporaryEnd - Vector3.new(1, 1, 1)) * .5 * resolution) * (rotation or CFrame.new(0,0,0))
		end
		if selectionObject then
			selectionObject.Visible = seeable
			selectionObject.Color = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor1)
			selectionObject.SurfaceColor = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor1)
		end
		if selectionHandles then
			selectionHandles.Visible = seeable and (tool == 'Move' or tool == 'Resize')
			selectionHandles.Color = BrickColor.new(mode == 'Select' and 'Cyan' or editColor2)
			selectionHandles.Style = tool == 'Move' and Enum.HandlesStyle.Movement or Enum.HandlesStyle.Resize
		end
		if selectionArcHandles then
			selectionArcHandles.Visible = seeable and tool == 'Rotate'
			selectionArcHandles.Color = BrickColor.new(mode == 'Select' and 'Cyan' or editColor2)
		end
	end

	mouse.Button1Down:connect(function()
		if on and mode == 'Select' then
			mouseDown = true
			behindThis = nil
			local mousePos = mouse.Hit.p + mouse.UnitRay.Direction * .05
			if mouse.Target == nil then	--cage the cursor so that it does not fly away
				mousePos = game.Workspace.CurrentCamera.CoordinateFrame.p + mouse.UnitRay.Direction * 100
			end
			clickStart = positionWorldToVoxel(mousePos)
			local thisDownLoop = {}
			downLoop = thisDownLoop
			while thisDownLoop == downLoop and mouseDown and on and mode == 'Select' do
				local mousePos = mouse.Hit.p + mouse.UnitRay.Direction * .05
				if mouse.Target == nil then	--cage the cursor so that it does not fly away
					mousePos = game.Workspace.CurrentCamera.CoordinateFrame.p + mouse.UnitRay.Direction * 100
				end
				local voxelCurrent = positionWorldToVoxel(mousePos)
				voxelCurrent = Vector3.new(
					math.max(math.min(voxelCurrent.x,clickStart.x+regionLengthLimit),clickStart.x-regionLengthLimit),
					math.max(math.min(voxelCurrent.y,clickStart.y+regionLengthLimit),clickStart.y-regionLengthLimit),
					math.max(math.min(voxelCurrent.z,clickStart.z+regionLengthLimit),clickStart.z-regionLengthLimit))
				selectionStart = Vector3.new(math.min(clickStart.x, voxelCurrent.x), math.min(clickStart.y, voxelCurrent.y), math.min(clickStart.z, voxelCurrent.z))
				selectionEnd = Vector3.new(math.max(clickStart.x, voxelCurrent.x), math.max(clickStart.y, voxelCurrent.y), math.max(clickStart.z, voxelCurrent.z))
				renderSelection()
				quickWait()
			end
		end
	end)
	mouse.Button1Up:connect(function()
		mouseDown = false
		if dragVector and dragVector.magnitude > 0 then
			if tool == 'Resize' then
				--[[if dragStart then
					selectionStart = Vector3.new(math.min(selectionStart.x+dragVector.x,selectionEnd.x),math.min(selectionStart.y+dragVector.y,selectionEnd.y),math.min(selectionStart.z+dragVector.z,selectionEnd.z))
				else
					selectionEnd = Vector3.new(math.max(selectionEnd.x+dragVector.x,selectionStart.x),math.max(selectionEnd.y+dragVector.y,selectionStart.y),math.max(selectionEnd.z+dragVector.z,selectionStart.z))
				end]]
				if dragStart then
					selectionStart = Vector3.new(
						math.min(
							math.max(selectionStart.x+dragVector.x,selectionEnd.x-regionLengthLimit),
							selectionEnd.x),
						math.min(
							math.max(selectionStart.y+dragVector.y,selectionEnd.y-regionLengthLimit),
							selectionEnd.y),
						math.min(
							math.max(selectionStart.z+dragVector.z,selectionEnd.z-regionLengthLimit),
							selectionEnd.z)
					)
				else
					selectionEnd = Vector3.new(
						math.max(
							math.min(selectionEnd.x+dragVector.x,selectionStart.x+regionLengthLimit),
							selectionStart.x),
						math.max(
							math.min(selectionEnd.y+dragVector.y,selectionStart.y+regionLengthLimit),
							selectionStart.y),
						math.max(
							math.min(selectionEnd.z+dragVector.z,selectionStart.z+regionLengthLimit),
							selectionStart.z)
					)
				end
			elseif tool == 'Move' then
				selectionStart = selectionStart + dragVector
				selectionEnd = selectionEnd + dragVector
			end
			
			changeHistory:SetWaypoint('Terrain '..currentButtonId)
		end
		if dragAngle and dragAngle ~= 0 then
			local rotationCFrame = CFrame.Angles(
				axis ~= 'X' and 0 or dragAngle * rotationInterval,
				axis ~= 'Y' and 0 or dragAngle * rotationInterval,
				axis ~= 'Z' and 0 or dragAngle * rotationInterval
			)
			local temporarySize = Vector3.new(1,1,1) + selectionEnd - selectionStart
			local centerOffset = Vector3.new(ceil(temporarySize.x * .5), ceil(temporarySize.y * .5), ceil(temporarySize.z * .5))
			temporarySize = rotationCFrame * temporarySize
			local temporarySizeX = round(math.abs(temporarySize.x))	--I need to round these because of floating point imprecision
			local temporarySizeY = round(math.abs(temporarySize.y))
			local temporarySizeZ = round(math.abs(temporarySize.z))
			centerOffset = centerOffset - Vector3.new(ceil(temporarySizeX * .5), ceil(temporarySizeY * .5), ceil(temporarySizeZ * .5))
			
			selectionEnd = selectionStart + centerOffset + Vector3.new(temporarySizeX, temporarySizeY, temporarySizeZ) - Vector3.new(1, 1, 1)
			selectionStart = selectionStart + centerOffset
			lockInMap()
			changeHistory:SetWaypoint('Terrain '..currentButtonId)
		end

		dragVector = nil
		dragAngle = nil
		renderSelection()
		--lockInMap()
	end)

	local function historyChanged()
		selectionStart = nil
		selectionEnd = nil
		lockedMaterials = nil
		lockedOccupancies = nil
		setButton('Select')
	end
	
	changeHistory.OnUndo:connect(historyChanged)
	changeHistory.OnRedo:connect(historyChanged)

	module.On = function()
		on = true
		screenGui.Parent = coreGui
		terrainRegionFrame.Visible = true

		if not selectionPart then
			selectionPart = Instance.new('Part')
			selectionPart.Name = 'SelectionPart'
			selectionPart.Transparency = 1
			selectionPart.TopSurface = 'Smooth'
			selectionPart.BottomSurface = 'Smooth'
			selectionPart.Anchored = true
			selectionPart.CanCollide = false
			selectionPart.Parent = screenGui
		end
		if not selectionObject then
			selectionObject = Instance.new('SelectionBox')
			selectionObject.Name = 'SelectionObject'
			selectionObject.Color = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor1)
			selectionObject.SurfaceTransparency = .85
			selectionObject.SurfaceColor = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor1)
			selectionObject.Adornee = selectionPart
			selectionObject.Visible = false
			selectionObject.Parent = selectionPart
		end
		if not selectionHandles then
			selectionHandles = Instance.new('Handles')
			selectionHandles.Name = 'SelectionHandles'
			selectionHandles.Color = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor2)
			selectionHandles.Adornee = selectionPart
			selectionHandles.Visible = false
			selectionHandles.Parent = coreGui--game.Workspace--terrainRegionFrame--selectionPart
			selectionHandles.MouseDrag:connect(dragHandles)
		end
		if not selectionArcHandles then
			selectionArcHandles = Instance.new('ArcHandles')
			selectionArcHandles.Name = 'SelectionArcHandles'
			selectionArcHandles.Color = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor2)
			selectionArcHandles.Adornee = selectionPart
			selectionArcHandles.Visible = false
			selectionArcHandles.Parent = coreGui--game.Workspace--terrainRegionFrame--selectionPart
			selectionArcHandles.MouseDrag:connect(dragArcHandles)
		end
		renderSelection()
		setButton(currentButtonId)
	end

	module.Off = function()
		setButton('Select')
		terrainRegionFrame.Visible = false
		fillFrame.Visible = false
		clearSelection()
		behindThis = nil
		on = false
	end
end


return module


]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX2686e3fb477e4a6cb525de09d70a737e">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">MaterialsList</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[local MaterialsListClass = {}

GuiUtilities = require(script.Parent.Libs.GuiUtilities)

local kMaterialCellSize = 31
local kMaterialCellPadding = 9

local kMaterialTooltipHeight = 30
local kHighlightSize = 4
local kHighlightColor = Color3.fromRGB(117, 167, 246)

MaterialsListClass.MaterialsTable = {	--Interface order is defined by order here
	{
		enum = Enum.Material.Grass, 
		image = "rbxasset://textures/TerrainTools/mtrl_grass.png", 
		text = "Grass", 
	},
	{
		enum = Enum.Material.Sand, 
		image = "rbxasset://textures/TerrainTools/mtrl_sand.png", 
		text = "Sand", 
	},
	{
		enum = Enum.Material.Rock,
		image = "rbxasset://textures/TerrainTools/mtrl_rock.png", 
		text = "Rock", 
	},
	{
		enum = Enum.Material.Water, 
		image = "rbxasset://textures/TerrainTools/mtrl_water.png",
		forceIgnoreWater = true, 
		forceIgnoreWaterTo = false, 
		text = "Water", 
	},
	{
		enum = Enum.Material.Ground, 
		image = "rbxasset://textures/TerrainTools/mtrl_ground.png", 
		text = "Ground", 
	},
	{
		enum = Enum.Material.Sandstone, 
		image = "rbxasset://textures/TerrainTools/mtrl_sandstone.png", 
		text = "Sandstone", 
	},
	{
		enum = Enum.Material.Slate, 
		image = "rbxasset://textures/TerrainTools/mtrl_slate.png", 
		text = "Slate", 
	},
	{
		enum = Enum.Material.Snow, 
		image = "rbxasset://textures/TerrainTools/mtrl_snow.png", 
		text = "Snow", 
	},
	{
		enum = Enum.Material.Mud, 
		image = "rbxasset://textures/TerrainTools/mtrl_mud.png", 
		text = "Mud", 
	},
	{
		enum = Enum.Material.Brick, 
		image = "rbxasset://textures/TerrainTools/mtrl_brick.png", 
		forceSnapToGrid = true, 
		text = "Brick", 
	},
	{
		enum = Enum.Material.Concrete, 
		image = "rbxasset://textures/TerrainTools/mtrl_concrete.png", 
		text = "Concrete", 
	},
	{
		enum = Enum.Material.Glacier, 
		image = "rbxasset://textures/TerrainTools/mtrl_glacier.png", 
		text = "Glacier", 
	},
	{
		enum = Enum.Material.WoodPlanks, 
		image = "rbxasset://textures/TerrainTools/mtrl_woodplanks.png", 
		forceSnapToGrid = true, 
		text = "Wood Planks", 
	},
	{
		enum = Enum.Material.CrackedLava, 
		image = "rbxasset://textures/TerrainTools/mtrl_crackedlava.png", 
		text = "Cracked Lava", 
	},
	{
		enum = Enum.Material.Basalt, 
		image = "rbxasset://textures/TerrainTools/mtrl_basalt.png", 
		text = "Basalt", 
	},
	{
		enum = Enum.Material.Ice,
		image = "rbxasset://textures/TerrainTools/mtrl_ice.png", 
		text = "Ice", 
	},
	{
		enum = Enum.Material.Salt, 
		image = "rbxasset://textures/TerrainTools/mtrl_salt.png", 
		text = "Salt", 
	},
	{
		enum = Enum.Material.Cobblestone, 
		image = "rbxasset://textures/TerrainTools/mtrl_cobblestone.png", 
		text = "Cobblestone", 
	},
	{
		enum = Enum.Material.Limestone, 
		image = "rbxasset://textures/TerrainTools/mtrl_limestone.png", 
		text = "Limestone", 
	},
	{
		enum = Enum.Material.Asphalt, 
		image = "rbxasset://textures/TerrainTools/mtrl_asphalt.png",
		text = "Asphalt", 
	},
	{
		enum = Enum.Material.LeafyGrass, 
		image = "rbxasset://textures/TerrainTools/mtrl_leafygrass.png",
		text = "Leafy Grass", 
	},
	{
		enum = Enum.Material.Pavement, 
		image = "rbxasset://textures/TerrainTools/mtrl_pavement.png",
		text = "Pavement", 
	},
}

MaterialsListClass.__index = MaterialsListClass
------------------------------------------
-- 
-- Public functions
--
------------------------------------------
-- Constructor
function MaterialsListClass.new()
	local self = {}
	setmetatable(self, MaterialsListClass)

	self._currentMaterialSelection = MaterialsListClass.MaterialsTable[1]
	self._callbackOnSelectionChanged = nil
	self._materialsFrame = nil
	self._activeToolTip = Instance.new("StringValue")

	self:_MakeMaterialsFrame()

	return self
end


function MaterialsListClass:SetCallbackOnSelectionChanged(callback)
	self._callbackOnSelectionChanged = callback
end

function MaterialsListClass:GetCurrentMaterialSelection()
	return self._currentMaterialSelection
end

function MaterialsListClass:GetMaterialsFrame()
	return self._materialsFrame
end

function MaterialsListClass:SetMaterialSelection(newMaterialSelection)
	self._currentMaterialSelection = newMaterialSelection

	if (self._callbackOnSelectionChanged) then 
		self._callbackOnSelectionChanged()
	end

	for _, v in pairs(self._materialsFrame:GetChildren()) do
		if string.sub(v.Name,1,14) == 'MaterialButton' then
			if v.Name == 'MaterialButton' .. self._currentMaterialSelection.enum.Name then
				v.BackgroundTransparency = .1
			else
				v.BackgroundTransparency = 1
			end
		end
	end
end

function MaterialsListClass:SetMaterialSelectionUsingEnum(foundMaterial)
	for _, materialTable in pairs(MaterialsListClass.MaterialsTable) do
		if materialTable.enum == foundMaterial then
			self:SetMaterialSelection(materialTable)
			break
		end
	end
end

------------------------------------------
-- 
-- Private functions
--
------------------------------------------

function MaterialsListClass:_MakeMaterialsFrame()
	self._materialsFrame = GuiUtilities.MakeFrame("MaterialsFrame")

	local uiGridLayout = Instance.new("UIGridLayout")
	uiGridLayout.CellSize = UDim2.new(0,kMaterialCellSize,0,kMaterialCellSize)
	uiGridLayout.CellPadding = UDim2.new(0,kMaterialCellPadding,0,kMaterialCellPadding)
	uiGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left
	uiGridLayout.VerticalAlignment = Enum.VerticalAlignment.Top
	uiGridLayout.SortOrder = Enum.SortOrder.LayoutOrder
	uiGridLayout.Parent = self._materialsFrame

	local uiPadding = Instance.new("UIPadding")
	uiPadding.PaddingTop = UDim.new(0, GuiUtilities.kStandardVMargin)
	uiPadding.PaddingBottom = UDim.new(0, GuiUtilities.kStandardVMargin)
	uiPadding.PaddingLeft = UDim.new(0, GuiUtilities.StandardLineLabelLeftMargin)
	uiPadding.PaddingRight = UDim.new(0, GuiUtilities.StandardLineLabelLeftMargin)
	uiPadding.Parent = self._materialsFrame

	for i, materialSubTable in pairs(MaterialsListClass.MaterialsTable) do
		self:_AddNewMaterialsButton(materialSubTable)
	end

	GuiUtilities.AdjustHeightDynamicallyToLayout(self._materialsFrame, 
		uiGridLayout, 
		2 * GuiUtilities.kStandardVMargin)

	return self._materialsFrame
end


function MaterialsListClass:_AddNewMaterialsButton(materialSubTable)
	local newMaterialButton = Instance.new('ImageButton')
	newMaterialButton.Name = 'MaterialButton' .. materialSubTable.enum.Name
	newMaterialButton.BorderSizePixel = kHighlightSize
	newMaterialButton.BorderColor3 = kHighlightColor
	newMaterialButton.BackgroundColor3 = Color3.new(.2, 1, 1)
	newMaterialButton.BackgroundTransparency = 1
	newMaterialButton.Image = materialSubTable.image
	newMaterialButton.Size = UDim2.new(0, 35, 0, 35)

	newMaterialButton.MouseButton1Down:connect(function()
		self:SetMaterialSelection(materialSubTable)
	end)

	newMaterialButton.Parent = self._materialsFrame
	-- Add spaces to names
		self:_MakeToolTip(newMaterialButton,
		materialSubTable.text) 
end

function MaterialsListClass:_MakeToolTip(guiElement, text)
	local Name = tostring(guiElement:GetFullName() .. text)

	local Frame = Instance.new("Frame")
	Frame.BackgroundTransparency = 0.3
	Frame.BackgroundColor3 = Color3.new(0, 0, 0)
	Frame.Size = UDim2.new(0, 100, 0, kMaterialTooltipHeight)
	Frame.SizeConstraint = "RelativeYY"
	Frame.ZIndex = guiElement.ZIndex + 1
	Frame.Style = "DropShadow"
	Frame.AnchorPoint = Vector2.new(0.5, 0.5)
	-- We will set position down below...

	-- Parent to main frame so it doesn't get clipped by anything.
	Frame.Parent = GuiUtilities.kMainFrame

	self._tweakingTooltipFrame = nil

	-- Tooltips are positioned as children of main screen.
	-- The buttons they describe are in a scroll widget, so they can 
	-- move around.
	-- So we need to watch the buttons and dynmically reposition.
	-- Also, we need to worry about slopping over right/left edges.
	local function updateTooltipPosition()
		-- Don't let this be re-entrant.
		if (self._tweakingTooltipFrame == Frame) then 
			return
		end
		self._tweakingTooltipFrame = Frame

		local materialSquareAbsPosition = guiElement.AbsolutePosition
		local materialSquareAbsSize = guiElement.AbsoluteSize

		local absSize = Frame.AbsoluteSize

		local targetLocationX = materialSquareAbsPosition.X + materialSquareAbsSize.X/2
		local targetLocationY = materialSquareAbsPosition.Y - kMaterialTooltipHeight/2

		local containerSize = self._materialsFrame.AbsoluteSize

		-- Worry about slopping over either side.
		if (targetLocationX < absSize.X/2) then 
			targetLocationX = absSize.X/2
		end

		if (targetLocationX > containerSize.X - absSize.X/2) then 
			targetLocationX = containerSize.X - absSize.X/2
		end

		Frame.Position = UDim2.new(0, targetLocationX, 0, targetLocationY)

		self._tweakingTooltipFrame = nil
	end
	Frame:GetPropertyChangedSignal("AbsolutePosition"):connect(updateTooltipPosition)
	Frame:GetPropertyChangedSignal("AbsoluteSize"):connect(updateTooltipPosition)
	guiElement:GetPropertyChangedSignal("AbsolutePosition"):connect(updateTooltipPosition)
	updateTooltipPosition()

	local TextLabel = Instance.new("TextLabel", Frame)
	TextLabel.BackgroundTransparency = 1
	TextLabel.TextXAlignment = "Left"
	TextLabel.Text = text
	TextLabel.BorderSizePixel = 0
	TextLabel.TextColor3 = Color3.new(1, 1, 1)
	TextLabel.Size = UDim2.new(1, -30, 1, 0);
	TextLabel.Position = UDim2.new(0, 10, 0, 0);
	TextLabel.FontSize = "Size10"
	TextLabel.ZIndex = Frame.ZIndex
	TextLabel.TextStrokeColor3 = Color3.new(0, 0, 0)
	TextLabel.TextStrokeTransparency = 0.87

	Frame.Visible = false

	guiElement.MouseEnter:connect(function()
		self._activeToolTip.Value = Name
		Frame.Visible = true
		Frame.Size = UDim2.new(0, math.ceil(TextLabel.TextBounds.X + 36), Frame.Size.Y.Scale, Frame.Size.Y.Offset)
	end)
	self._activeToolTip.Changed:connect(function()
		if self._activeToolTip.Value ~= Name then
			Frame.Visible = false
		end
	end)
	guiElement.MouseLeave:connect(function()
		Frame.Visible = false
	end)
end

return MaterialsListClass


]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBXc6b9a1d5cc7c487caea825762f3b5356">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">MainPanel</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[local module = {}

local GuiUtilities = require(script.Parent.Libs.GuiUtilities)
local ImageButtonWithText = require(script.Parent.Libs.ImageButtonWithText)

local kMainButtonOuterSize = 53
local kMainButtonBorderSize = 1
local kMainButtonActualSize = (kMainButtonOuterSize - 2 * kMainButtonBorderSize)

local kMainButtonActualSizeUDim2 = UDim2.new(0, kMainButtonActualSize, 0, kMainButtonActualSize)
local kMainButtonOuterSizeUDim2 = UDim2.new(0, kMainButtonOuterSize, 0, kMainButtonOuterSize)

local kMainButtonFramePadding = UDim.new(0, 4)

local kMainButtonListFrame

--[[
	How tool modules work:
		Your ModuleScript should return a table. The table can contain the following functions
			On	= This function will be called when your tool is selected. Will hand in the mouse object, and the tool.
			Off	= This function will be called when your tool is deselected.
			BrushOperation = If this function is present, the tool will use the basic brushing functionality, and use this function as the operation
				operation(centerPoint, materialsTable, occupanciesTable, resolution, selectionSize, strength, desiredMaterial, brushShape, minBounds, maxBounds)
			FirstTimeSetup = A function that can hold back the majority of the code from being setup until the tool is used for the first time
]]


local modules =
{
	Brush = require(script.Parent.TerrainBrush),
	TerrainGeneration = require(script.Parent.TerrainGeneration),
	TerrainSmoother = require(script.Parent.TerrainSmoother),
	TerrainRegionEditor = require(script.Parent.TerrainRegionEditor)
}

local kMainButtonConfigs = 
{
	{
		Name = "Generate",
		Text = "Generate",
		Tip = 'Generate landscapes of terrain.',
		Icon = "rbxasset://textures/TerrainTools/mt_generate.png",
		Modules = {modules.TerrainGeneration},
		LayoutOrder = 1,
	}, 
	{
		Name = "Add",
		Text = "Add",
		Tip = 'Click and hold to add terrain.',
		Icon = "rbxasset://textures/TerrainTools/mt_add.png",
		Modules = {modules.Brush,},
		UsesMaterials = true,
		LayoutOrder = 2,
		DisablesPlaneLock = true,
	}, 
	{
		Name = "Subtract",
		Text = "Subtract",
		Tip = 'Click and hold to remove terrain.',
		Icon = "rbxasset://textures/TerrainTools/mt_subtract.png",
		Modules = {modules.Brush,},
		LayoutOrder = 3,
		DisablesPlaneLock = true,
		DisablesAutoColor = true,
	}, 
	{
		Name = "Paint",
		Text = "Paint",
		Tip = 'Paint the material of the terrain.',
		Icon = "rbxasset://textures/TerrainTools/mt_paint.png",
		Modules = {modules.Brush,},
		UsesMaterials = true,
		LayoutOrder = 4,
		DisablesAutoColor = true,
	}, 
	{
		Name = "Grow",
		Text = "Grow",
		Tip = 'Click and hold to grow and expand terrain.',
		Icon = "rbxasset://textures/TerrainTools/mt_grow.png",
		Modules = {modules.Brush,},
		UsesMaterials = true,
		LayoutOrder = 5,
	}, 
	{
		Name = "Erode",
		Text = "Erode",
		Tip = 'Click and hold to erode and remove terrain.',
		Icon = "rbxasset://textures/TerrainTools/mt_erode.png",
		Modules = {modules.Brush,},
		LayoutOrder = 6,
		DisablesAutoColor = true,
	}, 
	{
		Name = "Smooth",
		Text = "Smooth",
		Tip = 'Brush to smooth out rough or jagged terrain.',
		Icon = "rbxasset://textures/TerrainTools/mt_smooth.png",
		Modules = {modules.Brush, modules.TerrainSmoother,},
		LayoutOrder = 7,
		DisablesAutoColor = true,
	}, 
	{
		Name = "Regions",
		Text = "Regions",
		Tip = 'Manipulate regions of smooth terrain.',
		Icon = "rbxasset://textures/TerrainTools/mt_regions.png",
		Modules = {modules.TerrainRegionEditor,},
		LayoutOrder = 8,
	}, 
}


local function createMainButton(thePlugin, thePluginGui, mainButtonConfig, toggleFunction)	
	local buttonObj = ImageButtonWithText.new(mainButtonConfig.Name, 
		mainButtonConfig.LayoutOrder, 
		mainButtonConfig.Icon, 
		mainButtonConfig.Text, 
		kMainButtonActualSizeUDim2,
		UDim2.new(0,26,0,26), 
		UDim2.new(0,13,0,3),
		UDim2.new(1,0,0,22), 
		UDim2.new(0,0,1,-22))

	buttonObj:getButton().Parent = kMainButtonListFrame
	mainButtonConfig.ButtonObj = buttonObj

	mainButtonConfig.ButtonObj:getButton().MouseButton1Click:connect(toggleFunction);

	local pluginAction = thePlugin:CreatePluginAction(mainButtonConfig.Name, 
		mainButtonConfig.Text, 
		mainButtonConfig.Tip)
			
	pluginAction.Triggered:connect(function()
  		-- Make sure the plugin GUI is visible.
		thePluginGui.Enabled = true
		-- Toggle this mode.
		toggleFunction()
	end)
end


-- Creates the main panel for tools, and creates the scrolling frame for sub-content section.
-- The main panel will be resized dynamically when widget is stretched horizontally
-- The content section will resize to fill the rest of the vertical space that the main panel is not using.
-- Returns the main panel, and returns the sub-content section
function makeMainPanel()
	-- Make the main panel for tool buttons
	local section = GuiUtilities.MakeFixedHeightFrame("MainPanel",  100)	
	section.Parent = pluginGui
	section.Position = UDim2.new(0, 0, 0, 0)

	GuiUtilities.SetMainFrame(section)

	local buttonListFrame = Instance.new("Frame")
	buttonListFrame.Name = "ButtonListFrame"
	buttonListFrame.BackgroundTransparency = 1
	buttonListFrame.BorderSizePixel = 0
	buttonListFrame.Size = UDim2.new(1, 0, 1, 0)
	buttonListFrame.Position = UDim2.new(0, 0, 0, 0)
	buttonListFrame.Parent = section

	local uiGridLayout = Instance.new("UIGridLayout")
	uiGridLayout.CellSize = kMainButtonOuterSizeUDim2
	uiGridLayout.CellPadding = UDim2.new(0,3,0,0)
	uiGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left
	uiGridLayout.VerticalAlignment = Enum.VerticalAlignment.Top
	uiGridLayout.SortOrder = Enum.SortOrder.LayoutOrder
	uiGridLayout.Parent = buttonListFrame
	uiGridLayout.SortOrder = Enum.SortOrder.LayoutOrder

	local uiPadding = Instance.new("UIPadding")
	uiPadding.PaddingBottom = kMainButtonFramePadding
	uiPadding.PaddingLeft = kMainButtonFramePadding
	uiPadding.PaddingRight = kMainButtonFramePadding
	uiPadding.PaddingTop = kMainButtonFramePadding
	uiPadding.Parent = buttonListFrame

	-- Make the visual that sits behind the scrollbar and gives the scrollbar thumb visual contrast
	local scrollbarBackground = Instance.new('Frame')
	scrollbarBackground.Name = 'ScrollbarBackground'
	scrollbarBackground.BackgroundColor3 = Color3.fromRGB(238, 238, 238)
	scrollbarBackground.BorderColor3 = Color3.fromRGB(182, 182, 182)
	scrollbarBackground.Parent = pluginGui

	-- Make the content section scrolling frame
	local mainSpaceScrollingFrame = Instance.new("ScrollingFrame")
	mainSpaceScrollingFrame.Name = "MainSpace"
	mainSpaceScrollingFrame.VerticalScrollBarPosition = Enum.VerticalScrollBarPosition.Right
	mainSpaceScrollingFrame.VerticalScrollBarInset = Enum.ScrollBarInset.ScrollBar
	mainSpaceScrollingFrame.ElasticBehavior = Enum.ElasticBehavior.Never
	mainSpaceScrollingFrame.ScrollBarThickness = 17
	mainSpaceScrollingFrame.BorderSizePixel = 0
	mainSpaceScrollingFrame.BackgroundTransparency = 1
	mainSpaceScrollingFrame.ZIndex = 2
	mainSpaceScrollingFrame.TopImage = "http://www.roblox.com/asset/?id=1533255544"
	mainSpaceScrollingFrame.MidImage = "http://www.roblox.com/asset/?id=1535685612"
	mainSpaceScrollingFrame.BottomImage = "http://www.roblox.com/asset/?id=1533256504"

	local uiListLayout = Instance.new("UIListLayout")
	uiListLayout.Parent = mainSpaceScrollingFrame
	uiListLayout.SortOrder = Enum.SortOrder.LayoutOrder

	mainSpaceScrollingFrame.Parent = pluginGui

	-- The scrollbar backing should only be visible when the scrollbar is visible
	local function updateScrollbarBackingVisibility()
		scrollbarBackground.Visible = mainSpaceScrollingFrame.CanvasSize.Y.Offset > mainSpaceScrollingFrame.AbsoluteSize.Y
	end

	-- Adjust the vertical size of the main buttons panel and the main panel details space when the main panel buttons change the number of rows are being rendered
	local function updateMainSectionsSizesAndPositions()
		section.Size = UDim2.new(1, 0, 0, uiGridLayout.AbsoluteContentSize.Y+kMainButtonFramePadding.Offset*2)
		mainSpaceScrollingFrame.Size = UDim2.new(1, 0, 1, -section.AbsoluteSize.Y)
		mainSpaceScrollingFrame.Position = UDim2.new(0, 0, 0, section.AbsolutePosition.Y+section.AbsoluteSize.Y)
		scrollbarBackground.Size = UDim2.new(0, mainSpaceScrollingFrame.ScrollBarThickness-2, 1, -section.AbsoluteSize.Y-2)
		scrollbarBackground.Position = UDim2.new(1, -mainSpaceScrollingFrame.ScrollBarThickness+1, 0, section.AbsolutePosition.Y+section.AbsoluteSize.Y+1)
		updateScrollbarBackingVisibility()
	end
	uiGridLayout:GetPropertyChangedSignal("AbsoluteContentSize"):connect(updateMainSectionsSizesAndPositions)
	updateMainSectionsSizesAndPositions()

	-- Update the canvas size of the scrolling frame to fit the size of it's contents
	local function updateScrollingFrameCanvas()
		mainSpaceScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, uiListLayout.AbsoluteContentSize.Y)
		updateScrollbarBackingVisibility()
	end
	uiListLayout:GetPropertyChangedSignal("AbsoluteContentSize"):connect(updateScrollingFrameCanvas)
	updateScrollingFrameCanvas()

	contentFrame = mainSpaceScrollingFrame
	kMainButtonListFrame = buttonListFrame
end

local function fastSpawn(func)	-- Creates a new thread like Spawn() but executes the code immediately, without a frame delay.
	coroutine.wrap(func)()
end


module.Initialize = function(thePlugin, thePluginGui)
	on = false
	currentMainButtonConfig = nil
	plugin = thePlugin
	pluginGui = thePluginGui
	makeMainPanel()
	local mouse = plugin:GetMouse()
	local userInput = game:GetService('UserInputService')
	local prevCameraType = game.Workspace.CurrentCamera.CameraType

	for mainButtonIndex, mainButtonConfig in ipairs(kMainButtonConfigs) do
		createMainButton(thePlugin, 
			thePluginGui, 
			mainButtonConfig, 
			function()
				if not on or (currentMainButtonConfig ~= nil and mainButtonConfig ~= currentMainButtonConfig) then	--if off or on but current tool isn't the desired tool, then select this tool.
					Selected(mainButtonConfig)
				else
					Deselected()
				end
			end)
	end


	-- If the plugin gui is disabled, we should definitely be deselected.
	pluginGui:GetPropertyChangedSignal("Enabled"):Connect(function()
		if (not pluginGui.Enabled) then 
			Deselected()
		end
	end)

	function Selected(tool)
		if plugin then
			plugin:Activate(true)
		end

		if not userInput.MouseEnabled then
			prevCameraType = game.Workspace.CurrentCamera.CameraType
			game.Workspace.CurrentCamera.CameraType = Enum.CameraType.Fixed	
		end

		tool.ButtonObj:setSelected(true)

		on = true
		currentMainButtonConfig = tool

		--local toolModule = tool.Module or module.Brush
		for _, toolModule in pairs(tool.Modules) do
			fastSpawn(function()
				if toolModule.FirstTimeSetup and not toolModule.isSetup then
					toolModule.FirstTimeSetup(mouse, pluginGui, contentFrame)
					toolModule.isSetup = true
				end
				if toolModule.On then
					toolModule.On(tool)
				end
			end)
		end
	end
	
	function Deselected()
		if not userInput.MouseEnabled then
			game.Workspace.CurrentCamera.CameraType = prevCameraType		
		end

		on = false
		local lastTool = currentMainButtonConfig
		currentMainButtonConfig = nil

		if lastTool then
			lastTool.ButtonObj:setSelected(false)

			--local lastToolModule = lastTool.Module or module.Brush
			for _, lastToolModule in pairs(lastTool.Modules) do
				fastSpawn(function()
					if lastToolModule.Off then
						lastToolModule.Off()
					end
				end)
			end
		end
	end

	if plugin then
		plugin.Deactivation:connect(function()
			if on then
				Deselected()
			end
		end)
	end

end


return module]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="Script" referent="RBX13c85d1c5d314589bcd50e5db5cd4b3a">
			<Properties>
				<bool name="Disabled">false</bool>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">main</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[local mainPanelModule = require(script.parent.MainPanel)
local terrainBrushScriptModule = require(script.parent.TerrainBrush)

local kToolbarButtonText = "Editor"

local kMinWidthWidth = 249

-- A function to sync toolbar button 'active' state with plugin gui
-- visibility.
local function updateButtonActive(button, plugin)
	if plugin.Enabled then 
	  button:SetActive(true)
	else
	  button:SetActive(false)
	end
end

-- Create the toolbar, create the button to toggle terrain tools plugin GUI on/off.
local toolbar = plugin:CreateToolbar('TerrainToolsLuaToolbarName')
local toggleVisibilityButton = toolbar:CreateButton(kToolbarButtonText,
				'Add, remove, and modify terrain',
				"rbxasset://textures/TerrainTools/icon_terrain_big.png")

toggleVisibilityButton.ClickableWhenViewportHidden = true

local dockWidgetPluginGuiInfo = DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Left, false, false, 0, 0, kMinWidthWidth)

local pluginGui = plugin:CreateDockWidgetPluginGui("TerrainTools_PluginGui", dockWidgetPluginGuiInfo)
pluginGui.Title = "Terrain Editor"

toggleVisibilityButton.Click:connect(function()
	if pluginGui.Enabled then
		pluginGui.Enabled = false
	else
		pluginGui.Enabled = true
	end
end)


-- Immediately sync toolbar button active state with plugin gui visibility.   
updateButtonActive(toggleVisibilityButton, pluginGui)

-- Listen for changes in plugin gui visibility to keep toolbar button
-- active state synced.
pluginGui:GetPropertyChangedSignal("Enabled"):connect(function(property)
	updateButtonActive(toggleVisibilityButton, pluginGui)
end)     

-- Make sure plugin and pluginGui have nice names.
plugin.Name = "Terrain"
pluginGui.Name = "Tools"


-- Some craziness I don't fully understand.
local Terrain = workspace:WaitForChild('Terrain', 86400) or workspace:WaitForChild('Terrain')
while not Terrain.IsSmooth do
	Terrain.Changed:wait()
end

-- Add the main panel.
mainPanelModule.Initialize(plugin, pluginGui)





]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX45ba2d6b73284c6a81a1ada51bd66cb1">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">TerrainGeneration</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[--Stickmasterluke

local module = {}

local GuiUtilities = require(script.Parent.Libs.GuiUtilities)
local VerticallyScalingListFrame = require(script.Parent.Libs.VerticallyScalingListFrame)
local CollapsibleTitledSection = require(script.Parent.Libs.CollapsibleTitledSection)
local LabeledCheckbox = require(script.Parent.Libs.LabeledCheckbox)
local LabeledTextInput = require(script.Parent.Libs.LabeledTextInput)
local LabeledMultiChoice = require(script.Parent.Libs.LabeledMultiChoice)
local CustomTextButton = require(script.Parent.Libs.CustomTextButton)

local ProgressFrame = require(script.Parent.ProgressFrame)

local coreGui = game:GetService('CoreGui')
local changeHistoryService = game:GetService('ChangeHistoryService')
local terrain

local pluginGui
local screenGui

local terrainGenerationFrame 
local progressFrame 
local pauseButton
local cancelButton 
local barFill 


local kBiomesCheckboxHPadding = 24

local kBottomButtonsPadding = 20

local kProgressFrameObj = nil

kBiomeData = {
	Mountains = {
		LayoutOrder = 1,
		Text = "Mountains",
	},
	Canyons = {
		LayoutOrder = 2,
		Text = "Canyons",
	},
	Dunes = {
		LayoutOrder = 3,
		Text = "Dunes",
	},
	Arctic = {
		LayoutOrder = 4,
		Text = "Arctic",
	},
	Lavaflow = {
		LayoutOrder = 5,
		Text = "Lavaflow",
	},
	Hills = {
		LayoutOrder = 6,
		Text = "Hills",
	},
	Plains = {
		LayoutOrder = 7,
		Text = "Plains",
	},
	Marsh = {
		LayoutOrder = 8,
		Text = "Marsh",
	},
	Water = {
		LayoutOrder = 9,
		Text = "Water",
	},
}

local kBiomeSizeChoices = {
	{
		Id = "Small", 
		Data = 50, 
		Text = "Small (50)"
	},
	{
		Id = "Medium", 
		Data = 100, 
		Text = "Medium (100)"
	},
	{
		Id = "Large", 
		Data = 200, 
		Text = "Large (200)"
	},
	{
		Id = "Massive", 
		Data = 500, 
		Text = "Massive (500)"
	},
}

local kMapSizeChoices = {
	{
		Id = "Small", 
		Data = 128, 
		Text = "Small (128)"
	},
	{
		Id = "Medium", 
		Data = 256, 
		Text = "Medium (256)"
	},
	{
		Id = "Large", 
		Data = 512, 
		Text = "Large (512)"
	},
	{
		Id = "Massive", 
		Data = 1024, 
		Text = "Massive (1024)"
	},
}

local kSelectedBiomes = {
	['Mountains'] = true,
	['Canyons'] = false,
	['Dunes'] = false,
	['Arctic'] = false,
	['Lavaflow'] = false,
	['Hills'] = true,
	['Plains'] = true,
	['Marsh'] = false,
	['Water'] = false,
}


local on = false
local mouse = nil
local generating = false
local clearing = false
local paused = false
local cancelIt = false

local noise = math.noise
local min = math.min
local max = math.max
local sin = math.sin
local cos = math.cos
local floor = math.floor
local ceil = math.ceil
local sqrt = math.sqrt
local randomseed = math.randomseed
local random = math.random
local pi = math.pi
local tau = math.pi*2


---------Directly used in Generation---------
local masterSeed = 618033988
local mapWidth = 256
local mapHeight = 128
local biomeSize = 100
local generateCaves = false
local waterLevel = .48
local surfaceThickness = .018
local biomes = {}
---------------------------------------------

-------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------

local rock = Enum.Material.Rock
local snow = Enum.Material.Snow
local ice = Enum.Material.Glacier
local grass = Enum.Material.Grass
local ground = Enum.Material.Ground
local mud = Enum.Material.Mud
local slate = Enum.Material.Slate
local concrete = Enum.Material.Concrete
local lava = Enum.Material.CrackedLava
local basalt = Enum.Material.Basalt
local air = Enum.Material.Air
local sand = Enum.Material.Sand
local sandstone = Enum.Material.Sandstone
local water = Enum.Material.Water

local fill = rock

local range = {}
randomseed(6180339)
theseed={}
for i=1,999 do
	table.insert(theseed,math.random())
end

local function MakeMapSettingsFrame()
	-- Height will be adjusted.
	local mapSettingsObj = CollapsibleTitledSection.new('MapSettingsFrame', 
	  'Map Settings', 
	  true, 
	  true)

	-- The 'Map size' dropdown.
	-- The "Size" dropdown.	
	local sizeMultiChoiceObj = LabeledMultiChoice.new("MapSize", "Map Size", kMapSizeChoices)
	sizeMultiChoiceObj:SetValueChangedFunction(function(index) 
		mapWidth = kMapSizeChoices[index].Data
	end)
	sizeMultiChoiceObj:SetSelectedIndex(2)
		
	-- The 'Seed' text box.
	local seedTextBoxObj = LabeledTextInput.new("Seed", "Seed", masterSeed)
	local seedFrame = seedTextBoxObj:GetFrame()
	local function computeMasterSeed(text)
		local compositeNumber = 0
		for i=1,#text do
			local character = string.sub(text,i,i)
			local number = tonumber(character)
			if number then
				compositeNumber = (compositeNumber+6)*(number+5)
			else
				compositeNumber = (compositeNumber+7)*(string.byte(character)+3)
			end
			compositeNumber = compositeNumber%61803	--yes, this does need to be done after every character iteration, otherwise number loses precision by the end
		end
		masterSeed = compositeNumber
	end
	seedTextBoxObj:SetValueChangedFunction(computeMasterSeed)
	computeMasterSeed(tostring(masterSeed))

	-- The 'Caves' check box.
	local cavesObj = LabeledCheckbox.new("CavesFrame", "Caves", generateCaves)
	cavesObj:SetValueChangedFunction(function(value)
		generateCaves = value
	end)

	-- Make contents of collapsible frame an auto-scaling list.
	GuiUtilities.MakeFrameAutoScalingList(mapSettingsObj:GetContentsFrame())

	-- Add these as striped fields in parent 
	GuiUtilities.AddStripedChildrenToListFrame(mapSettingsObj:GetContentsFrame(), 
		{sizeMultiChoiceObj:GetFrame(), seedFrame, cavesObj:GetFrame()})

	return mapSettingsObj:GetSectionFrame()
end

local function AddBiomeCheckbox(parentFrame, biomeId)
	local checkboxObj = LabeledCheckbox.new(biomeId, kBiomeData[biomeId].Text, kSelectedBiomes[biomeId])
	checkboxObj:SetValueChangedFunction(function(value)
		kSelectedBiomes[biomeId] = value
	end)
	checkboxObj:UseSmallSize()
	checkboxObj:GetFrame().LayoutOrder = kBiomeData[biomeId].LayoutOrder
	checkboxObj:GetFrame().Parent = parentFrame
end

local function MakeBiomesCheckboxes()
	local frame = GuiUtilities.MakeFrame("Checkboxes")

	local padding = Instance.new("UIPadding")
	padding.PaddingLeft = UDim.new(0, GuiUtilities.StandardLineLabelLeftMargin)
	padding.PaddingRight = UDim.new(0, GuiUtilities.StandardLineLabelLeftMargin)
	padding.Parent = frame
	
	-- Make a grid to put checkboxes in.
	local uiGridLayout = Instance.new("UIGridLayout")
	uiGridLayout.CellSize = LabeledCheckbox.kMinFrameSize
	uiGridLayout.CellPadding = UDim2.new(0, 
		kBiomesCheckboxHPadding,
		0,
		GuiUtilities.kStandardVMargin)
	uiGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left
	uiGridLayout.VerticalAlignment = Enum.VerticalAlignment.Top
	uiGridLayout.Parent = frame
	uiGridLayout.SortOrder = Enum.SortOrder.LayoutOrder

	for biomeId, biomeData in pairs(kBiomeData) do 
		AddBiomeCheckbox(frame, biomeId)
	end

		-- Sync size with content size.
	GuiUtilities.AdjustHeightDynamicallyToLayout(frame, uiGridLayout)

	return frame
end

local function MakeBiomesCheckboxesWithTitle()
	local vsl = VerticallyScalingListFrame.new("bcwt")
	vsl:AddBottomPadding()
	
	local titleLabel = GuiUtilities.MakeFrameWithSubSectionLabel("Title", "Biomes")
	vsl:AddChild(titleLabel)

	-- Container for cells.
	local cellFrame = MakeBiomesCheckboxes()
	vsl:AddChild(cellFrame)

	return vsl:GetFrame()
end

local function MakeBiomesSettingsFrame()
	-- Height will be updated dynamically.
	local biomesSettingsObj = CollapsibleTitledSection.new('BiomesSettingsFrame', 
		'Biomes Settings', 
		true, 
		true)
	
	-- The "Size" dropdown.	
	local sizeMultiChoiceObj = LabeledMultiChoice.new("SizeFrame", "Biome Size", kBiomeSizeChoices)
	sizeMultiChoiceObj:SetValueChangedFunction(function(index) 
		biomeSize = kBiomeSizeChoices[index].Data
	end)
	sizeMultiChoiceObj:SetSelectedIndex(2)

	-- The "Biomes" checkbox frame.
	local biomeCheckboxesFrame = MakeBiomesCheckboxesWithTitle()
	
	-- Make contents of collapsible frame an auto-scaling list.
	GuiUtilities.MakeFrameAutoScalingList(biomesSettingsObj:GetContentsFrame())

	-- add these as striped fields in parent.
	GuiUtilities.AddStripedChildrenToListFrame(biomesSettingsObj:GetContentsFrame(), {sizeMultiChoiceObj:GetFrame(), 
		biomeCheckboxesFrame})

	return biomesSettingsObj:GetSectionFrame()
end

local function MakeButtonsFrame()
	local frame = GuiUtilities.MakeFixedHeightFrame("Buttons", GuiUtilities.kBottomButtonsFrameHeight)
	frame.BackgroundTransparency = 1

	local clearButtonObj = CustomTextButton.new("ClearButton", "Clear")
	clearButtonObj:getButton().Parent = frame
	clearButtonObj:getButton().Size = UDim2.new(0, GuiUtilities.kBottomButtonsWidth, 0, GuiUtilities.kBottomButtonsHeight)
	clearButtonObj:getButton().Position = UDim2.new(0.5, -GuiUtilities.kBottomButtonsWidth - kBottomButtonsPadding/2,
		 1, -GuiUtilities.kBottomButtonsHeight)

	local generateButtonObj = CustomTextButton.new("GenerateButton", "Generate")
	generateButtonObj:getButton().Parent = frame
	generateButtonObj:getButton().Size = UDim2.new(0, GuiUtilities.kBottomButtonsWidth, 0, GuiUtilities.kBottomButtonsHeight)
	generateButtonObj:getButton().Position = UDim2.new(0.5, kBottomButtonsPadding/2, 
		1, -GuiUtilities.kBottomButtonsHeight)
	
	return frame
end

function MakeTerrainGenerationFrame()
	-- Create top level container.  
	-- It has no title bar.
	local verticallyScalingListFrameObj = VerticallyScalingListFrame.new("GenerationFrame")
	local verticallyScalingListFrame = verticallyScalingListFrameObj:GetFrame()

	local mapSettingsFrame = MakeMapSettingsFrame()
	mapSettingsFrame.Parent = verticallyScalingListFrame
	mapSettingsFrame.LayoutOrder = 1

	local biomesSettingsFrame = MakeBiomesSettingsFrame()
	biomesSettingsFrame.Parent = verticallyScalingListFrame
	biomesSettingsFrame.LayoutOrder = 2

	local buttonsFrame = MakeButtonsFrame()
	buttonsFrame.Parent = verticallyScalingListFrame
	buttonsFrame.LayoutOrder = 3

	return verticallyScalingListFrame
end

module.FirstTimeSetup = function(mouse, thePluginGui, theContentFrame)
	mouse = theMouse
	pluginGui = thePluginGui
	contentFrame = theContentFrame

	screenGui = Instance.new("ScreenGui")
	screenGui.Name = "TerrainBrushGui"

	-- Add Terrain Generation Frame(s)
	terrainGenerationFrame = MakeTerrainGenerationFrame()
	terrainGenerationFrame.Parent = contentFrame

	kProgressFrameObj = ProgressFrame.new()
	progressFrame = kProgressFrameObj:GetFrame()
	progressFrame.Parent = screenGui

	pauseButton = kProgressFrameObj:GetPauseButton()
	cancelButton = kProgressFrameObj:GetCancelButton()
	barFill = kProgressFrameObj:GetFill()

	pauseButton.MouseButton1Down:connect(function()
		paused = not paused
		updatePausedButton()
	end)
	
	cancelButton.MouseButton1Down:connect(function()
		if not cancelIt then
			cancelIt = true
			print('Canceled')
		end
	end)

	terrainGenerationFrame.Buttons.GenerateButton.MouseButton1Down:connect(function()
		-- A frame delay allows time for the button image to visually respond before the generation starts chugging away with processing.
		wait()
		generate()
	end)
	
	terrainGenerationFrame.Buttons.ClearButton.MouseButton1Down:connect(function()
		clearTerrain()
	end)
end

function checkRange(v)
	if not range.min or v < range.min then
		range.min = v
	end
	if not range.max or v > range.max then
		range.max = v
	end
end

local function getPerlin(x,y,z,seed,scale,raw)
	local seed = seed or 0
	local scale = scale or 1
	if not raw then
		return noise(x/scale+(seed*17)+masterSeed,y/scale-masterSeed,z/scale-seed*seed)*.5 + .5 -- accounts for bleeding from interpolated line
	else
		return noise(x/scale+(seed*17)+masterSeed,y/scale-masterSeed,z/scale-seed*seed)
	end
end


local function getNoise(x,y,z,seed1)
	local x = x or 0
	local y = y or 0
	local z = z or 0
	local seed1 = seed1 or 7
	local wtf=x+y+z+seed1+masterSeed + (masterSeed-x)*(seed1+z) + (seed1-y)*(masterSeed+z)		-- + x*(y+z) + z*(masterSeed+seed1) + seed1*(x+y)			--x+y+z+seed1+masterSeed + x*y*masterSeed-y*z+(z+masterSeed)*x	--((x+y)*(y-seed1)*seed1)-(x+z)*seed2+x*11+z*23-y*17
	return theseed[(floor(wtf%(#theseed)))+1]
end

local function thresholdFilter(value, bottom, size)
	if value <= bottom then
		return 0
	elseif value >= bottom+size then
		return 1
	else
		return (value-bottom)/size
	end
end

local function ridgedFilter(value)	--absolute and flip for ridges. and normalize
	return value<.5 and value*2 or 2-value*2
end

local function ridgedFlippedFilter(value)					--unflipped
	return value < .5 and 1-value*2 or value*2-1
end

local function advancedRidgedFilter(value, cutoff)
	local cutoff = cutoff or .5
	value = value - cutoff
	return 1 - (value < 0 and -value or value) * 1/(1-cutoff)
end


local function fractalize(operation,x,y,z, operationCount, scale, offset, gain)
	local operationCount = operationCount or 3
	local scale = scale or .5
	local offset = 0
	local gain = gain or 1
	local totalValue = 0
	local totalScale = 0
	for i=1, operationCount do
		local thisScale = scale^(i-1)
		totalScale = totalScale + thisScale
		totalValue = totalValue + (offset + gain * operation(x,y,z,i))*thisScale
	end
	return totalValue/totalScale
end


local function mountainsOperation(x,y,z,i)
	return ridgedFilter(getPerlin(x,y,z,100+i,(1/i)*160))
end

local canyonBandingMaterial = {rock,mud,sand,sand,sandstone,sandstone,sandstone,sandstone,sandstone,sandstone,}

local function findBiomeInfo(choiceBiome,x,y,z,verticalGradientTurbulence)
	local choiceBiomeValue = .5
	local choiceBiomeSurface = grass
	local choiceBiomeFill = rock
	if choiceBiome == 'City' then
		choiceBiomeValue = .55
		choiceBiomeSurface = concrete
		choiceBiomeFill = slate
	elseif choiceBiome == 'Water' then
		choiceBiomeValue = .36+getPerlin(x,y,z,2,50)*.08
		choiceBiomeSurface = 
			(1-verticalGradientTurbulence < .44 and slate)
			or sand
	elseif choiceBiome == 'Marsh' then
		local preLedge = getPerlin(x+getPerlin(x,0,z,5,7,true)*10+getPerlin(x,0,z,6,30,true)*50,0,z+getPerlin(x,0,z,9,7,true)*10+getPerlin(x,0,z,10,30,true)*50,2,70)	--could use some turbulence
		local grassyLedge = thresholdFilter(preLedge,.65,0)
		local largeGradient = getPerlin(x,y,z,4,100)
		local smallGradient = getPerlin(x,y,z,3,20)
		local smallGradientThreshold = thresholdFilter(smallGradient,.5,0)
		choiceBiomeValue = waterLevel-.04
			+preLedge*grassyLedge*.025
			+largeGradient*.035
			+smallGradient*.025
		choiceBiomeSurface =
			(grassyLedge >= 1 and grass)
			or (1-verticalGradientTurbulence < waterLevel-.01 and mud)
			or (1-verticalGradientTurbulence < waterLevel+.01 and ground)
			or grass
		choiceBiomeFill = slate
	elseif choiceBiome == 'Plains' then
		local rivulet = ridgedFlippedFilter(getPerlin(x+getPerlin(x,y,z,17,40)*25,0,z+getPerlin(x,y,z,19,40)*25,2,200))
		local rivuletThreshold = thresholdFilter(rivulet,.01,0)

		local rockMap = thresholdFilter(ridgedFlippedFilter(getPerlin(x,0,z,101,7)),.3,.7)		--rocks
			 * thresholdFilter(getPerlin(x,0,z,102,50),.6,.05)									--zoning

		choiceBiomeValue = .5			--.51
		+getPerlin(x,y,z,2,100)*.02		--.05
		+rivulet*.05					--.02
		+rockMap*.05		--.03
		+rivuletThreshold*.005

		local verticalGradient = 1-((y-1)/(mapHeight-1))
		local surfaceGradient = verticalGradient*.5 + choiceBiomeValue*.5
		local thinSurface = surfaceGradient > .5-surfaceThickness*.4 and surfaceGradient < .5+surfaceThickness*.4
		choiceBiomeSurface =
			(rockMap>0 and rock)
			or (not thinSurface and mud)
			or (thinSurface and rivuletThreshold <=0 and water)
			or (1-verticalGradientTurbulence < waterLevel-.01 and sand)
			or grass
		choiceBiomeFill =
			(rockMap>0 and rock)
			or sandstone
	elseif choiceBiome == 'Canyons' then
		local canyonNoise = ridgedFlippedFilter(getPerlin(x,0,z,2,200))
		local canyonNoiseTurbed = ridgedFlippedFilter(getPerlin(x+getPerlin(x,0,z,5,20,true)*20,0,z+getPerlin(x,0,z,9,20,true)*20,2,200))
		local sandbank = thresholdFilter(canyonNoiseTurbed,0,.05)
		local canyonTop = thresholdFilter(canyonNoiseTurbed,.125,0)
		local mesaSlope = thresholdFilter(canyonNoise,.33,.12)
		local mesaTop = thresholdFilter(canyonNoiseTurbed,.49,0)
		choiceBiomeValue = .42
			+getPerlin(x,y,z,2,70)*.05
			+canyonNoise*.05
			+sandbank*.04										--canyon bottom slope
			+thresholdFilter(canyonNoiseTurbed,.05,0)*.08		--canyon cliff
			+thresholdFilter(canyonNoiseTurbed,.05,.075)*.04	--canyon cliff top slope
			+canyonTop*.01										--canyon cliff top ledge

			+thresholdFilter(canyonNoiseTurbed,.0575,.2725)*.01	--plane slope

			+mesaSlope*.06			--mesa slope
			+thresholdFilter(canyonNoiseTurbed,.45,0)*.14		--mesa cliff
			+thresholdFilter(canyonNoiseTurbed,.45,.04)*.025	--mesa cap
			+mesaTop*.02										--mesa top ledge
		choiceBiomeSurface =
			(1-verticalGradientTurbulence < waterLevel+.015 and sand)		--this for biome blending in to lakes
			or (sandbank>0 and sandbank<1 and sand)							--this for canyonbase sandbanks
			--or (canyonTop>0 and canyonTop<=1 and mesaSlope<=0 and grass)		--this for grassy canyon tops
			--or (mesaTop>0 and mesaTop<=1 and grass)							--this for grassy mesa tops
			or sandstone
		choiceBiomeFill = canyonBandingMaterial[ceil((1-getNoise(1,y,2))*10)]
	elseif choiceBiome == 'Hills' then
		local rivulet = ridgedFlippedFilter(getPerlin(x+getPerlin(x,y,z,17,20)*20,0,z+getPerlin(x,y,z,19,20)*20,2,200))^(1/2)
		local largeHills = getPerlin(x,y,z,3,60)
		choiceBiomeValue = .48
			+largeHills*.05
				+(.05
				+largeHills*.1
				+getPerlin(x,y,z,4,25)*.125)
				*rivulet
		local surfaceMaterialGradient = (1-verticalGradientTurbulence)*.9 + rivulet*.1
		choiceBiomeSurface =
			(surfaceMaterialGradient < waterLevel-.015 and mud)
			or (surfaceMaterialGradient < waterLevel and ground)
			or grass
		choiceBiomeFill = slate
	elseif choiceBiome == 'Dunes' then
		local duneTurbulence = getPerlin(x,0,z,227,20)*24
		local layer1 = ridgedFilter(getPerlin(x,0,z,201,40))
		local layer2 = ridgedFilter(getPerlin(x/10+duneTurbulence,0,z+duneTurbulence,200,48))
		choiceBiomeValue = .4+.1*(layer1 + layer2)
		choiceBiomeSurface = sand
		choiceBiomeFill = sandstone
	elseif choiceBiome == 'Mountains' then
		local rivulet = ridgedFlippedFilter(getPerlin(x+getPerlin(x,y,z,17,20)*20,0,z+getPerlin(x,y,z,19,20)*20,2,200))
		choiceBiomeValue = -.4		--.3
			+fractalize(mountainsOperation,x,y/20,z, 8, .65)*1.2
			+rivulet*.2
		choiceBiomeSurface =
			(verticalGradientTurbulence < .275 and snow)
			or (verticalGradientTurbulence < .35 and rock)
			or (verticalGradientTurbulence < .4 and ground)
			or (1-verticalGradientTurbulence < waterLevel and rock)
			or (1-verticalGradientTurbulence < waterLevel+.01 and mud)
			or (1-verticalGradientTurbulence < waterLevel+.015 and ground)
			or grass
	elseif choiceBiome == 'Lavaflow' then
		local crackX = x+getPerlin(x,y*.25,z,21,8,true)*5
		local crackY = y+getPerlin(x,y*.25,z,22,8,true)*5
		local crackZ = z+getPerlin(x,y*.25,z,23,8,true)*5
		local crack1 = ridgedFilter(getPerlin(crackX+getPerlin(x,y,z,22,30,true)*30,crackY,crackZ+getPerlin(x,y,z,24,30,true)*30,2,120))
		local crack2 = ridgedFilter(getPerlin(crackX,crackY,crackZ,3,40))*(crack1*.25+.75)
		local crack3 = ridgedFilter(getPerlin(crackX,crackY,crackZ,4,20))*(crack2*.25+.75)

		local generalHills = thresholdFilter(getPerlin(x,y,z,9,40),.25,.5)*getPerlin(x,y,z,10,60)

		local cracks = max(0,1-thresholdFilter(crack1,.975,0)-thresholdFilter(crack2,.925,0)-thresholdFilter(crack3,.9,0))

		local spireVec = CFrame.Angles(.7,.7,0)*Vector3.new(crackX,crackY,crackZ)
		local spires = thresholdFilter(getPerlin(spireVec.x/40,spireVec.y/300,spireVec.z/30,123,1),.6,.4)

		choiceBiomeValue = waterLevel+.02
			+cracks*(.5+generalHills*.5)*.02
			+generalHills*.05
			+spires*.3
			+((1-verticalGradientTurbulence > waterLevel+.01 or spires>0) and .04 or 0)			--This lets it lip over water

		choiceBiomeFill = (spires>0 and rock) or (cracks<1 and lava) or basalt
		choiceBiomeSurface = (choiceBiomeFill == lava and 1-verticalGradientTurbulence < waterLevel and basalt) or choiceBiomeFill
	elseif choiceBiome == 'Arctic' then
		local preBoundary = getPerlin(x+getPerlin(x,0,z,5,8,true)*5,y/8,z+getPerlin(x,0,z,9,8,true)*5,2,20)
		--local cliffs = thresholdFilter(preBoundary,.5,0)
		local boundary = ridgedFilter(preBoundary)
		local roughChunks = getPerlin(x,y/4,z,436,2)
		local boundaryMask = thresholdFilter(boundary,.8,.1)	--,.7,.25)
		local boundaryTypeMask = getPerlin(x,0,z,6,74)-.5
		local boundaryComp = 0
		if boundaryTypeMask < 0 then							--divergent
			boundaryComp = (boundary > (1+boundaryTypeMask*.5) and -.17 or 0)
							--* boundaryTypeMask*-2
		else													--convergent
			boundaryComp = boundaryMask*.1*roughChunks
							* boundaryTypeMask
		end
		choiceBiomeValue = .55
			+boundary*.05*boundaryTypeMask		--.1	--soft slope up or down to boundary
			+boundaryComp								--convergent/divergent effects
			+getPerlin(x,0,z,123,25)*.025	--*cliffs	--gentle rolling slopes

		choiceBiomeSurface = (1-verticalGradientTurbulence < waterLevel-.1 and ice) or (boundaryMask>.6 and boundaryTypeMask>.1 and roughChunks>.5 and ice) or snow
		choiceBiomeFill = ice
	end
	return choiceBiomeValue, choiceBiomeSurface, choiceBiomeFill
end

function findBiomeTransitionValue(biome,weight,value,averageValue)
	if biome == 'Arctic' then
		return (weight>.2 and 1 or 0)*value
	elseif biome == 'Canyons' then
		return (weight>.7 and 1 or 0)*value
	elseif biome == 'Mountains' then
		local weight = weight^3			--This improves the ease of mountains transitioning to other biomes
		return averageValue*(1-weight)+value*weight
	else
		return averageValue*(1-weight)+value*weight
	end
end

function updatePausedButton()
	pauseButton.Style = paused and 'RobloxRoundButton' or 'RobloxRoundDefaultButton'
	pauseButton.Text = paused and 'Resume' or 'Pause'
end

function generate()
	terrain = workspace.Terrain
	if not generating and not clearing and terrain then
		generating = true
		paused = false
		updatePausedButton()
		cancelIt = false
		progressFrame.Visible = true

		local mapWidth = mapWidth
		local biomeSize = biomeSize
		local biomeBlendPercent = .25	--(biomeSize==50 or biomeSize == 100) and .5 or .25
		local biomeBlendPercentInverse = 1-biomeBlendPercent
		local biomeBlendDistortion = biomeBlendPercent
		local smoothScale = .5/mapHeight

		local startTime = tick()
		biomes = {}
		for i,v in pairs(kSelectedBiomes) do
			if v then
				table.insert(biomes,i)
			end
		end
		if #biomes<=0 then
			table.insert(biomes,'Hills')
		end
		--local oMap = {}
		--local mMap = {}
		for x = 1, mapWidth do
			local oMapX = {}
			--oMap[x] = oMapX
			local mMapX = {}
			--mMap[x] = mMapX
			for z = 1, mapWidth do
				local biomeNoCave = false
				local cellToBiomeX = x/biomeSize + getPerlin(x,0,z,233,biomeSize*.3)*.25 + getPerlin(x,0,z,235,biomeSize*.05)*.075
				local cellToBiomeZ = z/biomeSize + getPerlin(x,0,z,234,biomeSize*.3)*.25 + getPerlin(x,0,z,236,biomeSize*.05)*.075
				local closestPoint = Vector3.new(0,0,0)
				local closestDistance = 1000000
				local biomePoints = {}
				for vx=-1,1 do
					for vz=-1,1 do
						local gridPointX = floor(cellToBiomeX+vx+.5)
						local gridPointZ = floor(cellToBiomeZ+vz+.5)
						--local pointX, pointZ = getBiomePoint(gridPointX,gridPointZ)
						local pointX = gridPointX+(getNoise(gridPointX,gridPointZ,53)-.5)*.75	--de-uniforming grid for vornonoi
						local pointZ = gridPointZ+(getNoise(gridPointX,gridPointZ,73)-.5)*.75

						local dist = sqrt((pointX-cellToBiomeX)^2 + (pointZ-cellToBiomeZ)^2)
						if dist < closestDistance then
							closestPoint = Vector3.new(pointX,0,pointZ)
							closestDistance = dist
						end
						table.insert(biomePoints,{
							x = pointX,
							z = pointZ,
							dist = dist,
							biomeNoise = getNoise(gridPointX,gridPointZ),
							weight = 0
						})
					end
				end
				local weightTotal = 0
				local weightPoints = {}
				for _,point in pairs(biomePoints) do
					local weight = point.dist == closestDistance and 1 or ((closestDistance / point.dist)-biomeBlendPercentInverse)/biomeBlendPercent
					if weight > 0 then
						local weight = weight^2.1		--this smooths the biome transition from linear to cubic InOut
						weightTotal = weightTotal + weight
						local biome = biomes[ceil(#biomes*(1-point.biomeNoise))]	--inverting the noise so that it is limited as (0,1]. One less addition operation when finding a random list index
						weightPoints[biome] = {
							weight = weightPoints[biome] and weightPoints[biome].weight + weight or weight
						}
					end
				end
				for biome,info in pairs(weightPoints) do
					info.weight = info.weight / weightTotal
					if biome == 'Arctic' then		--biomes that don't have caves that breach the surface
						biomeNoCave = true
					end
				end


				for y = 1, mapHeight do
					local oMapY = oMapX[y] or {}
					oMapX[y] = oMapY
					local mMapY = mMapX[y] or {}
					mMapX[y] = mMapY

					--[[local oMapY = {}
					oMapX[y] = oMapY
					local mMapY = {}
					mMapX[z] = mMapY]]


					local verticalGradient = 1-((y-1)/(mapHeight-1))
					local caves = 0
					local verticalGradientTurbulence = verticalGradient*.9 + .1*getPerlin(x,y,z,107,15)
					local choiceValue = 0
					local choiceSurface = lava
					local choiceFill = rock
	
					if verticalGradient > .65 or verticalGradient < .1 then
						--under surface of every biome; don't get biome data; waste of time.
						choiceValue = .5
					elseif #biomes == 1 then
						choiceValue, choiceSurface, choiceFill = findBiomeInfo(biomes[1],x,y,z,verticalGradientTurbulence)
					else
						local averageValue = 0
						--local findChoiceMaterial = -getNoise(x,y,z,19)
						for biome,info in pairs(weightPoints) do
							local biomeValue, biomeSurface, biomeFill = findBiomeInfo(biome,x,y,z,verticalGradientTurbulence)
							info.biomeValue = biomeValue
							info.biomeSurface = biomeSurface
							info.biomeFill = biomeFill
							local value = biomeValue * info.weight
							averageValue = averageValue + value
							--[[if findChoiceMaterial < 0 and findChoiceMaterial + weight >= 0 then
								choiceMaterial = biomeMaterial
							end
							findChoiceMaterial = findChoiceMaterial + weight]]
						end
						for biome,info in pairs(weightPoints) do
							local value = findBiomeTransitionValue(biome,info.weight,info.biomeValue,averageValue)
							if value > choiceValue then
								choiceValue = value
								choiceSurface = info.biomeSurface
								choiceFill = info.biomeFill
							end
						end
					end

					local preCaveComp = verticalGradient*.5 + choiceValue*.5

					local surface = preCaveComp > .5-surfaceThickness and preCaveComp < .5+surfaceThickness

					if generateCaves																--user wants caves
						and (not biomeNoCave or verticalGradient > .65)								--biome allows caves or deep enough
							and not (surface and (1-verticalGradient) < waterLevel+.005)			--caves only breach surface above waterlevel
								and not (surface and (1-verticalGradient) > waterLevel+.58) then	--caves don't go too high so that they don't cut up mountain tops
									local ridged2 = ridgedFilter(getPerlin(x,y,z,4,30))
									local caves2 = thresholdFilter(ridged2,.84,.01)
									local ridged3 = ridgedFilter(getPerlin(x,y,z,5,30))
									local caves3 = thresholdFilter(ridged3,.84,.01)
									local ridged4 = ridgedFilter(getPerlin(x,y,z,6,30))
									local caves4 = thresholdFilter(ridged4,.84,.01)
									local caveOpenings = (surface and 1 or 0) * thresholdFilter(getPerlin(x,0,z,143,62),.35,0)	--.45
									caves = caves2 * caves3 * caves4 - caveOpenings
									caves = caves < 0 and 0 or caves > 1 and 1 or caves
					end

					local comp = preCaveComp - caves

					local smoothedResult = thresholdFilter(comp,.5,smoothScale)

					---below water level				  -above surface		-no terrain
					if 1-verticalGradient < waterLevel and preCaveComp <= .5 and smoothedResult <= 0 then
						smoothedResult = 1
						choiceSurface = water
						choiceFill = water
						surface = true
					end

					oMapY[z] = (y == 1 and 1) or smoothedResult
					mMapY[z] = (y == 1 and lava) or (smoothedResult <= 0 and air) or (surface and choiceSurface) or choiceFill
				end
			end

			local regionStart = Vector3.new(mapWidth*-2+(x-1)*4,mapHeight*-2,mapWidth*-2)
			local regionEnd = Vector3.new(mapWidth*-2+x*4,mapHeight*2,mapWidth*2)
			local mapRegion = Region3.new(regionStart, regionEnd)
			terrain:WriteVoxels(mapRegion, 4, {mMapX}, {oMapX})

			local completionPercent = x/mapWidth
			barFill.Size = UDim2.new(completionPercent,0,1,0)

			wait()
			while paused and not cancelIt do
				wait()
			end
			if cancelIt then
				break
			end
		end
		changeHistoryService:SetWaypoint('Terrain Generation')
		progressFrame.Visible = false
		generating = false
		print('Generation Complete',tick()-startTime)
	end
end


module.On = function(theTool)
	screenGui.Parent = coreGui
	terrainGenerationFrame.Visible = true
	on = true
end

module.Off = function()
	on = false
	screenGui.Parent = nil
	terrainGenerationFrame.Visible = false
end

function clearTerrain()
	terrain = workspace.Terrain
	if not generating and not clearing and terrain then
		clearing = true
		terrain:Clear()
		changeHistoryService:SetWaypoint('Terrain Clear')
		clearing = false
	end
end


return module


]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX3a738f5730554a68880aac6268ffde3b">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">ProgressFrame</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- ProgressFrame.lua
--
-- Creates a frame that has a progress bar.
--
----------------------------------------

local GuiUtilities = require(script.Parent.Libs.GuiUtilities)

local kWidth = 400
local kHeight = 160

local kStandardHMargin = 10
local kStandardVMargin = 5
local kBarHeight = 25
local kButtonHeight = 30

local kTitleBarHeight = 25

ProgressFrameClass = {}
ProgressFrameClass.__index = ProgressFrameClass

function ProgressFrameClass.new(nameSuffix, labelText, initValue, initDisabled)
	local self = {}
	setmetatable(self, ProgressFrameClass)

	self._frame = Instance.new("Frame")
	self._frame.Name = "ProgressFrame"
	self._frame.Position = UDim2.new(0.5, -kWidth/2, 0, 0)
	self._frame.Size = UDim2.new(0, kWidth, 0, kHeight)
	self._frame.Style = Enum.FrameStyle.DropShadow

	self._frame.Visible = false

	self._bar = Instance.new("Frame")
	self._bar.Name = "Bar"
	self._bar.Parent = self._frame
	self._bar.Position = UDim2.new(0, kStandardHMargin, 0.39, 0)
	self._bar.Size = UDim2.new(1, -2 * kStandardHMargin, 0, kBarHeight)

	self._fill = Instance.new("ImageLabel")
	self._fill.Name = "ImageLabel"
	self._fill.Parent = self._bar
	self._fill.Image = "rbxasset://textures/TerrainTools/progress_bar.png"
	self._fill.ImageColor3 = Color3.new(0, 0.9, 0.4)
	self._fill.ImageRectOffset = Vector2.new(0, 256)
	self._fill.ImageRectSize = Vector2.new(0, -100)
	self._fill.ScaleType = Enum.ScaleType.Stretch

	self._pauseButton = Instance.new("TextButton")
	self._pauseButton.Name = "PauseButton"
	self._pauseButton.Text = "Pause"
	self._pauseButton.Parent = self._frame
	self._pauseButton.Style = Enum.ButtonStyle.RobloxRoundDefaultButton
	self._pauseButton.Size = UDim2.new(0.5, -2 * kStandardHMargin, 0, kButtonHeight)
	self._pauseButton.Position = UDim2.new(0, kStandardHMargin, 1, -kStandardVMargin - kButtonHeight)

	self._cancelButton = Instance.new("TextButton")
	self._cancelButton.Name = "CancelButton"
	self._cancelButton.Text = "Cancel"
	self._cancelButton.Parent = self._frame
	self._cancelButton.AnchorPoint = Vector2.new(1, 0)
	self._cancelButton.Style = Enum.ButtonStyle.RobloxRoundDefaultButton
	self._cancelButton.Size = UDim2.new(0.5, -2 * kStandardHMargin, 0, kButtonHeight)
	self._cancelButton.Position = UDim2.new(1, -kStandardHMargin, 1, -kStandardVMargin - kButtonHeight)

	self._titleLabel = Instance.new("TextLabel")
	self._titleLabel.Name = "TitleLabel"
	self._titleLabel.Text = "Progress"
	self._titleLabel.Parent = self._frame
	self._titleLabel.BackgroundTransparency = 0.75
	self._titleLabel.BackgroundColor3 = GuiUtilities.kStandardWhite
	self._titleLabel.Size = UDim2.new(1, 0, 0, kTitleBarHeight)

	self:_FixFont(self._pauseButton)
	self:_FixFont(self._cancelButton)
	self:_FixFont(self._titleLabel)

	return self
end

function ProgressFrameClass:_FixFont(frame)
	frame.Font = Enum.Font.SourceSansBold
	frame.TextColor3 = GuiUtilities.kStandardWhite
	frame.TextSize = 18
end

function ProgressFrameClass:GetFrame() 
	return self._frame
end

function ProgressFrameClass:GetPauseButton() 
	return self._pauseButton
end

function ProgressFrameClass:GetCancelButton() 
	return self._cancelButton
end

function ProgressFrameClass:GetFill() 
	return self._fill
end

return ProgressFrameClass]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBXfb208a3dd46946c1b9eae5641805133f">
			<Properties>
				<Content name="LinkedSource"><null></null></Content>
				<string name="Name">TerrainSmoother</string>
				<string name="ScriptGuid"></string>
				<ProtectedString name="Source"><![CDATA[--

local module = {}

module.FirstTimeSetup = function(theMouse, thePluginGui, theContentFrame)
	local terrain = game.Workspace.Terrain

	local function getCell(list, x, y, z)
		return list and list[x] and list[x][y] and list[x][y][z]
	end

	local function extendRange(x)		--This is very important. It allows cells to fully diminish or fully fill by lying to the algorithm
		return x * 1.5 - .25
	end

	function getNeighborOccupanciesFast(list, x, y, z, includeSelf)
		local fullNeighbor = false
		local emptyNeighbor = false
		local neighborOccupancies = includeSelf and extendRange(getCell(list, x, y, z)) or 0
		local totalNeighbors = includeSelf and 1 or 0
		for axis = 1, 3 do
			for offset = -1, 1, 2 do
				local neighbor = nil
				if axis == 1 then
					neighbor = list[x + offset] and list[x + offset][y][z]
				elseif axis == 2 then
					neighbor = list[x][y + offset] and list[x][y + offset][z]
				elseif axis == 3 then
					neighbor = list[x][y][z + offset]
				end
				if neighbor then
					if neighbor >= 1 then
						fullNeighbor = true
					end
					if neighbor <= 0 then
						emptyNeighbor = true
					end
					neighbor = extendRange(neighbor)
					totalNeighbors = totalNeighbors + 1
					neighborOccupancies = neighborOccupancies + neighbor
				end
			end
		end
		return neighborOccupancies / (totalNeighbors > 0 and totalNeighbors or extendRange(getCell(list, x, y, z))), fullNeighbor, emptyNeighbor
	end

	function getNeighborOccupancies(list, x, y, z, includeSelf, range)
		local fullNeighbor = false
		local emptyNeighbor = false
		local range = range or 1
		local neighborOccupancies = 0
		local totalNeighbors = 0
		local sqrt = math.sqrt
		for ix = -range, range do
			for iy = -range, range do
				for iz = -range, range do
					if includeSelf or not (ix == 0 and iy == 0 and iz == 0) then
						local neighbor = getCell(list, x + ix, y + iy, z + iz)
						if neighbor then
							local distanceScale = 1 - (sqrt(ix * ix + iy * iy + iz * iz) / (range * 2))
							if neighbor >= 1 then
								fullNeighbor = true
							end
							if neighbor <= 0 then
								emptyNeighbor = true
							end
							neighbor = extendRange(neighbor)
							totalNeighbors = totalNeighbors + 1 * distanceScale
							neighborOccupancies = neighborOccupancies + neighbor * distanceScale
						end
					end
				end
			end
		end
		return neighborOccupancies / (totalNeighbors > 0 and totalNeighbors or extendRange(getCell(list, x, y, z))), fullNeighbor, emptyNeighbor
	end


	function operation(centerPoint, materials, occupancies, resolution, selectionSize, strength, desiredMaterial, brushType, minBounds, maxBounds)
		local region = Region3.new(minBounds, maxBounds)
		local readMaterials, readOccupancies = terrain:ReadVoxels(region, resolution)

		local radius = selectionSize * .5 * resolution

		local materialAir = Enum.Material.Air

		for ix, vx in ipairs(readOccupancies) do
			local cellVectorX = minBounds.x + (ix - .5) * resolution - centerPoint.x

			for iy, vy in pairs(vx) do
				local cellVectorY = minBounds.y + (iy - .5) * resolution - centerPoint.y

				for iz, cellOccupancy in pairs(vy) do
					local cellVectorZ = minBounds.z + (iz - .5) * resolution - centerPoint.z

					local cellMaterial = materials[ix][iy][iz]
					local distance = math.sqrt(cellVectorX * cellVectorX + cellVectorY * cellVectorY + cellVectorZ * cellVectorZ)
		
					local magnitudePercent = 1
					local brushOccupancy = 1
					if brushType == 'Sphere' then
						magnitudePercent = math.cos(math.min(1, distance / (radius + resolution * .5)) * math.pi * .5)
						brushOccupancy = math.max(0, math.min(1, (radius + .5 * resolution - distance) / resolution))
					elseif brushType == 'Box' then
						--leave as default
					end

					if brushOccupancy >= .5 then
						local neighborOccupancies, fullNeighbor, emptyNeighbor = getNeighborOccupancies(readOccupancies, ix, iy, iz, true, 1)
						local difference = (neighborOccupancies - cellOccupancy) * (strength + .1) * .5 * brushOccupancy * magnitudePercent

						if not fullNeighbor and difference > 0 then
							difference = 0
						elseif not emptyNeighbor and difference < 0 then
							difference = 0
						end

						if readMaterials[ix][iy][iz] == materialAir or cellOccupancy <= 0 and difference > 0 then
							materials[ix][iy][iz] = desiredMaterial
						end
						if difference ~= 0 then
							occupancies[ix][iy][iz] = math.max(0, math.min(1, cellOccupancy + difference))
						end
					end
				end
			end
		end
	end

	module.BrushOperation = operation

end

return module

]]></ProtectedString>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
		</Item>
		<Item class="Folder" referent="RBX2f342756a9c74f27aa2e340a820120ee">
			<Properties>
				<string name="Name">Libs</string>
				<BinaryString name="Tags"></BinaryString>
			</Properties>
			<Item class="ModuleScript" referent="RBXc102800747574d78b27a05a43941168e">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">LabeledCheckbox</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- LabeledCheckbox.lua
--
-- Creates a frame containing a label and a checkbox.
--
----------------------------------------
GuiUtilities = require(script.Parent.GuiUtilities)

local kCheckboxWidth = GuiUtilities.kCheckboxWidth

local kMinTextSize = 14
local kMinHeight = 24
local kMinLabelWidth = GuiUtilities.kCheckboxMinLabelWidth
local kMinMargin = GuiUtilities.kCheckboxMinMargin
local kMinButtonWidth = kCheckboxWidth;

local kMinLabelSize = UDim2.new(0, kMinLabelWidth, 0, kMinHeight)
local kMinLabelPos = UDim2.new(0, kMinButtonWidth + kMinMargin, 0, kMinHeight/2)

local kMinButtonSize = UDim2.new(0, kMinButtonWidth, 0, kMinButtonWidth)
local kMinButtonPos = UDim2.new(0, 0, 0, kMinHeight/2)

local kCheckImageWidth = 8
local kMinCheckImageWidth = kCheckImageWidth

local kCheckImageSize = UDim2.new(0, kCheckImageWidth, 0, kCheckImageWidth)
local kMinCheckImageSize = UDim2.new(0, kMinCheckImageWidth, 0, kMinCheckImageWidth)

local kEnabledCheckImage = "rbxasset://textures/TerrainTools/icon_tick.png"
local kDisabledCheckImage = "rbxasset://textures/TerrainTools/icon_tick_grey.png"
local kCheckboxFrameImage = "rbxasset://textures/TerrainTools/checkbox_square.png"
LabeledCheckboxClass = {}
LabeledCheckboxClass.__index = LabeledCheckboxClass

LabeledCheckboxClass.kMinFrameSize = UDim2.new(0, kMinLabelWidth + kMinMargin + kMinButtonWidth, 0, kMinHeight)


function LabeledCheckboxClass.new(nameSuffix, labelText, initValue, initDisabled)
	local self = {}
	setmetatable(self, LabeledCheckboxClass)

	local initValue = not not initValue
	local initDisabled = not not initDisabled

	local frame = GuiUtilities.MakeStandardFixedHeightFrame("CBF" .. nameSuffix)

	local fullBackgroundButton = Instance.new("TextButton")
	fullBackgroundButton.Name = "FullBackground"
	fullBackgroundButton.Parent = frame
	fullBackgroundButton.BackgroundTransparency = 1
	fullBackgroundButton.Size = UDim2.new(1, 0, 1, 0)
	fullBackgroundButton.Position = UDim2.new(0, 0, 0, 0)
	fullBackgroundButton.Text = ""

	local label = GuiUtilities.MakeStandardPropertyLabel(labelText)
	label.Parent = fullBackgroundButton

	local button = Instance.new('ImageButton')
	button.Name = 'Button'
	button.Size = UDim2.new(0, kCheckboxWidth, 0, kCheckboxWidth)
	button.AnchorPoint = Vector2.new(0, .5)
	button.BackgroundTransparency = 0
	button.Position = UDim2.new(0, GuiUtilities.StandardLineElementLeftMargin, .5, 0)
	button.Parent = fullBackgroundButton
	button.Image = kCheckboxFrameImage
	button.BorderSizePixel = 0
	button.AutoButtonColor = false
	
	local checkImage = Instance.new("ImageLabel")
	checkImage.Name = "CheckImage"
	checkImage.Parent = button
	checkImage.Image = kEnabledCheckImage
	checkImage.Visible = false
	checkImage.Size = kCheckImageSize
	checkImage.AnchorPoint = Vector2.new(0.5, 0.5)
	checkImage.Position = UDim2.new(0.5, 0, 0.5, 0)
	checkImage.BackgroundTransparency = 1
	checkImage.BorderSizePixel = 0

	self._frame = frame
	self._button = button
	self._label = label
	self._checkImage = checkImage
	self._fullBackgroundButton = fullBackgroundButton
	self._useDisabledOverride = false
	self._disabledOverride = false
	self._disabled = not disabled
	self:SetDisabled(disabled)

	self._value = not initValue
	self:SetValue(initValue)

	self:_SetupMouseClickHandling()

	return self
end


function LabeledCheckboxClass:_MaybeToggleState()
	if not self._disabled then
		self:SetValue(not self._value)
	end
end

function LabeledCheckboxClass:_SetupMouseClickHandling()
	self._button.MouseButton1Down:connect(function()
		self:_MaybeToggleState()
	end)

	self._fullBackgroundButton.MouseButton1Down:connect(function()
		self:_MaybeToggleState()
	end)
end

function LabeledCheckboxClass:_HandleUpdatedValue()
	self._checkImage.Visible = self:GetValue()

	if (self._valueChangedFunction) then 
		self._valueChangedFunction(self:GetValue())
	end
end

-- Small checkboxes are a different entity.
-- All the bits are smaller.
-- Fixed width instead of flood-fill.
-- Box comes first, then label.
function LabeledCheckboxClass:UseSmallSize()
	self._label.TextSize = kMinTextSize
	self._label.Size = kMinLabelSize
	self._label.Position = kMinLabelPos
	self._label.TextXAlignment = Enum.TextXAlignment.Left

	self._button.Size = kMinButtonSize    
	self._button.Position = kMinButtonPos

	self._checkImage.Size = kMinCheckImageSize

	self._frame.Size = LabeledCheckboxClass.kMinFrameSize
	self._frame.BackgroundTransparency = 1
end

function LabeledCheckboxClass:GetFrame()
	return self._frame
end

function LabeledCheckboxClass:GetValue()
	-- If button is disabled, and we should be using a disabled override, 
	-- use the disabled override.
	if (self._disabled and self._useDisabledOverride) then 
		return self._disabledOverride
	else
		return self._value
	end
end

function LabeledCheckboxClass:GetLabel()
	return self._label
end

function LabeledCheckboxClass:GetButton()
	return self._getButton
end

function LabeledCheckboxClass:SetValueChangedFunction(vcFunction) 
	self._valueChangedFunction = vcFunction
end

function LabeledCheckboxClass:SetDisabled(newDisabled)
	local newDisabled = not not newDisabled

	local originalValue = self:GetValue()

	if newDisabled ~= self._disabled then
		self._disabled = newDisabled

		-- if we are no longer disabled, then we don't need or want 
		-- the override any more.  Forget it.
		if (not self._disabled) then 
			self._useDisabledOverride = false
		end

		if (newDisabled) then 
			self._checkImage.Image = kDisabledCheckImage
		else
			self._checkImage.Image = kEnabledCheckImage
		end

		self._label.TextColor3 = self._disabled and GuiUtilities.kDisabledTextColor or GuiUtilities.kStandardTextColor
		self._button.BackgroundColor3 = self._disabled and GuiUtilities.kButtonDisabledBackgroundColor or GuiUtilities.kButtonStandardBackgroundColor
		self._button.BorderColor3 = self._disabled and GuiUtilities.kButtonDisabledBorderColor or GuiUtilities.kButtonStandardBorderColor
		if self._disabledChangedFunction then
			self._disabledChangedFunction(self._disabled)
		end
	end

	local newValue = self:GetValue()
	if (newValue ~= oldValue) then 
		self:_HandleUpdatedValue()
	end
end

function LabeledCheckboxClass:DisableWithOverrideValue(overrideValue)
	-- Disable this checkbox.  While disabled, force value to override
	-- value.
	local oldValue = self:GetValue()
	self._useDisabledOverride = true
	self._disabledOverride = overrideValue
	self:SetDisabled(true)
	local newValue = self:GetValue()
	if (oldValue ~= newValue) then 
		self:_HandleUpdatedValue()
	end		
end

function LabeledCheckboxClass:GetDisabled()
	return self._disabled
end

function LabeledCheckboxClass:SetValue(newValue)
	local newValue = not not newValue
	
	if newValue ~= self._value then
		self._value = newValue

		self:_HandleUpdatedValue()
	end
end

return LabeledCheckboxClass]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX563304c1866f4ae48084bbf887c2b981">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">CollapsibleTitledSection</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- CollapsibleTitledSectionClass
--
-- Creates a section with a title label:
--
-- "SectionXXX"
--     "TitleBarVisual"
--     "Contents"
--
-- Requires "parent" and "sectionName" parameters and returns the section and its contentsFrame
-- The entire frame will resize dynamically as contents frame changes size.
--
--  "showTitle" is true by default and will not display the section title if false
-- Both "minimizeable" and "minimizedByDefault" are false by default
-- These parameters define if the section will have an arrow button infront of the title label, 
-- which the user may use to hide the section's contents
--
----------------------------------------
GuiUtilities = require(script.Parent.GuiUtilities)

local kRightButtonAsset = "rbxasset://textures/TerrainTools/button_arrow.png"
local kDownButtonAsset = "rbxasset://textures/TerrainTools/button_arrow_down.png"

local kArrowSize = 9
local kDoubleClickTimeSec = 0.5

CollapsibleTitledSectionClass = {}
CollapsibleTitledSectionClass.__index = CollapsibleTitledSectionClass


function CollapsibleTitledSectionClass.new(nameSuffix, titleText, showTitle, minimizable, minimizedByDefault)
	local self = {}
	setmetatable(self, CollapsibleTitledSectionClass)

	showTitle = showTitle or showTitle == nil
	
	self._minimized = minimizedByDefault
	self._minimizable = minimizable

	self._titleBarHeight = showTitle and GuiUtilities.kTitleBarHeight or 0

	local frame = Instance.new('Frame')
	frame.Name = 'CTSection' .. nameSuffix
	frame.BackgroundTransparency = 1
	self._frame = frame

	local uiListLayout = Instance.new('UIListLayout')
	uiListLayout.SortOrder = Enum.SortOrder.LayoutOrder
	uiListLayout.Parent = frame
	self._uiListLayout = uiListLayout

	local contentsFrame = Instance.new('Frame')
	contentsFrame.Name = 'Contents'
	contentsFrame.BackgroundTransparency = 1
	contentsFrame.Size = UDim2.new(1, 0, 0, 1)
	contentsFrame.Position = UDim2.new(0, 0, 0, titleBarSize)
	contentsFrame.Parent = frame
	contentsFrame.LayoutOrder = 2
	self._contentsFrame = contentsFrame

	uiListLayout:GetPropertyChangedSignal('AbsoluteContentSize'):connect(function()
		self:_UpdateSize()
	end)
	self:_UpdateSize()

	if showTitle then
		self:_CreateTitleBar(titleText)
	end

	return self
end


function CollapsibleTitledSectionClass:GetSectionFrame()
	return self._frame
end

function CollapsibleTitledSectionClass:GetContentsFrame()
	return self._contentsFrame
end

function CollapsibleTitledSectionClass:_UpdateSize()
	local totalSize = self._uiListLayout.AbsoluteContentSize.Y
	if self._minimized then
		totalSize = self._titleBarHeight
	end
	self._frame.Size = UDim2.new(1, 0, 0, totalSize)
end

function CollapsibleTitledSectionClass:_UpdateMinimizeButton()
	-- We can't rotate it because rotated images don't get clipped by parents.
	-- This is all in a scroll widget.
	-- :(
	if (self._minimized) then 
		self._minimizeButton.Image = kRightButtonAsset
	else
		self._minimizeButton.Image = kDownButtonAsset
	end
end

function CollapsibleTitledSectionClass:_ToggleCollapsedState()
	self._minimized = not self._minimized
	self._contentsFrame.Visible = not self._minimized
	self:_UpdateMinimizeButton()
	self:_UpdateSize()
end

function CollapsibleTitledSectionClass:_CreateTitleBar(titleText)
	local titleTextOffset = self._titleBarHeight

	local titleBar = Instance.new('ImageButton')
	titleBar.AutoButtonColor = false
	titleBar.Name = 'TitleBarVisual'
	titleBar.BorderSizePixel = 0
	titleBar.Position = UDim2.new(0, 0, 0, 0)
	titleBar.Size = UDim2.new(1, 0, 0, self._titleBarHeight)
	titleBar.BackgroundColor3 = Color3.new(.85, .85, .85)        --todo: input spec section title background color
	titleBar.Parent = self._frame
	titleBar.LayoutOrder = 1

	local titleLabel = Instance.new('TextLabel')
	titleLabel.Name = 'TitleLabel'
	titleLabel.BackgroundTransparency = 1
	titleLabel.Font = Enum.Font.SourceSansBold                --todo: input spec font
	titleLabel.TextSize = 15                                  --todo: input spec font size
	titleLabel.TextColor3 = GuiUtilities.kStandardTextColor
	titleLabel.TextXAlignment = Enum.TextXAlignment.Left
	titleLabel.Text = titleText
	titleLabel.Position = UDim2.new(0, titleTextOffset, 0, 0)
	titleLabel.Size = UDim2.new(1, -titleTextOffset, 1, GuiUtilities.kTextVerticalFudge)
	titleLabel.Parent = titleBar

	if self._minimizable then
		self._minimizeButton = Instance.new('ImageButton')
		self._minimizeButton.Name = 'MinimizeSectionButton'
		self._minimizeButton.Image = kRightButtonAsset              --todo: input arrow image from spec
		self._minimizeButton.Size = UDim2.new(0, kArrowSize, 0, kArrowSize)
		self._minimizeButton.AnchorPoint = Vector2.new(0.5, 0.5)
		self._minimizeButton.Position = UDim2.new(0, self._titleBarHeight*.5,
			 0, self._titleBarHeight*.5)
		self._minimizeButton.BackgroundTransparency = 1

		self._minimizeButton.MouseButton1Down:connect(function()
			self:_ToggleCollapsedState()
		end)
		self:_UpdateMinimizeButton()
		self._minimizeButton.Parent = titleBar
	end

	self._latestClickTime = 0
	titleBar.MouseButton1Down:connect(function()
		local now = tick()	
		if (now - self._latestClickTime < kDoubleClickTimeSec) then 
			self:_ToggleCollapsedState()
			self._latestClickTime = 0
		else
			self._latestClickTime = now
		end
	end)
end

return CollapsibleTitledSectionClass]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX65f907a04c4a48b087dfd1c9437d9b12">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">ImageButtonWithText</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- ImageButtonWithText.lua
--
-- An image button with text underneath.  Standardized hover, clicked, and 
-- selected states.
--
----------------------------------------
GuiUtilities = require(script.Parent.GuiUtilities)

ImageButtonWithTextClass = {}
ImageButtonWithTextClass.__index = ImageButtonWithTextClass

local kSelectedBaseTransparency = 0.2
local kAdditionalTransparency = 0.1

function ImageButtonWithTextClass.new(name, 
		layoutOrder, 
		icon, 
		text, 
		buttonSize,
		imageSize, 
		imagePos, 
		textSize, 
		textPos)
	local self = {}
	setmetatable(self, ImageButtonWithTextClass)

	local button = Instance.new("ImageButton")
	button.Name = name
	button.AutoButtonColor = false
	button.Size = buttonSize
	button.BorderSizePixel = 1
	-- Image-with-text button has translucent background and "selected" background color.
	-- When selected we set transluency to not-zero so we see selected color.
	button.BackgroundTransparency = 1 
	button.BackgroundColor3 = GuiUtilities.kStandardWhite

	button.LayoutOrder = layoutOrder

	local buttonIcon = Instance.new("ImageLabel")
	buttonIcon.BackgroundTransparency = 1
	buttonIcon.Image = icon or ""
	buttonIcon.Size = imageSize
	buttonIcon.Position = imagePos
	buttonIcon.Parent = button

	local textLabel = Instance.new("TextLabel")
	textLabel.BackgroundTransparency = 1
	textLabel.Text = text
	textLabel.Size = textSize
	textLabel.Position = textPos
	textLabel.TextScaled = true
	textLabel.Font = Enum.Font.SourceSans
	textLabel.Parent = button

	local uiTextSizeConstraint = Instance.new("UITextSizeConstraint")
	-- Spec asks for fontsize of 12 pixels, but in Roblox the text font sizes look smaller than the mock
	--Note: For this font the Roblox text size is 25.7% larger than the design spec. 
	uiTextSizeConstraint.MaxTextSize = 15		                                          
	uiTextSizeConstraint.Parent = textLabel

	self._button = button
	self._clicked = false
	self._hovered = false
	self._selected = false

	button.InputBegan:connect(function(input)
		if (input.UserInputType == Enum.UserInputType.MouseMovement) then               
			self._hovered = true
			self:_updateButtonVisual()
		end
	end)


	button.InputEnded:connect(function(input)
		if (input.UserInputType == Enum.UserInputType.MouseMovement) then               
			self._hovered = false
			self._clicked = false
			self:_updateButtonVisual()
		end
	end)    

	button.MouseButton1Down:connect(function()
		self._clicked = true
		self:_updateButtonVisual()
	end)

	button.MouseButton1Up:connect(function()
		self._clicked = false
		self:_updateButtonVisual()
	end)
	
	self:_updateButtonVisual()

	return self
end

function ImageButtonWithTextClass:_updateButtonVisual()
	-- Possibilties:
	if (self._clicked) then 
		-- This covers 'clicked and selected' or 'clicked'
		self._button.BackgroundColor3 = GuiUtilities.kButtonSelectedColor
		self._button.BorderColor3 = GuiUtilities.kButtonBorderSelectedColor
		if (self._selected) then 
			self._button.BackgroundTransparency = GuiUtilities.kButtonBackgroundIntenseTransparency
		else
			self._button.BackgroundTransparency = GuiUtilities.kButtonBackgroundTransparency
		end
	elseif (self._hovered) then 
		-- This covers 'hovered and selected' or 'hovered'
		self._button.BackgroundColor3 = GuiUtilities.kButtonHoverColor
		self._button.BorderColor3 = GuiUtilities.kButtonBorderHoverColor
		if (self._selected) then 
			self._button.BackgroundTransparency = GuiUtilities.kButtonBackgroundIntenseTransparency
		else
			self._button.BackgroundTransparency = GuiUtilities.kButtonBackgroundTransparency
		end
	elseif (self._selected) then 
		-- This covers 'selected'
		self._button.BackgroundColor3 = GuiUtilities.kButtonSelectedColor
		self._button.BorderColor3 = GuiUtilities.kButtonBorderSelectedColor
		self._button.BackgroundTransparency = GuiUtilities.kButtonBackgroundTransparency
	else
		-- This covers 'no special state'
		self._button.BackgroundColor3 = GuiUtilities.kStandardWhite
		self._button.BackgroundTransparency = 1
	end
end

function ImageButtonWithTextClass:getButton()
	return self._button
end

function ImageButtonWithTextClass:setSelected(selected)
	self._selected = selected
	self:_updateButtonVisual()
end
 
function ImageButtonWithTextClass:getSelected()
	return self._selected
end


return ImageButtonWithTextClass]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX0c507fcc4bf642d79d1b1f2be01fdc25">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">LabeledSlider</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- LabeledSlider.lua
--
-- Creates a frame containing a label and a slider control.
--
----------------------------------------
GuiUtilities = require(script.Parent.GuiUtilities)
rbxGuiLibrary = require(script.Parent.RbxGui)

local kSliderWidth = 100

local kSliderThumbImage = "rbxasset://textures/TerrainTools/sliderbar_button.png"
local kPreThumbImage = "rbxasset://textures/TerrainTools/sliderbar_blue.png"
local kPostThumbImage = "rbxasset://textures/TerrainTools/sliderbar_grey.png"

local kThumbSize = 13

local kSteps = 100

LabeledSliderClass = {}
LabeledSliderClass.__index = LabeledSliderClass

function LabeledSliderClass.new(nameSuffix, labelText, sliderIntervals, defaultValue)
	local self = {}
	setmetatable(self, LabeledSliderClass)

	self._valueChangedFunction = nil

	local sliderIntervals = sliderIntervals or 100
	local defaultValue = defaultValue or 1

	local frame = GuiUtilities.MakeStandardFixedHeightFrame('Slider' .. nameSuffix)
	self._frame = frame

	local label = GuiUtilities.MakeStandardPropertyLabel(labelText)
	label.Parent = frame
	self._label = label

	self._value = defaultValue

	 --steps, width, position
	 local slider, sliderValue = rbxGuiLibrary.CreateSlider(sliderIntervals, 
		kSteps, 
		UDim2.new(0, 0, .5, -3))
	self._slider = slider
	self._sliderValue = sliderValue
	-- Some tweaks to make slider look nice.
	-- Hide the existing bar.
	slider.Bar.BackgroundTransparency = 1
	-- Replace slider thumb image.
	self._thumb = slider.Bar.Slider
	self._thumb.Image = kSliderThumbImage
	self._thumb.AnchorPoint = Vector2.new(0.5, 0.5)
	self._thumb.Size = UDim2.new(0, kThumbSize, 0, kThumbSize)
	
	-- Add images on bar.
	self._preThumbImage = Instance.new("ImageLabel")
	self._preThumbImage.Name = "PreThumb"
	self._preThumbImage.Parent = slider.Bar
	self._preThumbImage.Size = UDim2.new(1, 0, 1, 0)
	self._preThumbImage.Position = UDim2.new(0, 0, 0, 0)
	self._preThumbImage.Image = kPreThumbImage
	self._preThumbImage.BorderSizePixel = 0

	self._postThumbImage = Instance.new("ImageLabel")
	self._postThumbImage.Name = "PostThumb"
	self._postThumbImage.Parent = slider.Bar
	self._postThumbImage.Size = UDim2.new(1, 0, 1, 0)
	self._postThumbImage.Position = UDim2.new(0, 0, 0, 0)
	self._postThumbImage.Image = kPostThumbImage
	self._postThumbImage.BorderSizePixel = 0

	sliderValue.Changed:connect(function()
		self._value = sliderValue.Value

		-- Min value is 1.
		-- Max value is sliderIntervals.
		-- So scale is...
		local scale = (self._value - 1)/(sliderIntervals-1)

		self._preThumbImage.Size = UDim2.new(scale, 0, 1, 0)
		self._postThumbImage.Size = UDim2.new(1 - scale, 0, 1, 0)
		self._postThumbImage.Position = UDim2.new(scale, 0, 0, 0)
		
		self._thumb.Position = UDim2.new(scale, 0, 
			0.5, 0)

		if self._valueChangedFunction then 
			self._valueChangedFunction(self._value)
		end
	end)
	
	self:SetValue(defaultValue)
	slider.AnchorPoint = Vector2.new(0, 0.5)
	slider.Size = UDim2.new(0, kSliderWidth, 1, 0)
	slider.Position = UDim2.new(0, GuiUtilities.StandardLineElementLeftMargin, 0, GuiUtilities.kStandardPropertyHeight/2)
	slider.Parent = frame
	
	return self
end

function LabeledSliderClass:SetValueChangedFunction(vcf)
	self._valueChangedFunction = vcf
end

function LabeledSliderClass:GetFrame()
	return self._frame
end

function LabeledSliderClass:SetValue(newValue)
	if self._sliderValue.Value ~= newValue then
		self._sliderValue.Value = newValue
	end
end

function LabeledSliderClass:GetValue()
	return self._sliderValue.Value
end


return LabeledSliderClass]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBXdc545f9337af4582a17e789ceadcde4f">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">LabeledRadioButton</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- LabeledRadioButton.lua
--
-- Creates a frame containing a label and a radio button.
--
----------------------------------------
GuiUtilities = require(script.Parent.GuiUtilities)
LabeledCheckbox = require(script.Parent.LabeledCheckbox)

local kButtonImage = "rbxasset://textures/TerrainTools/radio_button_frame.png"
local kBulletImage = "rbxasset://textures/TerrainTools/radio_button_bullet.png"

local kFrameSize = 12
local kBulletSize = 14

LabeledRadioButtonClass = {}
LabeledRadioButtonClass.__index = LabeledRadioButtonClass
setmetatable(LabeledRadioButtonClass, LabeledCheckbox)

function LabeledRadioButtonClass.new(nameSuffix, labelText)
	local newButton = LabeledCheckbox.new(nameSuffix, labelText, false)
	setmetatable(newButton, LabeledRadioButtonClass)

	newButton:UseSmallSize()
	newButton._checkImage.Position = UDim2.new(0.5, 0, 0.5, 0)
	newButton._checkImage.Image = kBulletImage
	newButton._checkImage.Size = UDim2.new(0, kBulletSize, 0, kBulletSize)

	newButton._button.Image = kButtonImage
	newButton._button.Size = UDim2.new(0, kFrameSize, 0, kFrameSize)
	newButton._button.BackgroundTransparency = 1

	return newButton
end

function LabeledRadioButtonClass:_MaybeToggleState()
	-- A checkbox can never be toggled off. 
	-- Only turns off because another one turns on.
	if (not self._disabled and not self._value) then 
		self:SetValue(not self._value)
	end
end

return LabeledRadioButtonClass]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBXe508c56a6d5f41a1bc196411163472fa">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">RbxGui</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[local t = {}

local function ScopedConnect(parentInstance, instance, event, signalFunc, syncFunc, removeFunc)
	local eventConnection = nil

	--Connection on parentInstance is scoped by parentInstance (when destroyed, it goes away)
	local tryConnect = function()
		if game:IsAncestorOf(parentInstance) then
			--Entering the world, make sure we are connected/synced
			if not eventConnection then
				eventConnection = instance[event]:connect(signalFunc)
				if syncFunc then syncFunc() end
			end
		else
			--Probably leaving the world, so disconnect for now
			if eventConnection then
				eventConnection:disconnect()
				if removeFunc then removeFunc() end
			end
		end
	end

	--Hook it up to ancestryChanged signal
	local connection = parentInstance.AncestryChanged:connect(tryConnect)
	
	--Now connect us if we're already in the world
	tryConnect()
	
	return connection
end

local function getLayerCollectorAncestor(instance)
	local localInstance = instance
	while localInstance and not localInstance:IsA("LayerCollector") do
		localInstance = localInstance.Parent
	end
	return localInstance
end

local function CreateButtons(frame, buttons, yPos, ySize)
	local buttonNum = 1
	local buttonObjs = {}
	for i, obj in ipairs(buttons) do 
		local button = Instance.new("TextButton")
		button.Name = "Button" .. buttonNum
		button.Font = Enum.Font.Arial
		button.FontSize = Enum.FontSize.Size18
		button.AutoButtonColor = true
		button.Modal = true
		if obj["Style"] then
			button.Style = obj.Style
		else
			button.Style = Enum.ButtonStyle.RobloxButton
		end
		if obj["ZIndex"] then
			button.ZIndex = obj.ZIndex
		end
		button.Text = obj.Text
		button.TextColor3 = Color3.new(1,1,1)
		button.MouseButton1Click:connect(obj.Function)
		button.Parent = frame
		buttonObjs[buttonNum] = button

		buttonNum = buttonNum + 1
	end
	local numButtons = buttonNum-1

	if numButtons == 1 then
		frame.Button1.Position = UDim2.new(0.35, 0, yPos.Scale, yPos.Offset)
		frame.Button1.Size = UDim2.new(.4,0,ySize.Scale, ySize.Offset)
	elseif numButtons == 2 then
		frame.Button1.Position = UDim2.new(0.1, 0, yPos.Scale, yPos.Offset)
		frame.Button1.Size = UDim2.new(.8/3,0, ySize.Scale, ySize.Offset)

		frame.Button2.Position = UDim2.new(0.55, 0, yPos.Scale, yPos.Offset)
		frame.Button2.Size = UDim2.new(.35,0, ySize.Scale, ySize.Offset)
	elseif numButtons >= 3 then
		local spacing = .1 / numButtons
		local buttonSize = .9 / numButtons

		buttonNum = 1
		while buttonNum <= numButtons do
			buttonObjs[buttonNum].Position = UDim2.new(spacing*buttonNum + (buttonNum-1) * buttonSize, 0, yPos.Scale, yPos.Offset)
			buttonObjs[buttonNum].Size = UDim2.new(buttonSize, 0, ySize.Scale, ySize.Offset)
			buttonNum = buttonNum + 1
		end
	end
end

local function setSliderPos(newAbsPosX,slider,sliderPosition,bar,steps)

	local newStep = steps - 1 --otherwise we really get one more step than we want
	local relativePosX = math.min(1, math.max(0, (newAbsPosX - bar.AbsolutePosition.X) / bar.AbsoluteSize.X ))
	local wholeNum, remainder = math.modf(relativePosX * newStep)
	if remainder > 0.5 then
		wholeNum = wholeNum + 1
	end
	relativePosX = wholeNum/newStep

	local result = math.ceil(relativePosX * newStep)
	if sliderPosition.Value ~= (result + 1) then --only update if we moved a step
		sliderPosition.Value = result + 1
		slider.Position = UDim2.new(relativePosX,-slider.AbsoluteSize.X/2,slider.Position.Y.Scale,slider.Position.Y.Offset)
	end
	
end

local function cancelSlide(areaSoak)
	areaSoak.Visible = false
end

t.CreateStyledMessageDialog = function(title, message, style, buttons)
	local frame = Instance.new("Frame")
	frame.Size = UDim2.new(0.5, 0, 0, 165)
	frame.Position = UDim2.new(0.25, 0, 0.5, -72.5)
	frame.Name = "MessageDialog"
	frame.Active = true
	frame.Style = Enum.FrameStyle.RobloxRound	
	
	local styleImage = Instance.new("ImageLabel")
	styleImage.Name = "StyleImage"
	styleImage.BackgroundTransparency = 1
	styleImage.Position = UDim2.new(0,5,0,15)
	if style == "error" or style == "Error" then
		styleImage.Size = UDim2.new(0, 71, 0, 71)
		styleImage.Image = "https://www.roblox.com/asset/?id=42565285"
	elseif style == "notify" or style == "Notify" then
		styleImage.Size = UDim2.new(0, 71, 0, 71)
		styleImage.Image = "https://www.roblox.com/asset/?id=42604978"
	elseif style == "confirm" or style == "Confirm" then
		styleImage.Size = UDim2.new(0, 74, 0, 76)
		styleImage.Image = "https://www.roblox.com/asset/?id=42557901"
	else
		return t.CreateMessageDialog(title,message,buttons)
	end
	styleImage.Parent = frame
	
	local titleLabel = Instance.new("TextLabel")
	titleLabel.Name = "Title"
	titleLabel.Text = title
	titleLabel.TextStrokeTransparency = 0
	titleLabel.BackgroundTransparency = 1
	titleLabel.TextColor3 = Color3.new(221/255,221/255,221/255)
	titleLabel.Position = UDim2.new(0, 80, 0, 0)
	titleLabel.Size = UDim2.new(1, -80, 0, 40)
	titleLabel.Font = Enum.Font.ArialBold
	titleLabel.FontSize = Enum.FontSize.Size36
	titleLabel.TextXAlignment = Enum.TextXAlignment.Center
	titleLabel.TextYAlignment = Enum.TextYAlignment.Center
	titleLabel.Parent = frame

	local messageLabel = Instance.new("TextLabel")
	messageLabel.Name = "Message"
	messageLabel.Text = message
	messageLabel.TextStrokeTransparency = 0
	messageLabel.TextColor3 = Color3.new(221/255,221/255,221/255)
	messageLabel.Position = UDim2.new(0.025, 80, 0, 45)
	messageLabel.Size = UDim2.new(0.95, -80, 0, 55)
	messageLabel.BackgroundTransparency = 1
	messageLabel.Font = Enum.Font.Arial
	messageLabel.FontSize = Enum.FontSize.Size18
	messageLabel.TextWrap = true
	messageLabel.TextXAlignment = Enum.TextXAlignment.Left
	messageLabel.TextYAlignment = Enum.TextYAlignment.Top
	messageLabel.Parent = frame

	CreateButtons(frame, buttons, UDim.new(0, 105), UDim.new(0, 40) )

	return frame
end

t.CreateMessageDialog = function(title, message, buttons)
	local frame = Instance.new("Frame")
	frame.Size = UDim2.new(0.5, 0, 0.5, 0)
	frame.Position = UDim2.new(0.25, 0, 0.25, 0)
	frame.Name = "MessageDialog"
	frame.Active = true
	frame.Style = Enum.FrameStyle.RobloxRound

	local titleLabel = Instance.new("TextLabel")
	titleLabel.Name = "Title"
	titleLabel.Text = title
	titleLabel.BackgroundTransparency = 1
	titleLabel.TextColor3 = Color3.new(221/255,221/255,221/255)
	titleLabel.Position = UDim2.new(0, 0, 0, 0)
	titleLabel.Size = UDim2.new(1, 0, 0.15, 0)
	titleLabel.Font = Enum.Font.ArialBold
	titleLabel.FontSize = Enum.FontSize.Size36
	titleLabel.TextXAlignment = Enum.TextXAlignment.Center
	titleLabel.TextYAlignment = Enum.TextYAlignment.Center
	titleLabel.Parent = frame

	local messageLabel = Instance.new("TextLabel")
	messageLabel.Name = "Message"
	messageLabel.Text = message
	messageLabel.TextColor3 = Color3.new(221/255,221/255,221/255)
	messageLabel.Position = UDim2.new(0.025, 0, 0.175, 0)
	messageLabel.Size = UDim2.new(0.95, 0, .55, 0)
	messageLabel.BackgroundTransparency = 1
	messageLabel.Font = Enum.Font.Arial
	messageLabel.FontSize = Enum.FontSize.Size18
	messageLabel.TextWrap = true
	messageLabel.TextXAlignment = Enum.TextXAlignment.Left
	messageLabel.TextYAlignment = Enum.TextYAlignment.Top
	messageLabel.Parent = frame

	CreateButtons(frame, buttons, UDim.new(0.8,0), UDim.new(0.15, 0))

	return frame
end

-- written by jmargh
-- to be used for the new settings menu
t.CreateScrollingDropDownMenu = function(onSelectedCallback, size, position, baseZ)
	local maxVisibleList = 6
	local baseZIndex = 0
	if type(baseZ) == 'number' then
		baseZIndex = baseZ
	end

	local dropDownMenu = {}
	local currentList = nil

	local updateFunc = nil
	local frame = Instance.new('Frame')
	frame.Name = "DropDownMenuFrame"
	frame.Size = size
	frame.Position = position
	frame.BackgroundTransparency = 1
	dropDownMenu.Frame = frame

	local currentSelectionName = Instance.new('TextButton')
	currentSelectionName.Name = "CurrentSelectionName"
	currentSelectionName.Size = UDim2.new(1, 0, 1, 0)
	currentSelectionName.BackgroundTransparency = 1
	currentSelectionName.Font = Enum.Font.SourceSansBold
	currentSelectionName.FontSize = Enum.FontSize.Size18
	currentSelectionName.TextXAlignment = Enum.TextXAlignment.Left
	currentSelectionName.TextYAlignment = Enum.TextYAlignment.Center
	currentSelectionName.TextColor3 = Color3.new(0.5, 0.5, 0.5)
	currentSelectionName.TextWrap = true
	currentSelectionName.ZIndex = baseZIndex
	currentSelectionName.Style = Enum.ButtonStyle.RobloxRoundDropdownButton
	currentSelectionName.Text = "Choose One"
	currentSelectionName.Parent = frame
	dropDownMenu.CurrentSelectionButton = currentSelectionName

	local icon = Instance.new('ImageLabel')
	icon.Name = "DropDownIcon"
	icon.Size = UDim2.new(0, 16, 0, 12)
	icon.Position = UDim2.new(1, -17, 0.5, -6)
	icon.Image = 'rbxasset://textures/ui/dropdown_arrow.png'
	icon.BackgroundTransparency = 1
	icon.ZIndex = baseZIndex
	icon.Parent = currentSelectionName

	local listMenu = nil
	local scrollingBackground = nil
	local visibleCount = 0
	local isOpen = false

	local function onEntrySelected()
		icon.Rotation = 0
		scrollingBackground:TweenSize(UDim2.new(1, 0, 0, currentSelectionName.AbsoluteSize.y), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 0.15, true)
		--
		listMenu.ScrollBarThickness = 0
		listMenu:TweenSize(UDim2.new(1, -16, 0, 24), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 0.15, true, function()
			if not isOpen then
				listMenu.Visible = false
				scrollingBackground.Visible = false
			end
		end)
		isOpen = false
	end

	currentSelectionName.MouseButton1Click:connect(function()
		if not currentSelectionName.Active or #currentList == 0 then return end
		if isOpen then
			onEntrySelected()
			return
		end
		--
		isOpen = true
		icon.Rotation = 180
		if listMenu then listMenu.Visible = true end
		if scrollingBackground then scrollingBackground.Visible = true end
		--
		if scrollingBackground then
			scrollingBackground:TweenSize(UDim2.new(1, 0, 0, visibleCount * 24 + 8), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 0.15, true)
		end
		if listMenu then
			listMenu:TweenSize(UDim2.new(1, -16, 0, visibleCount * 24), Enum.EasingDirection.InOut, Enum.EasingStyle.Sine, 0.15, true, function()
				listMenu.ScrollBarThickness = 6
			end)
		end
	end)

	--[[ Public API ]]--
	dropDownMenu.IsOpen = function()
		return isOpen
	end

	dropDownMenu.Close = function()
		onEntrySelected()
	end

	dropDownMenu.Reset = function()
		isOpen = false
		icon.Rotation = 0
		listMenu.ScrollBarThickness = 0
		listMenu.Size = UDim2.new(1, -16, 0, 24)
		listMenu.Visible = false
		scrollingBackground.Visible = false
	end

	dropDownMenu.SetVisible = function(isVisible)
		if frame then
			frame.Visible = isVisible
		end
	end

	dropDownMenu.UpdateZIndex = function(newZIndexBase)
		currentSelectionName.ZIndex = newZIndexBase
		icon.ZIndex = newZIndexBase
		if scrollingBackground then scrollingBackground.ZIndex = newZIndexBase + 1 end
		if listMenu then
			listMenu.ZIndex = newZIndexBase + 2
			for _,child in pairs(listMenu:GetChildren()) do
				child.ZIndex = newZIndexBase + 4
			end
		end
	end

	dropDownMenu.SetActive = function(isActive)
		currentSelectionName.Active = isActive
	end

	dropDownMenu.SetSelectionText = function(text)
		currentSelectionName.Text = text
	end

	dropDownMenu.CreateList = function(list)
		currentSelectionName.Text = "Choose One"
		if listMenu then listMenu:Destroy() end
		if scrollingBackground then scrollingBackground:Destroy() end
		--
		currentList = list
		local length = #list
		visibleCount = math.min(maxVisibleList, length)
		local listMenuOffset = visibleCount * 24

		listMenu = Instance.new('ScrollingFrame')
		listMenu.Name = "ListMenu"
		listMenu.Size = UDim2.new(1, -16, 0, 24)
		listMenu.Position = UDim2.new(0, 12, 0, 32)
		listMenu.CanvasSize = UDim2.new(0, 0, 0, length * 24)
		listMenu.BackgroundTransparency = 1
		listMenu.BorderSizePixel = 0
		listMenu.ZIndex = baseZIndex + 2
		listMenu.Visible = false
		listMenu.Active = true
		listMenu.BottomImage = 'rbxasset://textures/ui/scroll-bottom.png'
		listMenu.MidImage = 'rbxasset://textures/ui/scroll-middle.png'
		listMenu.TopImage = 'rbxasset://textures/ui/scroll-top.png'
		listMenu.ScrollBarThickness = 0
		listMenu.Parent = frame

		scrollingBackground = Instance.new('TextButton')
		scrollingBackground.Name = "ScrollingBackground"
		scrollingBackground.Size = UDim2.new(1, 0, 0, currentSelectionName.AbsoluteSize.y)
		scrollingBackground.Position = UDim2.new(0, 0, 0, 28)
		scrollingBackground.BackgroundColor3 = Color3.new(1, 1, 1)
		scrollingBackground.Style = Enum.ButtonStyle.RobloxRoundDropdownButton
		scrollingBackground.ZIndex = baseZIndex + 1
		scrollingBackground.Text = ""
		scrollingBackground.Visible = false
		scrollingBackground.AutoButtonColor = false
		scrollingBackground.Parent = frame

		for i = 1, length do
			local entry = list[i]
			local btn = Instance.new('TextButton')
			btn.Name = entry
			btn.Size = UDim2.new(1, 0, 0, 24)
			btn.Position = UDim2.new(0, 0, 0, (i - 1) * 24)
			btn.BackgroundTransparency = 0
			btn.BackgroundColor3 = Color3.new(1, 1, 1)
			btn.BorderSizePixel = 0
			btn.Font = Enum.Font.SourceSans
			btn.FontSize = Enum.FontSize.Size18
			btn.TextColor3 = Color3.new(0.5, 0.5, 0.5)
			btn.TextXAlignment = Enum.TextXAlignment.Left
			btn.TextYAlignment = Enum.TextYAlignment.Center
			btn.Text = entry
			btn.ZIndex = baseZIndex + 4
			btn.AutoButtonColor = false
			btn.Parent = listMenu

			btn.MouseButton1Click:connect(function()
				currentSelectionName.Text = btn.Text
				onEntrySelected()
				btn.Font = Enum.Font.SourceSans
				btn.TextColor3 = Color3.new(0.5, 0.5, 0.5)
				btn.BackgroundColor3 = Color3.new(1, 1, 1)
				onSelectedCallback(btn.Text)
			end)

			btn.MouseEnter:connect(function()
				btn.TextColor3 = Color3.new(1, 1, 1)
				btn.BackgroundColor3 = Color3.new(0.75, 0.75, 0.75)
			end)
			btn.MouseLeave:connect(function()
				btn.TextColor3 = Color3.new(0.5, 0.5, 0.5)
				btn.BackgroundColor3 = Color3.new(1, 1, 1)
			end)
		end
	end

	return dropDownMenu
end

t.CreateDropDownMenu = function(items, onSelect, forRoblox, whiteSkin, baseZ)
	local baseZIndex = 0
	if (type(baseZ) == "number") then
		baseZIndex = baseZ
	end
	local width = UDim.new(0, 100)
	local height = UDim.new(0, 32)

	local xPos = 0.055
	local frame = Instance.new("Frame")
	local textColor = Color3.new(1,1,1)
	if (whiteSkin) then
		textColor = Color3.new(0.5, 0.5, 0.5)
	end
	frame.Name = "DropDownMenu"
	frame.BackgroundTransparency = 1
	frame.Size = UDim2.new(width, height)

	local dropDownMenu = Instance.new("TextButton")
	dropDownMenu.Name = "DropDownMenuButton"
	dropDownMenu.TextWrap = true
	dropDownMenu.TextColor3 = textColor
	dropDownMenu.Text = "Choose One"
	dropDownMenu.Font = Enum.Font.ArialBold
	dropDownMenu.FontSize = Enum.FontSize.Size18
	dropDownMenu.TextXAlignment = Enum.TextXAlignment.Left
	dropDownMenu.TextYAlignment = Enum.TextYAlignment.Center
	dropDownMenu.BackgroundTransparency = 1
	dropDownMenu.AutoButtonColor = true
	if (whiteSkin) then
		dropDownMenu.Style = Enum.ButtonStyle.RobloxRoundDropdownButton
	else
		dropDownMenu.Style = Enum.ButtonStyle.RobloxButton
	end
	dropDownMenu.Size = UDim2.new(1,0,1,0)
	dropDownMenu.Parent = frame
	dropDownMenu.ZIndex = 2 + baseZIndex

	local dropDownIcon = Instance.new("ImageLabel")
	dropDownIcon.Name = "Icon"
	dropDownIcon.Active = false
	if (whiteSkin) then
		dropDownIcon.Image = "rbxasset://textures/ui/dropdown_arrow.png"
		dropDownIcon.Size = UDim2.new(0,16,0,12)
		dropDownIcon.Position = UDim2.new(1,-17,0.5, -6)
	else
		dropDownIcon.Image = "https://www.roblox.com/asset/?id=45732894"
		dropDownIcon.Size = UDim2.new(0,11,0,6)
		dropDownIcon.Position = UDim2.new(1,-11,0.5, -2)
	end
	dropDownIcon.BackgroundTransparency = 1
	dropDownIcon.Parent = dropDownMenu
	dropDownIcon.ZIndex = 2 + baseZIndex
	
	local itemCount = #items
	local dropDownItemCount = #items
	local useScrollButtons = false
	if dropDownItemCount > 6 then
		useScrollButtons = true
		dropDownItemCount = 6
	end
	
	local droppedDownMenu = Instance.new("TextButton")
	droppedDownMenu.Name = "List"
	droppedDownMenu.Text = ""
	droppedDownMenu.BackgroundTransparency = 1
	--droppedDownMenu.AutoButtonColor = true
	if (whiteSkin) then
		droppedDownMenu.Style = Enum.ButtonStyle.RobloxRoundDropdownButton
	else
		droppedDownMenu.Style = Enum.ButtonStyle.RobloxButton
	end
	droppedDownMenu.Visible = false
	droppedDownMenu.Active = true	--Blocks clicks
	droppedDownMenu.Position = UDim2.new(0,0,0,0)
	droppedDownMenu.Size = UDim2.new(1,0, (1 + dropDownItemCount)*.8, 0)
	droppedDownMenu.Parent = frame
	droppedDownMenu.ZIndex = 2 + baseZIndex

	local choiceButton = Instance.new("TextButton")
	choiceButton.Name = "ChoiceButton"
	choiceButton.BackgroundTransparency = 1
	choiceButton.BorderSizePixel = 0
	choiceButton.Text = "ReplaceMe"
	choiceButton.TextColor3 = textColor
	choiceButton.TextXAlignment = Enum.TextXAlignment.Left
	choiceButton.TextYAlignment = Enum.TextYAlignment.Center
	choiceButton.BackgroundColor3 = Color3.new(1, 1, 1)
	choiceButton.Font = Enum.Font.Arial
	choiceButton.FontSize = Enum.FontSize.Size18
	if useScrollButtons then
		choiceButton.Size = UDim2.new(1,-13, .8/((dropDownItemCount + 1)*.8),0) 
	else
		choiceButton.Size = UDim2.new(1, 0, .8/((dropDownItemCount + 1)*.8),0) 
	end
	choiceButton.TextWrap = true
	choiceButton.ZIndex = 2 + baseZIndex

	local areaSoak = Instance.new("TextButton")
	areaSoak.Name = "AreaSoak"
	areaSoak.Text = ""
	areaSoak.BackgroundTransparency = 1
	areaSoak.Active = true
	areaSoak.Size = UDim2.new(1,0,1,0)
	areaSoak.Visible = false
	areaSoak.ZIndex = 3 + baseZIndex

	local dropDownSelected = false

	local scrollUpButton 
	local scrollDownButton
	local scrollMouseCount = 0

	local setZIndex = function(baseZIndex)
		droppedDownMenu.ZIndex = baseZIndex +1
		if scrollUpButton then
			scrollUpButton.ZIndex = baseZIndex + 3
		end
		if scrollDownButton then
			scrollDownButton.ZIndex = baseZIndex + 3
		end
		
		local children = droppedDownMenu:GetChildren()
		if children then
			for i, child in ipairs(children) do
				if child.Name == "ChoiceButton" then
					child.ZIndex = baseZIndex + 2
				elseif child.Name == "ClickCaptureButton" then
					child.ZIndex = baseZIndex
				end
			end
		end
	end

	local scrollBarPosition = 1
	local updateScroll = function()
		if scrollUpButton then
			scrollUpButton.Active = scrollBarPosition > 1 
		end
		if scrollDownButton then
			scrollDownButton.Active = scrollBarPosition + dropDownItemCount <= itemCount 
		end

		local children = droppedDownMenu:GetChildren()
		if not children then return end

		local childNum = 1			
		for i, obj in ipairs(children) do
			if obj.Name == "ChoiceButton" then
				if childNum < scrollBarPosition or childNum >= scrollBarPosition + dropDownItemCount then
					obj.Visible = false
				else
					obj.Position = UDim2.new(0,0,((childNum-scrollBarPosition+1)*.8)/((dropDownItemCount+1)*.8),0)
					obj.Visible = true
				end
				obj.TextColor3 = textColor
				obj.BackgroundTransparency = 1

				childNum = childNum + 1
			end
		end
	end
	local toggleVisibility = function()
		dropDownSelected = not dropDownSelected

		areaSoak.Visible = not areaSoak.Visible
		dropDownMenu.Visible = not dropDownSelected
		droppedDownMenu.Visible = dropDownSelected
		if dropDownSelected then
			setZIndex(4 + baseZIndex)
		else
			setZIndex(2 + baseZIndex)
		end
		if useScrollButtons then
			updateScroll()
		end
	end
	droppedDownMenu.MouseButton1Click:connect(toggleVisibility)

	local updateSelection = function(text)
		local foundItem = false
		local children = droppedDownMenu:GetChildren()
		local childNum = 1
		if children then
			for i, obj in ipairs(children) do
				if obj.Name == "ChoiceButton" then
					if obj.Text == text then
						obj.Font = Enum.Font.ArialBold
						foundItem = true			
						scrollBarPosition = childNum						
						if (whiteSkin) then
							obj.TextColor3 = Color3.new(90/255,142/255,233/255)
						end
					else
						obj.Font = Enum.Font.Arial
						if (whiteSkin) then
							obj.TextColor3 = textColor
						end
					end
					childNum = childNum + 1
				end
			end
		end
		if not text then
			dropDownMenu.Text = "Choose One"
			scrollBarPosition = 1
		else
			if not foundItem then
				error("Invalid Selection Update -- " .. text)
			end

			if scrollBarPosition + dropDownItemCount > itemCount + 1 then
				scrollBarPosition = itemCount - dropDownItemCount + 1
			end

			dropDownMenu.Text = text
		end
	end
	
	local function scrollDown()
		if scrollBarPosition + dropDownItemCount <= itemCount then
			scrollBarPosition = scrollBarPosition + 1
			updateScroll()
			return true
		end
		return false
	end
	local function scrollUp()
		if scrollBarPosition > 1 then
			scrollBarPosition = scrollBarPosition - 1
			updateScroll()
			return true
		end
		return false
	end
	
	if useScrollButtons then
		--Make some scroll buttons
		scrollUpButton = Instance.new("ImageButton")
		scrollUpButton.Name = "ScrollUpButton"
		scrollUpButton.BackgroundTransparency = 1
		scrollUpButton.Image = "rbxasset://textures/ui/scrollbuttonUp.png"
		scrollUpButton.Size = UDim2.new(0,17,0,17) 
		scrollUpButton.Position = UDim2.new(1,-11,(1*.8)/((dropDownItemCount+1)*.8),0)
		scrollUpButton.MouseButton1Click:connect(
			function()
				scrollMouseCount = scrollMouseCount + 1
			end)
		scrollUpButton.MouseLeave:connect(
			function()
				scrollMouseCount = scrollMouseCount + 1
			end)
		scrollUpButton.MouseButton1Down:connect(
			function()
				scrollMouseCount = scrollMouseCount + 1
	
				scrollUp()
				local val = scrollMouseCount
				wait(0.5)
				while val == scrollMouseCount do
					if scrollUp() == false then
						break
					end
					wait(0.1)
				end				
			end)

		scrollUpButton.Parent = droppedDownMenu

		scrollDownButton = Instance.new("ImageButton")
		scrollDownButton.Name = "ScrollDownButton"
		scrollDownButton.BackgroundTransparency = 1
		scrollDownButton.Image = "rbxasset://textures/ui/scrollbuttonDown.png"
		scrollDownButton.Size = UDim2.new(0,17,0,17) 
		scrollDownButton.Position = UDim2.new(1,-11,1,-11)
		scrollDownButton.Parent = droppedDownMenu
		scrollDownButton.MouseButton1Click:connect(
			function()
				scrollMouseCount = scrollMouseCount + 1
			end)
		scrollDownButton.MouseLeave:connect(
			function()
				scrollMouseCount = scrollMouseCount + 1
			end)
		scrollDownButton.MouseButton1Down:connect(
			function()
				scrollMouseCount = scrollMouseCount + 1

				scrollDown()
				local val = scrollMouseCount
				wait(0.5)
				while val == scrollMouseCount do
					if scrollDown() == false then
						break
					end
					wait(0.1)
				end				
			end)	

		local scrollbar = Instance.new("ImageLabel")
		scrollbar.Name = "ScrollBar"
		scrollbar.Image = "rbxasset://textures/ui/scrollbar.png"
		scrollbar.BackgroundTransparency = 1
		scrollbar.Size = UDim2.new(0, 18, (dropDownItemCount*.8)/((dropDownItemCount+1)*.8), -(17) - 11 - 4)
		scrollbar.Position = UDim2.new(1,-11,(1*.8)/((dropDownItemCount+1)*.8),17+2)
		scrollbar.Parent = droppedDownMenu
	end

	for i,item in ipairs(items) do
		-- needed to maintain local scope for items in event listeners below
		local button = choiceButton:clone()
		if forRoblox then
			button.RobloxLocked = true
		end		
		button.Text = item
		button.Parent = droppedDownMenu
		if (whiteSkin) then
			button.TextColor3 = textColor
		end

		button.MouseButton1Click:connect(function()
			--Remove Highlight
			if (not whiteSkin) then
				button.TextColor3 = Color3.new(1,1,1)
			end
			button.BackgroundTransparency = 1

			updateSelection(item)
			onSelect(item)

			toggleVisibility()
		end)
		button.MouseEnter:connect(function()
			--Add Highlight	
			if (not whiteSkin) then
				button.TextColor3 = Color3.new(0,0,0)
			end
			button.BackgroundTransparency = 0
		end)

		button.MouseLeave:connect(function()
			--Remove Highlight
			if (not whiteSkin) then
				button.TextColor3 = Color3.new(1,1,1)
			end
			button.BackgroundTransparency = 1
		end)
	end

	--This does the initial layout of the buttons	
	updateScroll()
	
	frame.AncestryChanged:connect(function(child,parent)
		if parent == nil then
			areaSoak.Parent = nil
		else
			areaSoak.Parent = getLayerCollectorAncestor(frame)
		end
	end)

	dropDownMenu.MouseButton1Click:connect(toggleVisibility)
	areaSoak.MouseButton1Click:connect(toggleVisibility)
	return frame, updateSelection
end

t.CreatePropertyDropDownMenu = function(instance, property, enum)

	local items = enum:GetEnumItems()
	local names = {}
	local nameToItem = {}
	for i,obj in ipairs(items) do
		names[i] = obj.Name
		nameToItem[obj.Name] = obj
	end

	local frame
	local updateSelection
	frame, updateSelection = t.CreateDropDownMenu(names, function(text) instance[property] = nameToItem[text] end)

	ScopedConnect(frame, instance, "Changed", 
		function(prop)
			if prop == property then
				updateSelection(instance[property].Name)
			end
		end,
		function()
			updateSelection(instance[property].Name)
		end)

	return frame
end

t.GetFontHeight = function(font, fontSize)
	if font == nil or fontSize == nil then
		error("Font and FontSize must be non-nil")
	end
	
	local fontSizeInt = tonumber(fontSize.Name:match("%d+")) -- Clever hack to extract the size from the enum itself.

	if font == Enum.Font.Legacy then -- Legacy has a 50% bigger size.
		return math.ceil(fontSizeInt*1.5)
	else -- Size is literally just the fontSizeInt
		return fontSizeInt
	end
end

local function layoutGuiObjectsHelper(frame, guiObjects, settingsTable)
	local totalPixels = frame.AbsoluteSize.Y
	local pixelsRemaining = frame.AbsoluteSize.Y
	for i, child in ipairs(guiObjects) do
		if child:IsA("TextLabel") or child:IsA("TextButton") then
			local isLabel = child:IsA("TextLabel")
			if isLabel then
				pixelsRemaining = pixelsRemaining - settingsTable["TextLabelPositionPadY"]
			else
				pixelsRemaining = pixelsRemaining - settingsTable["TextButtonPositionPadY"]
			end
			child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining)
			child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, pixelsRemaining)

			if child.TextFits and child.TextBounds.Y < pixelsRemaining then
				child.Visible = true
				if isLabel then
					child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.TextBounds.Y + settingsTable["TextLabelSizePadY"])
				else 
					child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.TextBounds.Y + settingsTable["TextButtonSizePadY"])
				end

				while not child.TextFits do
					child.Size = UDim2.new(child.Size.X.Scale, child.Size.X.Offset, 0, child.AbsoluteSize.Y + 1)
				end
				pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y		

				if isLabel then
					pixelsRemaining = pixelsRemaining - settingsTable["TextLabelPositionPadY"]
				else
					pixelsRemaining = pixelsRemaining - settingsTable["TextButtonPositionPadY"]
				end
			else
				child.Visible = false
				pixelsRemaining = -1
			end			

		else
			--GuiObject
			child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining)
			pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y
			child.Visible = (pixelsRemaining >= 0)
		end
	end
end

t.LayoutGuiObjects = function(frame, guiObjects, settingsTable)
	if not frame:IsA("GuiObject") then
		error("Frame must be a GuiObject")
	end
	for i, child in ipairs(guiObjects) do
		if not child:IsA("GuiObject") then
			error("All elements that are layed out must be of type GuiObject")
		end
	end

	if not settingsTable then
		settingsTable = {}
	end

	if not settingsTable["TextLabelSizePadY"] then
		settingsTable["TextLabelSizePadY"] = 0
	end
	if not settingsTable["TextLabelPositionPadY"] then
		settingsTable["TextLabelPositionPadY"] = 0
	end
	if not settingsTable["TextButtonSizePadY"] then
		settingsTable["TextButtonSizePadY"] = 12
	end
	if not settingsTable["TextButtonPositionPadY"] then
		settingsTable["TextButtonPositionPadY"] = 2
	end

	--Wrapper frame takes care of styled objects
	local wrapperFrame = Instance.new("Frame")
	wrapperFrame.Name = "WrapperFrame"
	wrapperFrame.BackgroundTransparency = 1
	wrapperFrame.Size = UDim2.new(1,0,1,0)
	wrapperFrame.Parent = frame

	for i, child in ipairs(guiObjects) do
		child.Parent = wrapperFrame
	end

	local recalculate = function()
		wait()
		layoutGuiObjectsHelper(wrapperFrame, guiObjects, settingsTable)
	end
	
	frame.Changed:connect(
		function(prop)
			if prop == "AbsoluteSize" then
				--Wait a heartbeat for it to sync in
				recalculate(nil)
			end
		end)
	frame.AncestryChanged:connect(recalculate)

	layoutGuiObjectsHelper(wrapperFrame, guiObjects, settingsTable)
end


t.CreateSlider = function(steps,width,position)
	local sliderGui = Instance.new("Frame")
	sliderGui.Size = UDim2.new(1,0,1,0)
	sliderGui.BackgroundTransparency = 1
	sliderGui.Name = "SliderGui"
	
	local sliderSteps = Instance.new("IntValue")
	sliderSteps.Name = "SliderSteps"
	sliderSteps.Value = steps
	sliderSteps.Parent = sliderGui
	
	local areaSoak = Instance.new("TextButton")
	areaSoak.Name = "AreaSoak"
	areaSoak.Text = ""
	areaSoak.BackgroundTransparency = 1
	areaSoak.Active = false
	areaSoak.Size = UDim2.new(1,0,1,0)
	areaSoak.Visible = false
	areaSoak.ZIndex = 4
	
	sliderGui.AncestryChanged:connect(function(child,parent)
		if parent == nil then
			areaSoak.Parent = nil
		else
			areaSoak.Parent = getLayerCollectorAncestor(sliderGui)
		end
	end)
	
	local sliderPosition = Instance.new("IntValue")
	sliderPosition.Name = "SliderPosition"
	sliderPosition.Value = 0
	sliderPosition.Parent = sliderGui
	
	local id = math.random(1,100)
	
	local bar = Instance.new("TextButton")
	bar.Text = ""
	bar.AutoButtonColor = false
	bar.Name = "Bar"
	bar.BackgroundColor3 = Color3.new(0,0,0)
	if type(width) == "number" then
		bar.Size = UDim2.new(0,width,0,5)
	else
		bar.Size = UDim2.new(0,200,0,5)
	end
	bar.BorderColor3 = Color3.new(95/255,95/255,95/255)
	bar.ZIndex = 2
	bar.Parent = sliderGui
	
	if position["X"] and position["X"]["Scale"] and position["X"]["Offset"] and position["Y"] and position["Y"]["Scale"] and position["Y"]["Offset"] then
		bar.Position = position
	end
	
	local slider = Instance.new("ImageButton")
	slider.Name = "Slider"
	slider.BackgroundTransparency = 1
	slider.Image = "rbxasset://textures/ui/Slider.png"
	slider.Position = UDim2.new(0,0,0.5,-10)
	slider.Size = UDim2.new(0,20,0,20)
	slider.ZIndex = 3
	slider.Parent = bar
	
	local areaSoakMouseMoveCon = nil
	
	areaSoak.MouseLeave:connect(function()
		if areaSoak.Visible then
			cancelSlide(areaSoak)
		end
	end)
	areaSoak.MouseButton1Up:connect(function()
		if areaSoak.Visible then
			cancelSlide(areaSoak)
		end
	end)
	
	slider.MouseButton1Down:connect(function()
		areaSoak.Visible = true
		if areaSoakMouseMoveCon then areaSoakMouseMoveCon:disconnect() end
		areaSoakMouseMoveCon = areaSoak.MouseMoved:connect(function(x,y)
			setSliderPos(x,slider,sliderPosition,bar,steps)
		end)
	end)
	
	slider.MouseButton1Up:connect(function() cancelSlide(areaSoak) end)
	
	sliderPosition.Changed:connect(function(prop)
		sliderPosition.Value = math.min(steps, math.max(1,sliderPosition.Value))
		local relativePosX = (sliderPosition.Value - 1) / (steps - 1)
		slider.Position = UDim2.new(relativePosX,-slider.AbsoluteSize.X/2,slider.Position.Y.Scale,slider.Position.Y.Offset)
	end)
	
	bar.MouseButton1Down:connect(function(x,y)
		setSliderPos(x,slider,sliderPosition,bar,steps)
	end)
	
	return sliderGui, sliderPosition, sliderSteps

end



t.CreateSliderNew = function(steps,width,position)
	local sliderGui = Instance.new("Frame")
	sliderGui.Size = UDim2.new(1,0,1,0)
	sliderGui.BackgroundTransparency = 1
	sliderGui.Name = "SliderGui"
	
	local sliderSteps = Instance.new("IntValue")
	sliderSteps.Name = "SliderSteps"
	sliderSteps.Value = steps
	sliderSteps.Parent = sliderGui
	
	local areaSoak = Instance.new("TextButton")
	areaSoak.Name = "AreaSoak"
	areaSoak.Text = ""
	areaSoak.BackgroundTransparency = 1
	areaSoak.Active = false
	areaSoak.Size = UDim2.new(1,0,1,0)
	areaSoak.Visible = false
	areaSoak.ZIndex = 6
	
	sliderGui.AncestryChanged:connect(function(child,parent)
		if parent == nil then
			areaSoak.Parent = nil
		else
			areaSoak.Parent = getLayerCollectorAncestor(sliderGui)
		end
	end)
	
	local sliderPosition = Instance.new("IntValue")
	sliderPosition.Name = "SliderPosition"
	sliderPosition.Value = 0
	sliderPosition.Parent = sliderGui
	
	local id = math.random(1,100)
	
	local sliderBarImgHeight = 7
	local sliderBarCapImgWidth = 4

	local bar = Instance.new("ImageButton")
	bar.BackgroundTransparency = 1
	bar.Image = "rbxasset://textures/ui/Slider-BKG-Center.png"
	bar.Name = "Bar"
	local displayWidth = 200
	if type(width) == "number" then
		bar.Size = UDim2.new(0,width - (sliderBarCapImgWidth * 2),0,sliderBarImgHeight)
		displayWidth = width - (sliderBarCapImgWidth * 2)
	else
		bar.Size = UDim2.new(0,200,0,sliderBarImgHeight)
	end
	bar.ZIndex = 3
	bar.Parent = sliderGui	
	if position["X"] and position["X"]["Scale"] and position["X"]["Offset"] and position["Y"] and position["Y"]["Scale"] and position["Y"]["Offset"] then
		bar.Position = position
	end

	local barLeft = bar:clone()
	barLeft.Name = "BarLeft"
	barLeft.Image = "rbxasset://textures/ui/Slider-BKG-Left-Cap.png"
	barLeft.Size = UDim2.new(0, sliderBarCapImgWidth, 0, sliderBarImgHeight)
	barLeft.Position = UDim2.new(position.X.Scale, position.X.Offset - sliderBarCapImgWidth, position.Y.Scale, position.Y.Offset)
	barLeft.Parent = sliderGui	
	barLeft.ZIndex = 3

	local barRight = barLeft:clone()
	barRight.Name = "BarRight"
	barRight.Image = "rbxasset://textures/ui/Slider-BKG-Right-Cap.png"
	barRight.Position = UDim2.new(position.X.Scale, position.X.Offset + displayWidth, position.Y.Scale, position.Y.Offset)
	barRight.Parent = sliderGui	

	local fillLeft = barLeft:clone()
	fillLeft.Name = "FillLeft"
	fillLeft.Image = "rbxasset://textures/ui/Slider-Fill-Left-Cap.png"
	fillLeft.Parent = sliderGui	
	fillLeft.ZIndex = 4

	local fill = fillLeft:clone()
	fill.Name = "Fill"
	fill.Image = "rbxasset://textures/ui/Slider-Fill-Center.png"
	fill.Parent = bar	
	fill.ZIndex = 4
	fill.Position = UDim2.new(0, 0, 0, 0)
	fill.Size = UDim2.new(0.5, 0, 1, 0)


--	bar.Visible = false

	local slider = Instance.new("ImageButton")
	slider.Name = "Slider"
	slider.BackgroundTransparency = 1
	slider.Image = "rbxasset://textures/ui/slider_new_tab.png"
	slider.Position = UDim2.new(0,0,0.5,-14)
	slider.Size = UDim2.new(0,28,0,28)
	slider.ZIndex = 5
	slider.Parent = bar
	
	local areaSoakMouseMoveCon = nil
	
	areaSoak.MouseLeave:connect(function()
		if areaSoak.Visible then
			cancelSlide(areaSoak)
		end
	end)
	areaSoak.MouseButton1Up:connect(function()
		if areaSoak.Visible then
			cancelSlide(areaSoak)
		end
	end)
	
	slider.MouseButton1Down:connect(function()
		areaSoak.Visible = true
		if areaSoakMouseMoveCon then areaSoakMouseMoveCon:disconnect() end
		areaSoakMouseMoveCon = areaSoak.MouseMoved:connect(function(x,y)
			setSliderPos(x,slider,sliderPosition,bar,steps)
		end)
	end)
	
	slider.MouseButton1Up:connect(function() cancelSlide(areaSoak) end)
	
	sliderPosition.Changed:connect(function(prop)
		sliderPosition.Value = math.min(steps, math.max(1,sliderPosition.Value))
		local relativePosX = (sliderPosition.Value - 1) / (steps - 1)
		slider.Position = UDim2.new(relativePosX,-slider.AbsoluteSize.X/2,slider.Position.Y.Scale,slider.Position.Y.Offset)
		fill.Size = UDim2.new(relativePosX, 0, 1, 0)
	end)
	
	bar.MouseButton1Down:connect(function(x,y)
		setSliderPos(x,slider,sliderPosition,bar,steps)
	end)

	fill.MouseButton1Down:connect(function(x,y)
		setSliderPos(x,slider,sliderPosition,bar,steps)
	end)

	fillLeft.MouseButton1Down:connect(function(x,y)
		setSliderPos(x,slider,sliderPosition,bar,steps)
	end)


	return sliderGui, sliderPosition, sliderSteps

end





t.CreateTrueScrollingFrame = function()
	local lowY = nil
	local highY = nil
	
	local dragCon = nil
	local upCon = nil

	local internalChange = false

	local descendantsChangeConMap = {}

	local scrollingFrame = Instance.new("Frame")
	scrollingFrame.Name = "ScrollingFrame"
	scrollingFrame.Active = true
	scrollingFrame.Size = UDim2.new(1,0,1,0)
	scrollingFrame.ClipsDescendants = true

	local controlFrame = Instance.new("Frame")
	controlFrame.Name = "ControlFrame"
	controlFrame.BackgroundTransparency = 1
	controlFrame.Size = UDim2.new(0,18,1,0)
	controlFrame.Position = UDim2.new(1,-20,0,0)
	controlFrame.Parent = scrollingFrame
	
	local scrollBottom = Instance.new("BoolValue")
	scrollBottom.Value = false
	scrollBottom.Name = "ScrollBottom"
	scrollBottom.Parent = controlFrame
	
	local scrollUp = Instance.new("BoolValue")
	scrollUp.Value = false
	scrollUp.Name = "scrollUp"
	scrollUp.Parent = controlFrame

	local scrollUpButton = Instance.new("TextButton")
	scrollUpButton.Name = "ScrollUpButton"
	scrollUpButton.Text = ""
	scrollUpButton.AutoButtonColor = false
	scrollUpButton.BackgroundColor3 = Color3.new(0,0,0)
	scrollUpButton.BorderColor3 = Color3.new(1,1,1)
	scrollUpButton.BackgroundTransparency = 0.5
	scrollUpButton.Size = UDim2.new(0,18,0,18)
	scrollUpButton.ZIndex = 2
	scrollUpButton.Parent = controlFrame
	for i = 1, 6 do
		local triFrame = Instance.new("Frame")
		triFrame.BorderColor3 = Color3.new(1,1,1)
		triFrame.Name = "tri" .. tostring(i)
		triFrame.ZIndex = 3
		triFrame.BackgroundTransparency = 0.5
		triFrame.Size = UDim2.new(0,12 - ((i -1) * 2),0,0)
		triFrame.Position = UDim2.new(0,3 + (i -1),0.5,2 - (i -1))
		triFrame.Parent = scrollUpButton
	end
	scrollUpButton.MouseEnter:connect(function()
		scrollUpButton.BackgroundTransparency = 0.1
		local upChildren = scrollUpButton:GetChildren()
		for i = 1, #upChildren do
			upChildren[i].BackgroundTransparency = 0.1
		end
	end)
	scrollUpButton.MouseLeave:connect(function()
		scrollUpButton.BackgroundTransparency = 0.5
		local upChildren = scrollUpButton:GetChildren()
		for i = 1, #upChildren do
			upChildren[i].BackgroundTransparency = 0.5
		end
	end)

	local scrollDownButton = scrollUpButton:clone()
	scrollDownButton.Name = "ScrollDownButton"
	scrollDownButton.Position = UDim2.new(0,0,1,-18)
	local downChildren = scrollDownButton:GetChildren()
	for i = 1, #downChildren do
		downChildren[i].Position = UDim2.new(0,3 + (i -1),0.5,-2 + (i - 1))
	end
	scrollDownButton.MouseEnter:connect(function()
		scrollDownButton.BackgroundTransparency = 0.1
		local downChildren = scrollDownButton:GetChildren()
		for i = 1, #downChildren do
			downChildren[i].BackgroundTransparency = 0.1
		end
	end)
	scrollDownButton.MouseLeave:connect(function()
		scrollDownButton.BackgroundTransparency = 0.5
		local downChildren = scrollDownButton:GetChildren()
		for i = 1, #downChildren do
			downChildren[i].BackgroundTransparency = 0.5
		end
	end)
	scrollDownButton.Parent = controlFrame
	
	local scrollTrack = Instance.new("Frame")
	scrollTrack.Name = "ScrollTrack"
	scrollTrack.BackgroundTransparency = 1
	scrollTrack.Size = UDim2.new(0,18,1,-38)
	scrollTrack.Position = UDim2.new(0,0,0,19)
	scrollTrack.Parent = controlFrame

	local scrollbar = Instance.new("TextButton")
	scrollbar.BackgroundColor3 = Color3.new(0,0,0)
	scrollbar.BorderColor3 = Color3.new(1,1,1)
	scrollbar.BackgroundTransparency = 0.5
	scrollbar.AutoButtonColor = false
	scrollbar.Text = ""
	scrollbar.Active = true
	scrollbar.Name = "ScrollBar"
	scrollbar.ZIndex = 2
	scrollbar.BackgroundTransparency = 0.5
	scrollbar.Size = UDim2.new(0, 18, 0.1, 0)
	scrollbar.Position = UDim2.new(0,0,0,0)
	scrollbar.Parent = scrollTrack

	local scrollNub = Instance.new("Frame")
	scrollNub.Name = "ScrollNub"
	scrollNub.BorderColor3 = Color3.new(1,1,1)
	scrollNub.Size = UDim2.new(0,10,0,0)
	scrollNub.Position = UDim2.new(0.5,-5,0.5,0)
	scrollNub.ZIndex = 2
	scrollNub.BackgroundTransparency = 0.5
	scrollNub.Parent = scrollbar

	local newNub = scrollNub:clone()
	newNub.Position = UDim2.new(0.5,-5,0.5,-2)
	newNub.Parent = scrollbar
	
	local lastNub = scrollNub:clone()
	lastNub.Position = UDim2.new(0.5,-5,0.5,2)
	lastNub.Parent = scrollbar

	scrollbar.MouseEnter:connect(function()
		scrollbar.BackgroundTransparency = 0.1
		scrollNub.BackgroundTransparency = 0.1
		newNub.BackgroundTransparency = 0.1
		lastNub.BackgroundTransparency = 0.1
	end)
	scrollbar.MouseLeave:connect(function()
		scrollbar.BackgroundTransparency = 0.5
		scrollNub.BackgroundTransparency = 0.5
		newNub.BackgroundTransparency = 0.5
		lastNub.BackgroundTransparency = 0.5
	end)

	local mouseDrag = Instance.new("ImageButton")
	mouseDrag.Active = false
	mouseDrag.Size = UDim2.new(1.5, 0, 1.5, 0)
	mouseDrag.AutoButtonColor = false
	mouseDrag.BackgroundTransparency = 1
	mouseDrag.Name = "mouseDrag"
	mouseDrag.Position = UDim2.new(-0.25, 0, -0.25, 0)
	mouseDrag.ZIndex = 10
	
	local function positionScrollBar(x,y,offset)
		local oldPos = scrollbar.Position

		if y < scrollTrack.AbsolutePosition.y then
			scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,0,0)
			return (oldPos ~= scrollbar.Position)
		end
		
		local relativeSize = scrollbar.AbsoluteSize.Y/scrollTrack.AbsoluteSize.Y

		if y > (scrollTrack.AbsolutePosition.y + scrollTrack.AbsoluteSize.y) then
			scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,1 - relativeSize,0)
			return (oldPos ~= scrollbar.Position)
		end
		local newScaleYPos = (y - scrollTrack.AbsolutePosition.y - offset)/scrollTrack.AbsoluteSize.y
		if newScaleYPos + relativeSize > 1 then
			newScaleYPos = 1 - relativeSize
			scrollBottom.Value = true
			scrollUp.Value = false
		elseif newScaleYPos <= 0 then
			newScaleYPos = 0
			scrollUp.Value = true
			scrollBottom.Value = false
		else
			scrollUp.Value = false
			scrollBottom.Value = false
		end
		scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,newScaleYPos,0)
		
		return (oldPos ~= scrollbar.Position)
	end

	local function drillDownSetHighLow(instance)
		if not instance or not instance:IsA("GuiObject") then return end
		if instance == controlFrame then return end
		if instance:IsDescendantOf(controlFrame) then return end
		if not instance.Visible then return end

		if lowY and lowY > instance.AbsolutePosition.Y then
			lowY = instance.AbsolutePosition.Y
		elseif not lowY then
			lowY = instance.AbsolutePosition.Y
		end
		if highY and highY < (instance.AbsolutePosition.Y + instance.AbsoluteSize.Y) then
			highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y
		elseif not highY then
			highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y
		end
		local children = instance:GetChildren()
		for i = 1, #children do
			drillDownSetHighLow(children[i])
		end
	end

	local function resetHighLow()
		local firstChildren = scrollingFrame:GetChildren()

		for i = 1, #firstChildren do
			drillDownSetHighLow(firstChildren[i])
		end
	end

	local function recalculate()
		internalChange = true

		local percentFrame = 0
		if scrollbar.Position.Y.Scale > 0 then
			if scrollbar.Visible then
				percentFrame = scrollbar.Position.Y.Scale/((scrollTrack.AbsoluteSize.Y - scrollbar.AbsoluteSize.Y)/scrollTrack.AbsoluteSize.Y)
			else
				percentFrame = 0
			end
		end
		if percentFrame > 0.99 then percentFrame = 1 end

		local hiddenYAmount = (scrollingFrame.AbsoluteSize.Y - (highY - lowY)) * percentFrame
		
		local guiChildren = scrollingFrame:GetChildren()
		for i = 1, #guiChildren do
			if guiChildren[i] ~= controlFrame then
				guiChildren[i].Position = UDim2.new(guiChildren[i].Position.X.Scale,guiChildren[i].Position.X.Offset,
					0, math.ceil(guiChildren[i].AbsolutePosition.Y) - math.ceil(lowY) + hiddenYAmount)
			end
		end

		lowY = nil
		highY = nil
		resetHighLow()
		internalChange = false
	end

	local function setSliderSizeAndPosition()
		if not highY or not lowY then return end

		local totalYSpan = math.abs(highY - lowY)
		if totalYSpan == 0 then
			scrollbar.Visible = false
			scrollDownButton.Visible = false
			scrollUpButton.Visible = false

			if dragCon then dragCon:disconnect() dragCon = nil end
			if upCon then upCon:disconnect() upCon = nil end
			return
		end

		local percentShown = scrollingFrame.AbsoluteSize.Y/totalYSpan
		if percentShown >= 1 then
			scrollbar.Visible = false
			scrollDownButton.Visible = false
			scrollUpButton.Visible = false
			recalculate()
		else
			scrollbar.Visible = true
			scrollDownButton.Visible = true
			scrollUpButton.Visible = true

			scrollbar.Size = UDim2.new(scrollbar.Size.X.Scale,scrollbar.Size.X.Offset,percentShown,0)
		end

		local percentPosition = (scrollingFrame.AbsolutePosition.Y - lowY)/totalYSpan
		scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,percentPosition,-scrollbar.AbsoluteSize.X/2)

		if scrollbar.AbsolutePosition.y < scrollTrack.AbsolutePosition.y then
			scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,0,0)
		end

		if (scrollbar.AbsolutePosition.y + scrollbar.AbsoluteSize.Y) > (scrollTrack.AbsolutePosition.y + scrollTrack.AbsoluteSize.y) then
			local relativeSize = scrollbar.AbsoluteSize.Y/scrollTrack.AbsoluteSize.Y
			scrollbar.Position = UDim2.new(scrollbar.Position.X.Scale,scrollbar.Position.X.Offset,1 - relativeSize,0)
		end
	end
	
	local buttonScrollAmountPixels = 7
	local reentrancyGuardScrollUp = false
	local function doScrollUp()
		if reentrancyGuardScrollUp then return end
		
		reentrancyGuardScrollUp = true
			if positionScrollBar(0,scrollbar.AbsolutePosition.Y - buttonScrollAmountPixels,0) then
				recalculate()
			end
		reentrancyGuardScrollUp = false
	end
	
	local reentrancyGuardScrollDown = false
	local function doScrollDown()
		if reentrancyGuardScrollDown then return end
		
		reentrancyGuardScrollDown = true
			if positionScrollBar(0,scrollbar.AbsolutePosition.Y + buttonScrollAmountPixels,0) then
				recalculate()
			end
		reentrancyGuardScrollDown = false
	end

	local function scrollUp(mouseYPos)
		if scrollUpButton.Active then
			scrollStamp = tick()
			local current = scrollStamp
			local upCon
			upCon = mouseDrag.MouseButton1Up:connect(function()
				scrollStamp = tick()
				mouseDrag.Parent = nil
				upCon:disconnect()
			end)
			mouseDrag.Parent = getLayerCollectorAncestor(scrollbar)
			doScrollUp()
			wait(0.2)
			local t = tick()
			local w = 0.1
			while scrollStamp == current do
				doScrollUp()
				if mouseYPos and mouseYPos > scrollbar.AbsolutePosition.y then
					break
				end
				if not scrollUpButton.Active then break end
				if tick()-t > 5 then
					w = 0
				elseif tick()-t > 2 then
					w = 0.06
				end
				wait(w)
			end
		end
	end

	local function scrollDown(mouseYPos)
		if scrollDownButton.Active then
			scrollStamp = tick()
			local current = scrollStamp
			local downCon
			downCon = mouseDrag.MouseButton1Up:connect(function()
				scrollStamp = tick()
				mouseDrag.Parent = nil
				downCon:disconnect()
			end)
			mouseDrag.Parent = getLayerCollectorAncestor(scrollbar)
			doScrollDown()
			wait(0.2)
			local t = tick()
			local w = 0.1
			while scrollStamp == current do
				doScrollDown()
				if mouseYPos and mouseYPos < (scrollbar.AbsolutePosition.y + scrollbar.AbsoluteSize.x) then
					break
				end
				if not scrollDownButton.Active then break end
				if tick()-t > 5 then
					w = 0
				elseif tick()-t > 2 then
					w = 0.06
				end
				wait(w)
			end
		end
	end
	
	scrollbar.MouseButton1Down:connect(function(x,y)
		if scrollbar.Active then
			scrollStamp = tick()
			local mouseOffset = y - scrollbar.AbsolutePosition.y
			if dragCon then dragCon:disconnect() dragCon = nil end
			if upCon then upCon:disconnect() upCon = nil end
			local prevY = y
			local reentrancyGuardMouseScroll = false
			dragCon = mouseDrag.MouseMoved:connect(function(x,y)
				if reentrancyGuardMouseScroll then return end
				
				reentrancyGuardMouseScroll = true
					if positionScrollBar(x,y,mouseOffset) then
						recalculate()
					end
				reentrancyGuardMouseScroll = false
				
			end)
			upCon = mouseDrag.MouseButton1Up:connect(function()
				scrollStamp = tick()
				mouseDrag.Parent = nil
				dragCon:disconnect(); dragCon = nil
				upCon:disconnect(); drag = nil
			end)
			mouseDrag.Parent = getLayerCollectorAncestor(scrollbar)
		end
	end)

	local scrollMouseCount = 0

	scrollUpButton.MouseButton1Down:connect(function()
		scrollUp()
	end)
	scrollUpButton.MouseButton1Up:connect(function()
		scrollStamp = tick()
	end)

	scrollDownButton.MouseButton1Up:connect(function()
		scrollStamp = tick()
	end)
	scrollDownButton.MouseButton1Down:connect(function()
		 scrollDown()
	end)
		
	scrollbar.MouseButton1Up:connect(function()
		scrollStamp = tick()
	end)
	
	local function heightCheck(instance)
		if highY and (instance.AbsolutePosition.Y + instance.AbsoluteSize.Y) > highY then
			highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y
		elseif not highY then
			highY = instance.AbsolutePosition.Y + instance.AbsoluteSize.Y
		end
		setSliderSizeAndPosition()
	end
	
	local function highLowRecheck()
		local oldLowY = lowY
		local oldHighY = highY
		lowY = nil
		highY = nil
		resetHighLow()

		if (lowY ~= oldLowY) or (highY ~= oldHighY) then
			setSliderSizeAndPosition()
		end
	end

	local function descendantChanged(this, prop)
		if internalChange then return end
		if not this.Visible then return end

		if prop == "Size" or prop == "Position" then
			wait()
			highLowRecheck()
		end
	end

	scrollingFrame.DescendantAdded:connect(function(instance)
		if not instance:IsA("GuiObject") then return end

		if instance.Visible then
			wait() -- wait a heartbeat for sizes to reconfig
			highLowRecheck()
		end

		descendantsChangeConMap[instance] = instance.Changed:connect(function(prop) descendantChanged(instance, prop) end)
	end)

	scrollingFrame.DescendantRemoving:connect(function(instance)
		if not instance:IsA("GuiObject") then return end
		if descendantsChangeConMap[instance] then
			descendantsChangeConMap[instance]:disconnect()
			descendantsChangeConMap[instance] = nil
		end
		wait() -- wait a heartbeat for sizes to reconfig
		highLowRecheck()
	end)
	
	scrollingFrame.Changed:connect(function(prop)
		if prop == "AbsoluteSize" then
			if not highY or not lowY then return end

			highLowRecheck()
			setSliderSizeAndPosition()
		end
	end)

	return scrollingFrame, controlFrame
end

t.CreateScrollingFrame = function(orderList,scrollStyle)
	local frame = Instance.new("Frame")
	frame.Name = "ScrollingFrame"
	frame.BackgroundTransparency = 1
	frame.Size = UDim2.new(1,0,1,0)
	
	local scrollUpButton = Instance.new("ImageButton")
	scrollUpButton.Name = "ScrollUpButton"
	scrollUpButton.BackgroundTransparency = 1
	scrollUpButton.Image = "rbxasset://textures/ui/scrollbuttonUp.png"
	scrollUpButton.Size = UDim2.new(0,17,0,17) 

	
	local scrollDownButton = Instance.new("ImageButton")
	scrollDownButton.Name = "ScrollDownButton"
	scrollDownButton.BackgroundTransparency = 1
	scrollDownButton.Image = "rbxasset://textures/ui/scrollbuttonDown.png"
	scrollDownButton.Size = UDim2.new(0,17,0,17) 
	
	local scrollbar = Instance.new("ImageButton")
	scrollbar.Name = "ScrollBar"
	scrollbar.Image = "rbxasset://textures/ui/scrollbar.png"
	scrollbar.BackgroundTransparency = 1
	scrollbar.Size = UDim2.new(0, 18, 0, 150)

	local scrollStamp = 0
		
	local scrollDrag = Instance.new("ImageButton")
	scrollDrag.Image = "https://www.roblox.com/asset/?id=61367186"
	scrollDrag.Size = UDim2.new(1, 0, 0, 16)
	scrollDrag.BackgroundTransparency = 1
	scrollDrag.Name = "ScrollDrag"
	scrollDrag.Active = true
	scrollDrag.Parent = scrollbar
	
	local mouseDrag = Instance.new("ImageButton")
	mouseDrag.Active = false
	mouseDrag.Size = UDim2.new(1.5, 0, 1.5, 0)
	mouseDrag.AutoButtonColor = false
	mouseDrag.BackgroundTransparency = 1
	mouseDrag.Name = "mouseDrag"
	mouseDrag.Position = UDim2.new(-0.25, 0, -0.25, 0)
	mouseDrag.ZIndex = 10

	local style = "simple"
	if scrollStyle and tostring(scrollStyle) then
		style = scrollStyle
	end
	
	local scrollPosition = 1
	local rowSize = 0
	local howManyDisplayed = 0
		
	local layoutGridScrollBar = function()
		howManyDisplayed = 0
		local guiObjects = {}
		if orderList then
			for i, child in ipairs(orderList) do
				if child.Parent == frame then
					table.insert(guiObjects, child)
				end
			end
		else
			local children = frame:GetChildren()
			if children then
				for i, child in ipairs(children) do 
					if child:IsA("GuiObject") then
						table.insert(guiObjects, child)
					end
				end
			end
		end
		if #guiObjects == 0 then
			scrollUpButton.Active = false
			scrollDownButton.Active = false
			scrollDrag.Active = false
			scrollPosition = 1
			return
		end

		if scrollPosition > #guiObjects then
			scrollPosition = #guiObjects
		end
		
		if scrollPosition < 1 then scrollPosition = 1 end
		
		local totalPixelsY = frame.AbsoluteSize.Y
		local pixelsRemainingY = frame.AbsoluteSize.Y
		
		local totalPixelsX  = frame.AbsoluteSize.X
		
		local xCounter = 0
		local rowSizeCounter = 0
		local setRowSize = true

		local pixelsBelowScrollbar = 0
		local pos = #guiObjects
		
		local currentRowY = 0

		pos = scrollPosition
		--count up from current scroll position to fill out grid
		while pos <= #guiObjects and pixelsBelowScrollbar < totalPixelsY do
			xCounter = xCounter + guiObjects[pos].AbsoluteSize.X
			--previous pos was the end of a row
			if xCounter >= totalPixelsX then
				pixelsBelowScrollbar = pixelsBelowScrollbar + currentRowY
				currentRowY = 0
				xCounter = guiObjects[pos].AbsoluteSize.X
			end
			if guiObjects[pos].AbsoluteSize.Y > currentRowY then
				currentRowY = guiObjects[pos].AbsoluteSize.Y
			end
			pos = pos + 1
		end
		--Count wherever current row left off
		pixelsBelowScrollbar = pixelsBelowScrollbar + currentRowY
		currentRowY = 0
		
		pos = scrollPosition - 1
		xCounter = 0
		
		--objects with varying X,Y dimensions can rarely cause minor errors
		--rechecking every new scrollPosition is necessary to avoid 100% of errors
		
		--count backwards from current scrollPosition to see if we can add more rows
		while pixelsBelowScrollbar + currentRowY < totalPixelsY and pos >= 1 do
			xCounter = xCounter + guiObjects[pos].AbsoluteSize.X
			rowSizeCounter = rowSizeCounter + 1
			if xCounter >= totalPixelsX then
				rowSize = rowSizeCounter - 1
				rowSizeCounter = 0
				xCounter = guiObjects[pos].AbsoluteSize.X
				if pixelsBelowScrollbar + currentRowY <= totalPixelsY then
					--It fits, so back up our scroll position
					pixelsBelowScrollbar = pixelsBelowScrollbar + currentRowY
					if scrollPosition <= rowSize then
						scrollPosition = 1 
						break
					else
						scrollPosition = scrollPosition - rowSize
					end
					currentRowY = 0
				else
					break
				end
			end
			
			if guiObjects[pos].AbsoluteSize.Y > currentRowY then
				currentRowY = guiObjects[pos].AbsoluteSize.Y
			end

			pos = pos - 1
		end
		
		--Do check last time if pos = 0
		if (pos == 0) and (pixelsBelowScrollbar + currentRowY <= totalPixelsY) then
			scrollPosition = 1
		end

		xCounter = 0
		--pos = scrollPosition
		rowSizeCounter = 0
		setRowSize = true
		local lastChildSize = 0
		
		local xOffset,yOffset = 0
		if guiObjects[1] then
			yOffset = math.ceil(math.floor(math.fmod(totalPixelsY,guiObjects[1].AbsoluteSize.X))/2)
			xOffset = math.ceil(math.floor(math.fmod(totalPixelsX,guiObjects[1].AbsoluteSize.Y))/2)
		end
		
		for i, child in ipairs(guiObjects) do
			if i < scrollPosition then
				child.Visible = false
			else
				if pixelsRemainingY < 0 then
					child.Visible = false
				else
					--GuiObject
					if setRowSize then rowSizeCounter = rowSizeCounter + 1 end
					if xCounter + child.AbsoluteSize.X >= totalPixelsX then
						if setRowSize then
							rowSize = rowSizeCounter - 1
							setRowSize = false
						end
						xCounter = 0
						pixelsRemainingY = pixelsRemainingY - child.AbsoluteSize.Y
					end
					child.Position = UDim2.new(child.Position.X.Scale,xCounter + xOffset, 0, totalPixelsY - pixelsRemainingY + yOffset)
					xCounter = xCounter + child.AbsoluteSize.X
					child.Visible = ((pixelsRemainingY - child.AbsoluteSize.Y) >= 0)
					if child.Visible then
						howManyDisplayed = howManyDisplayed + 1
					end
					lastChildSize = child.AbsoluteSize				
				end
			end
		end

		scrollUpButton.Active = (scrollPosition > 1)
		if lastChildSize == 0 then 
			scrollDownButton.Active = false
		else
			scrollDownButton.Active = ((pixelsRemainingY - lastChildSize.Y) < 0)
		end
		scrollDrag.Active = #guiObjects > howManyDisplayed
		scrollDrag.Visible = scrollDrag.Active
	end



	local layoutSimpleScrollBar = function()
		local guiObjects = {}	
		howManyDisplayed = 0
		
		if orderList then
			for i, child in ipairs(orderList) do
				if child.Parent == frame then
					table.insert(guiObjects, child)
				end
			end
		else
			local children = frame:GetChildren()
			if children then
				for i, child in ipairs(children) do 
					if child:IsA("GuiObject") then
						table.insert(guiObjects, child)
					end
				end
			end
		end
		if #guiObjects == 0 then
			scrollUpButton.Active = false
			scrollDownButton.Active = false
			scrollDrag.Active = false
			scrollPosition = 1
			return
		end

		if scrollPosition > #guiObjects then
			scrollPosition = #guiObjects
		end
		
		local totalPixels = frame.AbsoluteSize.Y
		local pixelsRemaining = frame.AbsoluteSize.Y

		local pixelsBelowScrollbar = 0
		local pos = #guiObjects
		while pixelsBelowScrollbar < totalPixels and pos >= 1 do
			if pos >= scrollPosition then
				pixelsBelowScrollbar = pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y
			else
				if pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y <= totalPixels then
					--It fits, so back up our scroll position
					pixelsBelowScrollbar = pixelsBelowScrollbar + guiObjects[pos].AbsoluteSize.Y
					if scrollPosition <= 1 then
						scrollPosition = 1
						break
					else
						--local ("Backing up ScrollPosition from -- " ..scrollPosition)
						scrollPosition = scrollPosition - 1
					end
				else
					break
				end
			end
			pos = pos - 1
		end

		pos = scrollPosition
		for i, child in ipairs(guiObjects) do
			if i < scrollPosition then
				child.Visible = false
			else
				if pixelsRemaining < 0 then
					child.Visible = false
				else
					--GuiObject
					child.Position = UDim2.new(child.Position.X.Scale, child.Position.X.Offset, 0, totalPixels - pixelsRemaining)
					pixelsRemaining = pixelsRemaining - child.AbsoluteSize.Y
					if  (pixelsRemaining >= 0) then
						child.Visible = true
						howManyDisplayed = howManyDisplayed + 1
					else
						child.Visible = false
					end		
				end
			end
		end
		scrollUpButton.Active = (scrollPosition > 1)
		scrollDownButton.Active = (pixelsRemaining < 0)
		scrollDrag.Active = #guiObjects > howManyDisplayed
		scrollDrag.Visible = scrollDrag.Active
	end
	
		
	local moveDragger = function()	
		local guiObjects = 0
		local children = frame:GetChildren()
		if children then
			for i, child in ipairs(children) do 
				if child:IsA("GuiObject") then
					guiObjects = guiObjects + 1
				end
			end
		end
		
		if not scrollDrag.Parent then return end
		
		local dragSizeY = scrollDrag.Parent.AbsoluteSize.y * (1/(guiObjects - howManyDisplayed + 1))
		if dragSizeY < 16 then dragSizeY = 16 end
		scrollDrag.Size = UDim2.new(scrollDrag.Size.X.Scale,scrollDrag.Size.X.Offset,scrollDrag.Size.Y.Scale,dragSizeY)

		local relativeYPos = (scrollPosition - 1)/(guiObjects - (howManyDisplayed))
		if relativeYPos > 1 then relativeYPos = 1
		elseif relativeYPos < 0 then relativeYPos = 0 end
		local absYPos = 0
		
		if relativeYPos ~= 0 then
			absYPos = (relativeYPos * scrollbar.AbsoluteSize.y) - (relativeYPos * scrollDrag.AbsoluteSize.y)
		end
		
		scrollDrag.Position = UDim2.new(scrollDrag.Position.X.Scale,scrollDrag.Position.X.Offset,scrollDrag.Position.Y.Scale,absYPos)
	end

	local reentrancyGuard = false
	local recalculate = function()
		if reentrancyGuard then
			return
		end
		reentrancyGuard = true
		wait()
		local success, err = nil
		if style == "grid" then
			success, err = pcall(function() layoutGridScrollBar() end)
		elseif style == "simple" then
			success, err = pcall(function() layoutSimpleScrollBar() end)
		end
		if not success then print(err) end
		moveDragger()
		reentrancyGuard = false
	end
	
	local doScrollUp = function()
		scrollPosition = (scrollPosition) - rowSize
		if scrollPosition < 1 then scrollPosition = 1 end
		recalculate(nil)
	end
	
	local doScrollDown = function()
		scrollPosition = (scrollPosition) + rowSize
		recalculate(nil)
	end

	local scrollUp = function(mouseYPos)
		if scrollUpButton.Active then
			scrollStamp = tick()
			local current = scrollStamp
			local upCon
			upCon = mouseDrag.MouseButton1Up:connect(function()
				scrollStamp = tick()
				mouseDrag.Parent = nil
				upCon:disconnect()
			end)
			mouseDrag.Parent = getLayerCollectorAncestor(scrollbar)
			doScrollUp()
			wait(0.2)
			local t = tick()
			local w = 0.1
			while scrollStamp == current do
				doScrollUp()
				if mouseYPos and mouseYPos > scrollDrag.AbsolutePosition.y then
					break
				end
				if not scrollUpButton.Active then break end
				if tick()-t > 5 then
					w = 0
				elseif tick()-t > 2 then
					w = 0.06
				end
				wait(w)
			end
		end
	end

	local scrollDown = function(mouseYPos)
		if scrollDownButton.Active then
			scrollStamp = tick()
			local current = scrollStamp
			local downCon
			downCon = mouseDrag.MouseButton1Up:connect(function()
				scrollStamp = tick()
				mouseDrag.Parent = nil
				downCon:disconnect()
			end)
			mouseDrag.Parent = getLayerCollectorAncestor(scrollbar)
			doScrollDown()
			wait(0.2)
			local t = tick()
			local w = 0.1
			while scrollStamp == current do
				doScrollDown()
				if mouseYPos and mouseYPos < (scrollDrag.AbsolutePosition.y + scrollDrag.AbsoluteSize.x) then
					break
				end
				if not scrollDownButton.Active then break end
				if tick()-t > 5 then
					w = 0
				elseif tick()-t > 2 then
					w = 0.06
				end
				wait(w)
			end
		end
	end
	
	local y = 0
	scrollDrag.MouseButton1Down:connect(function(x,y)
		if scrollDrag.Active then
			scrollStamp = tick()
			local mouseOffset = y - scrollDrag.AbsolutePosition.y
			local dragCon
			local upCon
			dragCon = mouseDrag.MouseMoved:connect(function(x,y)
				local barAbsPos = scrollbar.AbsolutePosition.y
				local barAbsSize = scrollbar.AbsoluteSize.y
				
				local dragAbsSize = scrollDrag.AbsoluteSize.y
				local barAbsOne = barAbsPos + barAbsSize - dragAbsSize
				y = y - mouseOffset
				y = y < barAbsPos and barAbsPos or y > barAbsOne and barAbsOne or y
				y = y - barAbsPos
				
				local guiObjects = 0
				local children = frame:GetChildren()
				if children then
					for i, child in ipairs(children) do 
						if child:IsA("GuiObject") then
							guiObjects = guiObjects + 1
						end
					end
				end
				
				local doublePercent = y/(barAbsSize-dragAbsSize)
				local rowDiff = rowSize
				local totalScrollCount = guiObjects - (howManyDisplayed - 1)
				local newScrollPosition = math.floor((doublePercent * totalScrollCount) + 0.5) + rowDiff
				if newScrollPosition < scrollPosition then
					rowDiff = -rowDiff
				end
				
				if newScrollPosition < 1 then
					newScrollPosition = 1
				end
				
				scrollPosition = newScrollPosition
				recalculate(nil)
			end)
			upCon = mouseDrag.MouseButton1Up:connect(function()
				scrollStamp = tick()
				mouseDrag.Parent = nil
				dragCon:disconnect(); dragCon = nil
				upCon:disconnect(); drag = nil
			end)
			mouseDrag.Parent = getLayerCollectorAncestor(scrollbar)
		end
	end)

	local scrollMouseCount = 0

	scrollUpButton.MouseButton1Down:connect(
		function()
			scrollUp()
		end)
	scrollUpButton.MouseButton1Up:connect(function()
		scrollStamp = tick()
	end)


	scrollDownButton.MouseButton1Up:connect(function()
		scrollStamp = tick()
	end)
	scrollDownButton.MouseButton1Down:connect(
		function()
			scrollDown()	
		end)
		
	scrollbar.MouseButton1Up:connect(function()
		scrollStamp = tick()
	end)
	scrollbar.MouseButton1Down:connect(
		function(x,y)
			if y > (scrollDrag.AbsoluteSize.y + scrollDrag.AbsolutePosition.y) then
				scrollDown(y)
			elseif y < (scrollDrag.AbsolutePosition.y) then
				scrollUp(y)
			end
		end)


	frame.ChildAdded:connect(function()
		recalculate(nil)
	end)

	frame.ChildRemoved:connect(function()
		recalculate(nil)
	end)
	
	frame.Changed:connect(
		function(prop)
			if prop == "AbsoluteSize" then
				--Wait a heartbeat for it to sync in
				recalculate(nil)
			end
		end)
	frame.AncestryChanged:connect(function() recalculate(nil) end)

	return frame, scrollUpButton, scrollDownButton, recalculate, scrollbar
end
local function binaryGrow(min, max, fits)
	if min > max then
		return min
	end
	local biggestLegal = min

	while min <= max do
		local mid = min + math.floor((max - min) / 2)
		if fits(mid) and (biggestLegal == nil or biggestLegal < mid) then
			biggestLegal = mid
			
			--Try growing
			min = mid + 1
		else
			--Doesn't fit, shrink
			max = mid - 1
		end
	end
	return biggestLegal
end


local function binaryShrink(min, max, fits)
	if min > max then
		return min
	end
	local smallestLegal = max

	while min <= max do
		local mid = min + math.floor((max - min) / 2)
		if fits(mid) and (smallestLegal == nil or smallestLegal > mid) then
			smallestLegal = mid
			
			--It fits, shrink
			max = mid - 1			
		else
			--Doesn't fit, grow
			min = mid + 1
		end
	end
	return smallestLegal
end


local function getGuiOwner(instance)
	while instance ~= nil do
		if instance:IsA("ScreenGui") or instance:IsA("BillboardGui")  then
			return instance
		end
		instance = instance.Parent
	end
	return nil
end

t.AutoTruncateTextObject = function(textLabel)
	local text = textLabel.Text

	local fullLabel = textLabel:Clone()
	fullLabel.Name = "Full" .. textLabel.Name 
	fullLabel.BorderSizePixel = 0
	fullLabel.BackgroundTransparency = 0
	fullLabel.Text = text
	fullLabel.TextXAlignment = Enum.TextXAlignment.Center
	fullLabel.Position = UDim2.new(0,-3,0,0)
	fullLabel.Size = UDim2.new(0,100,1,0)
	fullLabel.Visible = false
	fullLabel.Parent = textLabel

	local shortText = nil
	local mouseEnterConnection = nil
	local mouseLeaveConnection= nil

	local checkForResize = function()
		if getGuiOwner(textLabel) == nil then
			return
		end
		textLabel.Text = text
		if textLabel.TextFits then 
			--Tear down the rollover if it is active
			if mouseEnterConnection then
				mouseEnterConnection:disconnect()
				mouseEnterConnection = nil
			end
			if mouseLeaveConnection then
				mouseLeaveConnection:disconnect()
				mouseLeaveConnection = nil
			end
		else
			local len = string.len(text)
			textLabel.Text = text .. "~"

			--Shrink the text
			local textSize = binaryGrow(0, len, 
				function(pos)
					if pos == 0 then
						textLabel.Text = "~"
					else
						textLabel.Text = string.sub(text, 1, pos) .. "~"
					end
					return textLabel.TextFits
				end)
			shortText = string.sub(text, 1, textSize) .. "~"
			textLabel.Text = shortText
			
			--Make sure the fullLabel fits
			if not fullLabel.TextFits then
				--Already too small, grow it really bit to start
				fullLabel.Size = UDim2.new(0, 10000, 1, 0)
			end
			
			--Okay, now try to binary shrink it back down
			local fullLabelSize = binaryShrink(textLabel.AbsoluteSize.X,fullLabel.AbsoluteSize.X, 
				function(size)
					fullLabel.Size = UDim2.new(0, size, 1, 0)
					return fullLabel.TextFits
				end)
			fullLabel.Size = UDim2.new(0,fullLabelSize+6,1,0)

			--Now setup the rollover effects, if they are currently off
			if mouseEnterConnection == nil then
				mouseEnterConnection = textLabel.MouseEnter:connect(
					function()
						fullLabel.ZIndex = textLabel.ZIndex + 1
						fullLabel.Visible = true
						--textLabel.Text = ""
					end)
			end
			if mouseLeaveConnection == nil then
				mouseLeaveConnection = textLabel.MouseLeave:connect(
					function()
						fullLabel.Visible = false
						--textLabel.Text = shortText
					end)
			end
		end
	end
	textLabel.AncestryChanged:connect(checkForResize)
	textLabel.Changed:connect(
		function(prop) 
			if prop == "AbsoluteSize" then 
				checkForResize() 	
			end 
		end)

	checkForResize()

	local function changeText(newText)
		text = newText
		fullLabel.Text = text
		checkForResize()
	end

	return textLabel, changeText
end

local function TransitionTutorialPages(fromPage, toPage, transitionFrame, currentPageValue)	
	if fromPage then
		fromPage.Visible = false
		if transitionFrame.Visible == false then
			transitionFrame.Size = fromPage.Size
			transitionFrame.Position = fromPage.Position
		end
	else
		if transitionFrame.Visible == false then
			transitionFrame.Size = UDim2.new(0.0,50,0.0,50)
			transitionFrame.Position = UDim2.new(0.5,-25,0.5,-25)
		end
	end
	transitionFrame.Visible = true
	currentPageValue.Value = nil

	local newSize, newPosition
	if toPage then
		--Make it visible so it resizes
		toPage.Visible = true

		newSize = toPage.Size
		newPosition = toPage.Position

		toPage.Visible = false
	else
		newSize = UDim2.new(0.0,50,0.0,50)
		newPosition = UDim2.new(0.5,-25,0.5,-25)
	end
	transitionFrame:TweenSizeAndPosition(newSize, newPosition, Enum.EasingDirection.InOut, Enum.EasingStyle.Quad, 0.3, true,
		function(state)
			if state == Enum.TweenStatus.Completed then
				transitionFrame.Visible = false
				if toPage then
					toPage.Visible = true
					currentPageValue.Value = toPage
				end
			end
		end)
end

t.CreateTutorial = function(name, tutorialKey, createButtons)
	local frame = Instance.new("Frame")
	frame.Name = "Tutorial-" .. name
	frame.BackgroundTransparency = 1
	frame.Size = UDim2.new(0.6, 0, 0.6, 0)
	frame.Position = UDim2.new(0.2, 0, 0.2, 0)

	local transitionFrame = Instance.new("Frame")
	transitionFrame.Name = "TransitionFrame"
	transitionFrame.Style = Enum.FrameStyle.RobloxRound
	transitionFrame.Size = UDim2.new(0.6, 0, 0.6, 0)
	transitionFrame.Position = UDim2.new(0.2, 0, 0.2, 0)
	transitionFrame.Visible = false
	transitionFrame.Parent = frame

	local currentPageValue = Instance.new("ObjectValue")
	currentPageValue.Name = "CurrentTutorialPage"
	currentPageValue.Value = nil
	currentPageValue.Parent = frame

	local boolValue = Instance.new("BoolValue")
	boolValue.Name = "Buttons"
	boolValue.Value = createButtons
	boolValue.Parent = frame

	local pages = Instance.new("Frame")
	pages.Name = "Pages"
	pages.BackgroundTransparency = 1
	pages.Size = UDim2.new(1,0,1,0)
	pages.Parent = frame

	local function getVisiblePageAndHideOthers()
		local visiblePage = nil
		local children = pages:GetChildren()
		if children then
			for i,child in ipairs(children) do
				if child.Visible then
					if visiblePage then
						child.Visible = false
					else
						visiblePage = child
					end
				end
			end
		end
		return visiblePage
	end

	local showTutorial = function(alwaysShow)
		if alwaysShow or UserSettings().GameSettings:GetTutorialState(tutorialKey) == false then
			print("Showing tutorial-",tutorialKey)
			local currentTutorialPage = getVisiblePageAndHideOthers()

			local firstPage = pages:FindFirstChild("TutorialPage1")
			if firstPage then
				TransitionTutorialPages(currentTutorialPage, firstPage, transitionFrame, currentPageValue)	
			else
				error("Could not find TutorialPage1")
			end
		end
	end

	local dismissTutorial = function()
		local currentTutorialPage = getVisiblePageAndHideOthers()

		if currentTutorialPage then
			TransitionTutorialPages(currentTutorialPage, nil, transitionFrame, currentPageValue)
		end

		UserSettings().GameSettings:SetTutorialState(tutorialKey, true)
	end

	local gotoPage = function(pageNum)
		local page = pages:FindFirstChild("TutorialPage" .. pageNum)
		local currentTutorialPage = getVisiblePageAndHideOthers()
		TransitionTutorialPages(currentTutorialPage, page, transitionFrame, currentPageValue)
	end

	return frame, showTutorial, dismissTutorial, gotoPage
end 

local function CreateBasicTutorialPage(name, handleResize, skipTutorial, giveDoneButton)
	local frame = Instance.new("Frame")
	frame.Name = "TutorialPage"
	frame.Style = Enum.FrameStyle.RobloxRound
	frame.Size = UDim2.new(0.6, 0, 0.6, 0)
	frame.Position = UDim2.new(0.2, 0, 0.2, 0)
	frame.Visible = false
	
	local frameHeader = Instance.new("TextLabel")
	frameHeader.Name = "Header"
	frameHeader.Text = name
	frameHeader.BackgroundTransparency = 1
	frameHeader.FontSize = Enum.FontSize.Size24
	frameHeader.Font = Enum.Font.ArialBold
	frameHeader.TextColor3 = Color3.new(1,1,1)
	frameHeader.TextXAlignment = Enum.TextXAlignment.Center
	frameHeader.TextWrap = true
	frameHeader.Size = UDim2.new(1,-55, 0, 22)
	frameHeader.Position = UDim2.new(0,0,0,0)
	frameHeader.Parent = frame

	local skipButton = Instance.new("ImageButton")
	skipButton.Name = "SkipButton"
	skipButton.AutoButtonColor = false
	skipButton.BackgroundTransparency = 1
	skipButton.Image = "rbxasset://textures/ui/closeButton.png"
	skipButton.MouseButton1Click:connect(function()
		skipTutorial()
	end)
	skipButton.MouseEnter:connect(function()
		skipButton.Image = "rbxasset://textures/ui/closeButton_dn.png"
	end)
	skipButton.MouseLeave:connect(function()
		skipButton.Image = "rbxasset://textures/ui/closeButton.png"
	end)
	skipButton.Size = UDim2.new(0, 25, 0, 25)
	skipButton.Position = UDim2.new(1, -25, 0, 0)
	skipButton.Parent = frame
	
	
	if giveDoneButton then
		local doneButton = Instance.new("TextButton")
		doneButton.Name = "DoneButton"
		doneButton.Style = Enum.ButtonStyle.RobloxButtonDefault
		doneButton.Text = "Done"
		doneButton.TextColor3 = Color3.new(1,1,1)
		doneButton.Font = Enum.Font.ArialBold
		doneButton.FontSize = Enum.FontSize.Size18
		doneButton.Size = UDim2.new(0,100,0,50)
		doneButton.Position = UDim2.new(0.5,-50,1,-50)
		
		if skipTutorial then
			doneButton.MouseButton1Click:connect(function() skipTutorial() end)
		end
		
		doneButton.Parent = frame
	end

	local innerFrame = Instance.new("Frame")
	innerFrame.Name = "ContentFrame"
	innerFrame.BackgroundTransparency = 1
	innerFrame.Position = UDim2.new(0,0,0,25)
	innerFrame.Parent = frame

	local nextButton = Instance.new("TextButton")
	nextButton.Name = "NextButton"
	nextButton.Text = "Next"
	nextButton.TextColor3 = Color3.new(1,1,1)
	nextButton.Font = Enum.Font.Arial
	nextButton.FontSize = Enum.FontSize.Size18
	nextButton.Style = Enum.ButtonStyle.RobloxButtonDefault
	nextButton.Size = UDim2.new(0,80, 0, 32)
	nextButton.Position = UDim2.new(0.5, 5, 1, -32)
	nextButton.Active = false
	nextButton.Visible = false
	nextButton.Parent = frame

	local prevButton = Instance.new("TextButton")
	prevButton.Name = "PrevButton"
	prevButton.Text = "Previous"
	prevButton.TextColor3 = Color3.new(1,1,1)
	prevButton.Font = Enum.Font.Arial
	prevButton.FontSize = Enum.FontSize.Size18
	prevButton.Style = Enum.ButtonStyle.RobloxButton
	prevButton.Size = UDim2.new(0,80, 0, 32)
	prevButton.Position = UDim2.new(0.5, -85, 1, -32)
	prevButton.Active = false
	prevButton.Visible = false
	prevButton.Parent = frame

	if giveDoneButton then
		innerFrame.Size = UDim2.new(1,0,1,-75)
	else
		innerFrame.Size = UDim2.new(1,0,1,-22)
	end

	local parentConnection = nil

	local function basicHandleResize()
		if frame.Visible and frame.Parent then
			local maxSize = math.min(frame.Parent.AbsoluteSize.X, frame.Parent.AbsoluteSize.Y)
			handleResize(200,maxSize)
		end
	end

	frame.Changed:connect(
		function(prop)
			if prop == "Parent" then
				if parentConnection ~= nil then
					parentConnection:disconnect()
					parentConnection = nil
				end
				if frame.Parent and frame.Parent:IsA("GuiObject") then
					parentConnection = frame.Parent.Changed:connect(
						function(parentProp)
							if parentProp == "AbsoluteSize" then
								wait()
								basicHandleResize()
							end
						end)
					basicHandleResize()
				end
			end

			if prop == "Visible" then 
				basicHandleResize()
			end
		end)

	return frame, innerFrame
end

t.CreateTextTutorialPage = function(name, text, skipTutorialFunc)
	local frame = nil
	local contentFrame = nil

	local textLabel = Instance.new("TextLabel")
	textLabel.BackgroundTransparency = 1
	textLabel.TextColor3 = Color3.new(1,1,1)
	textLabel.Text = text
	textLabel.TextWrap = true
	textLabel.TextXAlignment = Enum.TextXAlignment.Left
	textLabel.TextYAlignment = Enum.TextYAlignment.Center
	textLabel.Font = Enum.Font.Arial
	textLabel.FontSize = Enum.FontSize.Size14
	textLabel.Size = UDim2.new(1,0,1,0)

	local function handleResize(minSize, maxSize)
		size = binaryShrink(minSize, maxSize,
			function(size)
				frame.Size = UDim2.new(0, size, 0, size)
				return textLabel.TextFits
			end)
		frame.Size = UDim2.new(0, size, 0, size)
		frame.Position = UDim2.new(0.5, -size/2, 0.5, -size/2)
	end

	frame, contentFrame = CreateBasicTutorialPage(name, handleResize, skipTutorialFunc)
	textLabel.Parent = contentFrame

	return frame
end

t.CreateImageTutorialPage = function(name, imageAsset, x, y, skipTutorialFunc, giveDoneButton)
	local frame = nil
	local contentFrame = nil

	local imageLabel = Instance.new("ImageLabel")
	imageLabel.BackgroundTransparency = 1
	imageLabel.Image = imageAsset
	imageLabel.Size = UDim2.new(0,x,0,y)
	imageLabel.Position = UDim2.new(0.5,-x/2,0.5,-y/2)

	local function handleResize(minSize, maxSize)
		size = binaryShrink(minSize, maxSize,
			function(size)
				return size >= x and size >= y
			end)
		if size >= x and size >= y then
			imageLabel.Size = UDim2.new(0,x, 0,y)
			imageLabel.Position = UDim2.new(0.5,-x/2, 0.5, -y/2)
		else
			if x > y then
				--X is limiter, so 
				imageLabel.Size = UDim2.new(1,0,y/x,0)
				imageLabel.Position = UDim2.new(0,0, 0.5 - (y/x)/2, 0)
			else
				--Y is limiter
				imageLabel.Size = UDim2.new(x/y,0,1, 0)
				imageLabel.Position = UDim2.new(0.5-(x/y)/2, 0, 0, 0)
			end
		end
		size = size + 50
		frame.Size = UDim2.new(0, size, 0, size)
		frame.Position = UDim2.new(0.5, -size/2, 0.5, -size/2)
	end

	frame, contentFrame = CreateBasicTutorialPage(name, handleResize, skipTutorialFunc, giveDoneButton)
	imageLabel.Parent = contentFrame

	return frame
end

t.AddTutorialPage = function(tutorial, tutorialPage)
	local transitionFrame = tutorial.TransitionFrame
	local currentPageValue = tutorial.CurrentTutorialPage

	if not tutorial.Buttons.Value then
		tutorialPage.NextButton.Parent = nil
		tutorialPage.PrevButton.Parent = nil
	end

	local children = tutorial.Pages:GetChildren()
	if children and #children > 0 then
		tutorialPage.Name = "TutorialPage" .. (#children+1)
		local previousPage = children[#children]
		if not previousPage:IsA("GuiObject") then
			error("All elements under Pages must be GuiObjects")
		end

		if tutorial.Buttons.Value then
			if previousPage.NextButton.Active then
				error("NextButton already Active on previousPage, please only add pages with RbxGui.AddTutorialPage function")
			end
			previousPage.NextButton.MouseButton1Click:connect(
				function()
					TransitionTutorialPages(previousPage, tutorialPage, transitionFrame, currentPageValue)
				end)
			previousPage.NextButton.Active = true
			previousPage.NextButton.Visible = true

			if tutorialPage.PrevButton.Active then
				error("PrevButton already Active on tutorialPage, please only add pages with RbxGui.AddTutorialPage function")
			end
			tutorialPage.PrevButton.MouseButton1Click:connect(
				function()
					TransitionTutorialPages(tutorialPage, previousPage, transitionFrame, currentPageValue)
				end)
			tutorialPage.PrevButton.Active = true
			tutorialPage.PrevButton.Visible = true
		end

		tutorialPage.Parent = tutorial.Pages
	else
		--First child
		tutorialPage.Name = "TutorialPage1"
		tutorialPage.Parent = tutorial.Pages
	end
end 

t.CreateSetPanel = function(userIdsForSets, objectSelected, dialogClosed, size, position, showAdminCategories, useAssetVersionId)

	if not userIdsForSets then
		error("CreateSetPanel: userIdsForSets (first arg) is nil, should be a table of number ids")
	end
	if type(userIdsForSets) ~= "table" and type(userIdsForSets) ~= "userdata" then
		error("CreateSetPanel: userIdsForSets (first arg) is of type " ..type(userIdsForSets) .. ", should be of type table or userdata")
	end
	if not objectSelected then
		error("CreateSetPanel: objectSelected (second arg) is nil, should be a callback function!")
	end
	if type(objectSelected) ~= "function" then
		error("CreateSetPanel: objectSelected (second arg) is of type " .. type(objectSelected) .. ", should be of type function!")
	end
	if dialogClosed and type(dialogClosed) ~= "function" then
		error("CreateSetPanel: dialogClosed (third arg) is of type " .. type(dialogClosed) .. ", should be of type function!")
	end
	
	if showAdminCategories == nil then -- by default, don't show beta sets
		showAdminCategories = false
	end

	local arrayPosition = 1
	local insertButtons = {}
	local insertButtonCons = {}
	local contents = nil
	local setGui = nil

	-- used for water selections
	local waterForceDirection = "NegX"
	local waterForce = "None"
	local waterGui, waterTypeChangedEvent = nil
	
	local Data = {}
	Data.CurrentCategory = nil
	Data.Category = {}
	local SetCache = {}
	
	local userCategoryButtons = nil
	
	local buttonWidth = 64
	local buttonHeight = buttonWidth
	
	local SmallThumbnailUrl = nil
	local LargeThumbnailUrl = nil
	local BaseUrl = game:GetService("ContentProvider").BaseUrl:lower()
	local AssetGameUrl = string.gsub(BaseUrl, "www", "assetgame")
	
	if useAssetVersionId then
		LargeThumbnailUrl = AssetGameUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=420&ht=420&assetversionid="
		SmallThumbnailUrl = AssetGameUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=75&ht=75&assetversionid="
	else
		LargeThumbnailUrl = AssetGameUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=420&ht=420&aid="
		SmallThumbnailUrl = AssetGameUrl .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=75&ht=75&aid="
	end
		
	local function drillDownSetZIndex(parent, index)
		local children = parent:GetChildren()
		for i = 1, #children do
			if children[i]:IsA("GuiObject") then
				children[i].ZIndex = index
			end
			drillDownSetZIndex(children[i], index)
		end
	end
	
	-- for terrain stamping
	local currTerrainDropDownFrame = nil
	local terrainShapes = {"Block","Vertical Ramp","Corner Wedge","Inverse Corner Wedge","Horizontal Ramp","Auto-Wedge"}
	local terrainShapeMap = {}
	for i = 1, #terrainShapes do
		terrainShapeMap[terrainShapes[i]] = i - 1
	end	
	terrainShapeMap[terrainShapes[#terrainShapes]] = 6

	local function createWaterGui()
		local waterForceDirections = {"NegX","X","NegY","Y","NegZ","Z"}
		local waterForces = {"None", "Small", "Medium", "Strong", "Max"}

		local waterFrame = Instance.new("Frame")
		waterFrame.Name = "WaterFrame"
		waterFrame.Style = Enum.FrameStyle.RobloxSquare
		waterFrame.Size = UDim2.new(0,150,0,110)
		waterFrame.Visible = false

		local waterForceLabel = Instance.new("TextLabel")
		waterForceLabel.Name = "WaterForceLabel"
		waterForceLabel.BackgroundTransparency = 1
		waterForceLabel.Size = UDim2.new(1,0,0,12)
		waterForceLabel.Font = Enum.Font.ArialBold
		waterForceLabel.FontSize = Enum.FontSize.Size12
		waterForceLabel.TextColor3 = Color3.new(1,1,1)
		waterForceLabel.TextXAlignment = Enum.TextXAlignment.Left
		waterForceLabel.Text = "Water Force"
		waterForceLabel.Parent = waterFrame

		local waterForceDirLabel = waterForceLabel:Clone()
		waterForceDirLabel.Name = "WaterForceDirectionLabel"
		waterForceDirLabel.Text = "Water Force Direction"
		waterForceDirLabel.Position = UDim2.new(0,0,0,50)
		waterForceDirLabel.Parent = waterFrame

		local waterTypeChangedEvent = Instance.new("BindableEvent",waterFrame)
		waterTypeChangedEvent.Name = "WaterTypeChangedEvent"

		local waterForceDirectionSelectedFunc = function(newForceDirection)
			waterForceDirection = newForceDirection
			waterTypeChangedEvent:Fire({waterForce, waterForceDirection})
		end
		local waterForceSelectedFunc = function(newForce)
			waterForce = newForce
			waterTypeChangedEvent:Fire({waterForce, waterForceDirection})
		end

		local waterForceDirectionDropDown, forceWaterDirectionSelection = t.CreateDropDownMenu(waterForceDirections, waterForceDirectionSelectedFunc)
		waterForceDirectionDropDown.Size = UDim2.new(1,0,0,25)
		waterForceDirectionDropDown.Position = UDim2.new(0,0,1,3)
		forceWaterDirectionSelection("NegX")
		waterForceDirectionDropDown.Parent = waterForceDirLabel

		local waterForceDropDown, forceWaterForceSelection = t.CreateDropDownMenu(waterForces, waterForceSelectedFunc)
		forceWaterForceSelection("None")
		waterForceDropDown.Size = UDim2.new(1,0,0,25)
		waterForceDropDown.Position = UDim2.new(0,0,1,3)
		waterForceDropDown.Parent = waterForceLabel

		return waterFrame, waterTypeChangedEvent
	end

	-- Helper Function that contructs gui elements
	local function createSetGui()
	
		local setGui = Instance.new("ScreenGui")
		setGui.Name = "SetGui"
		
		local setPanel = Instance.new("Frame")
		setPanel.Name = "SetPanel"
		setPanel.Active = true
		setPanel.BackgroundTransparency = 1
		if position then
			setPanel.Position = position
		else
			setPanel.Position = UDim2.new(0.2, 29, 0.1, 24)
		end
		if size then
			setPanel.Size = size
		else
			setPanel.Size = UDim2.new(0.6, -58, 0.64, 0)
		end
		setPanel.Style = Enum.FrameStyle.RobloxRound
		setPanel.ZIndex = 6
		setPanel.Parent = setGui
		
			-- Children of SetPanel
			local itemPreview = Instance.new("Frame")
			itemPreview.Name = "ItemPreview"
			itemPreview.BackgroundTransparency = 1
			itemPreview.Position = UDim2.new(0.8,5,0.085,0)
			itemPreview.Size = UDim2.new(0.21,0,0.9,0)
			itemPreview.ZIndex = 6
			itemPreview.Parent = setPanel
			
				-- Children of ItemPreview
				local textPanel = Instance.new("Frame")
				textPanel.Name = "TextPanel"
				textPanel.BackgroundTransparency = 1
				textPanel.Position = UDim2.new(0,0,0.45,0)
				textPanel.Size = UDim2.new(1,0,0.55,0)
				textPanel.ZIndex = 6
				textPanel.Parent = itemPreview
					
					-- Children of TextPanel
					local rolloverText = Instance.new("TextLabel")
					rolloverText.Name = "RolloverText"
					rolloverText.BackgroundTransparency = 1
					rolloverText.Size = UDim2.new(1,0,0,48)
					rolloverText.ZIndex = 6
					rolloverText.Font = Enum.Font.ArialBold
					rolloverText.FontSize = Enum.FontSize.Size24
					rolloverText.Text = ""
					rolloverText.TextColor3 = Color3.new(1,1,1)
					rolloverText.TextWrap = true
					rolloverText.TextXAlignment = Enum.TextXAlignment.Left
					rolloverText.TextYAlignment = Enum.TextYAlignment.Top
					rolloverText.Parent = textPanel
					
				local largePreview = Instance.new("ImageLabel")
				largePreview.Name = "LargePreview"
				largePreview.BackgroundTransparency = 1
				largePreview.Image = ""
				largePreview.Size = UDim2.new(1,0,0,170)
				largePreview.ZIndex = 6
				largePreview.Parent = itemPreview
				
			local sets = Instance.new("Frame")
			sets.Name = "Sets"
			sets.BackgroundTransparency = 1
			sets.Position = UDim2.new(0,0,0,5)
			sets.Size = UDim2.new(0.23,0,1,-5)
			sets.ZIndex = 6
			sets.Parent = setPanel
			
				-- Children of Sets
				local line = Instance.new("Frame")
				line.Name = "Line"
				line.BackgroundColor3 = Color3.new(1,1,1)
				line.BackgroundTransparency = 0.7
				line.BorderSizePixel = 0
				line.Position = UDim2.new(1,-3,0.06,0)
				line.Size = UDim2.new(0,3,0.9,0)
				line.ZIndex = 6
				line.Parent = sets
				
				local setsLists, controlFrame = t.CreateTrueScrollingFrame()
				setsLists.Size = UDim2.new(1,-6,0.94,0)
				setsLists.Position = UDim2.new(0,0,0.06,0)
				setsLists.BackgroundTransparency = 1
				setsLists.Name = "SetsLists"
				setsLists.ZIndex = 6
				setsLists.Parent = sets
				drillDownSetZIndex(controlFrame, 7)
					
				local setsHeader = Instance.new("TextLabel")
				setsHeader.Name = "SetsHeader"
				setsHeader.BackgroundTransparency = 1
				setsHeader.Size = UDim2.new(0,47,0,24)
				setsHeader.ZIndex = 6
				setsHeader.Font = Enum.Font.ArialBold
				setsHeader.FontSize = Enum.FontSize.Size24
				setsHeader.Text = "Sets"
				setsHeader.TextColor3 = Color3.new(1,1,1)
				setsHeader.TextXAlignment = Enum.TextXAlignment.Left
				setsHeader.TextYAlignment = Enum.TextYAlignment.Top
				setsHeader.Parent = sets
			
			local cancelButton = Instance.new("TextButton")
			cancelButton.Name = "CancelButton"
			cancelButton.Position = UDim2.new(1,-32,0,-2)
			cancelButton.Size = UDim2.new(0,34,0,34)
			cancelButton.Style = Enum.ButtonStyle.RobloxButtonDefault
			cancelButton.ZIndex = 6
			cancelButton.Text = ""
			cancelButton.Modal = true
			cancelButton.Parent = setPanel
			
				-- Children of Cancel Button
				local cancelImage = Instance.new("ImageLabel")
				cancelImage.Name = "CancelImage"
				cancelImage.BackgroundTransparency = 1
				cancelImage.Image = "https://www.roblox.com/asset/?id=54135717"
				cancelImage.Position = UDim2.new(0,-2,0,-2)
				cancelImage.Size = UDim2.new(0,16,0,16)
				cancelImage.ZIndex = 6
				cancelImage.Parent = cancelButton
					
		return setGui
	end
	
	local function createSetButton(text)
		local setButton = Instance.new("TextButton")
		
		if text then setButton.Text = text
		else setButton.Text = "" end
		
		setButton.AutoButtonColor = false
		setButton.BackgroundTransparency = 1
		setButton.BackgroundColor3 = Color3.new(1,1,1)
		setButton.BorderSizePixel = 0
		setButton.Size = UDim2.new(1,-5,0,18)
		setButton.ZIndex = 6
		setButton.Visible = false
		setButton.Font = Enum.Font.Arial
		setButton.FontSize = Enum.FontSize.Size18
		setButton.TextColor3 = Color3.new(1,1,1)
		setButton.TextXAlignment = Enum.TextXAlignment.Left
		
		return setButton
	end
	
	local function buildSetButton(name, setId, setImageId, i,  count)
		local button = createSetButton(name)
		button.Text = name
		button.Name = "SetButton"
		button.Visible = true
		
		local setValue = Instance.new("IntValue")
		setValue.Name = "SetId"
		setValue.Value = setId
		setValue.Parent = button

		local setName = Instance.new("StringValue")
		setName.Name = "SetName"
		setName.Value = name
		setName.Parent = button

		return button
	end
	
	local function processCategory(sets)
		local setButtons = {}
		local numSkipped = 0
		for i = 1, #sets do
			if not showAdminCategories and sets[i].Name == "Beta" then
				numSkipped = numSkipped + 1
			else
				setButtons[i - numSkipped] = buildSetButton(sets[i].Name, sets[i].CategoryId, sets[i].ImageAssetId, i - numSkipped, #sets)
			end
		end
		return setButtons
	end
	
	local function handleResize()
		wait() -- neccessary to insure heartbeat happened
		
		local itemPreview = setGui.SetPanel.ItemPreview
		
		itemPreview.LargePreview.Size = UDim2.new(1,0,0,itemPreview.AbsoluteSize.X)
		itemPreview.LargePreview.Position = UDim2.new(0.5,-itemPreview.LargePreview.AbsoluteSize.X/2,0,0)
		itemPreview.TextPanel.Position = UDim2.new(0,0,0,itemPreview.LargePreview.AbsoluteSize.Y)
		itemPreview.TextPanel.Size = UDim2.new(1,0,0,itemPreview.AbsoluteSize.Y - itemPreview.LargePreview.AbsoluteSize.Y)
	end
	
	local function makeInsertAssetButton()
		local insertAssetButtonExample = Instance.new("Frame")
		insertAssetButtonExample.Name = "InsertAssetButtonExample"
		insertAssetButtonExample.Position = UDim2.new(0,128,0,64)
		insertAssetButtonExample.Size = UDim2.new(0,64,0,64)
		insertAssetButtonExample.BackgroundTransparency = 1
		insertAssetButtonExample.ZIndex = 6
		insertAssetButtonExample.Visible = false

		local assetId = Instance.new("IntValue")
		assetId.Name = "AssetId"
		assetId.Value = 0
		assetId.Parent = insertAssetButtonExample
		
		local assetName = Instance.new("StringValue")
		assetName.Name = "AssetName"
		assetName.Value = ""
		assetName.Parent = insertAssetButtonExample

		local button = Instance.new("TextButton")
		button.Name = "Button"
		button.Text = ""
		button.Style = Enum.ButtonStyle.RobloxButton
		button.Position = UDim2.new(0.025,0,0.025,0)
		button.Size = UDim2.new(0.95,0,0.95,0)
		button.ZIndex = 6
		button.Parent = insertAssetButtonExample

		local buttonImage = Instance.new("ImageLabel")
		buttonImage.Name = "ButtonImage"
		buttonImage.Image = ""
		buttonImage.Position = UDim2.new(0,-7,0,-7)
		buttonImage.Size = UDim2.new(1,14,1,14)
		buttonImage.BackgroundTransparency = 1
		buttonImage.ZIndex = 7
		buttonImage.Parent = button

		local configIcon = buttonImage:clone()
		configIcon.Name = "ConfigIcon"
		configIcon.Visible = false
		configIcon.Position = UDim2.new(1,-23,1,-24)
		configIcon.Size = UDim2.new(0,16,0,16)
		configIcon.Image = ""
		configIcon.ZIndex = 6
		configIcon.Parent = insertAssetButtonExample
		
		return insertAssetButtonExample
	end
	
	local function showLargePreview(insertButton)
		if insertButton:FindFirstChild("AssetId") then
			delay(0,function()
				game:GetService("ContentProvider"):Preload(LargeThumbnailUrl .. tostring(insertButton.AssetId.Value))
				setGui.SetPanel.ItemPreview.LargePreview.Image = LargeThumbnailUrl .. tostring(insertButton.AssetId.Value)
			end)
		end
		if insertButton:FindFirstChild("AssetName") then
			setGui.SetPanel.ItemPreview.TextPanel.RolloverText.Text = insertButton.AssetName.Value
		end
	end
	
	local function selectTerrainShape(shape)
		if currTerrainDropDownFrame then
			objectSelected(tostring(currTerrainDropDownFrame.AssetName.Value), tonumber(currTerrainDropDownFrame.AssetId.Value), shape)
		end
	end
	
	local function createTerrainTypeButton(name, parent)
		local dropDownTextButton = Instance.new("TextButton")
		dropDownTextButton.Name = name .. "Button"
		dropDownTextButton.Font = Enum.Font.ArialBold
		dropDownTextButton.FontSize = Enum.FontSize.Size14
		dropDownTextButton.BorderSizePixel = 0
		dropDownTextButton.TextColor3 = Color3.new(1,1,1)
		dropDownTextButton.Text = name
		dropDownTextButton.TextXAlignment = Enum.TextXAlignment.Left
		dropDownTextButton.BackgroundTransparency = 1
		dropDownTextButton.ZIndex = parent.ZIndex + 1
		dropDownTextButton.Size = UDim2.new(0,parent.Size.X.Offset - 2,0,16)
		dropDownTextButton.Position = UDim2.new(0,1,0,0)

		dropDownTextButton.MouseEnter:connect(function()
			dropDownTextButton.BackgroundTransparency = 0
			dropDownTextButton.TextColor3 = Color3.new(0,0,0)
		end)

		dropDownTextButton.MouseLeave:connect(function()
			dropDownTextButton.BackgroundTransparency = 1
			dropDownTextButton.TextColor3 = Color3.new(1,1,1)
		end)

		dropDownTextButton.MouseButton1Click:connect(function()
			dropDownTextButton.BackgroundTransparency = 1
			dropDownTextButton.TextColor3 = Color3.new(1,1,1)
			if dropDownTextButton.Parent and dropDownTextButton.Parent:IsA("GuiObject") then
				dropDownTextButton.Parent.Visible = false
			end
			selectTerrainShape(terrainShapeMap[dropDownTextButton.Text])
		end)

		return dropDownTextButton
	end
	
	local function createTerrainDropDownMenu(zIndex)
		local dropDown = Instance.new("Frame")
		dropDown.Name = "TerrainDropDown"
		dropDown.BackgroundColor3 = Color3.new(0,0,0)
		dropDown.BorderColor3 = Color3.new(1,0,0)
		dropDown.Size = UDim2.new(0,200,0,0)
		dropDown.Visible = false
		dropDown.ZIndex = zIndex
		dropDown.Parent = setGui

		for i = 1, #terrainShapes do
			local shapeButton = createTerrainTypeButton(terrainShapes[i],dropDown)
			shapeButton.Position = UDim2.new(0,1,0,(i - 1) * (shapeButton.Size.Y.Offset))
			shapeButton.Parent = dropDown
			dropDown.Size = UDim2.new(0,200,0,dropDown.Size.Y.Offset + (shapeButton.Size.Y.Offset))
		end

		dropDown.MouseLeave:connect(function()
			dropDown.Visible = false
		end)
	end

	
	local function createDropDownMenuButton(parent)
		local dropDownButton = Instance.new("ImageButton")
		dropDownButton.Name = "DropDownButton"
		dropDownButton.Image = "https://www.roblox.com/asset/?id=67581509"
		dropDownButton.BackgroundTransparency = 1
		dropDownButton.Size = UDim2.new(0,16,0,16)
		dropDownButton.Position = UDim2.new(1,-24,0,6)
		dropDownButton.ZIndex = parent.ZIndex + 2
		dropDownButton.Parent = parent
		
		if not setGui:FindFirstChild("TerrainDropDown") then
			createTerrainDropDownMenu(8)
		end
		
		dropDownButton.MouseButton1Click:connect(function()
			setGui.TerrainDropDown.Visible = true
			setGui.TerrainDropDown.Position = UDim2.new(0,parent.AbsolutePosition.X,0,parent.AbsolutePosition.Y)
			currTerrainDropDownFrame = parent
		end)
	end
	
	local function buildInsertButton()
		local insertButton = makeInsertAssetButton()
		insertButton.Name = "InsertAssetButton"
		insertButton.Visible = true

		if Data.Category[Data.CurrentCategory].SetName == "High Scalability" then
			createDropDownMenuButton(insertButton)
		end

		local lastEnter = nil
		local mouseEnterCon = insertButton.MouseEnter:connect(function()
			lastEnter = insertButton
			delay(0.1,function()
				if lastEnter == insertButton then
					showLargePreview(insertButton)
				end
			end)
		end)
		return insertButton, mouseEnterCon
	end
	
	local function realignButtonGrid(columns)
		local x = 0
		local y = 0 
		for i = 1, #insertButtons do
			insertButtons[i].Position = UDim2.new(0, buttonWidth * x, 0, buttonHeight * y)
			x = x + 1
			if x >= columns then
				x = 0
				y = y + 1
			end
		end
	end

	local function setInsertButtonImageBehavior(insertFrame, visible, name, assetId)
		if visible then
			insertFrame.AssetName.Value = name
			insertFrame.AssetId.Value = assetId
			local newImageUrl = SmallThumbnailUrl  .. assetId
			if newImageUrl ~= insertFrame.Button.ButtonImage.Image then
				delay(0,function()
					game:GetService("ContentProvider"):Preload(SmallThumbnailUrl  .. assetId)
					if insertFrame:findFirstChild("Button") then
						insertFrame.Button.ButtonImage.Image = SmallThumbnailUrl  .. assetId
					end
				end)
			end
			table.insert(insertButtonCons,
				insertFrame.Button.MouseButton1Click:connect(function()
					-- special case for water, show water selection gui
					local isWaterSelected = (name == "Water") and (Data.Category[Data.CurrentCategory].SetName == "High Scalability")
					waterGui.Visible = isWaterSelected
					if isWaterSelected then
						objectSelected(name, tonumber(assetId), nil)
					else
						objectSelected(name, tonumber(assetId))
					end
				end)
			)
			insertFrame.Visible = true
		else
			insertFrame.Visible = false
		end
	end
	
	local function loadSectionOfItems(setGui, rows, columns)
		local pageSize = rows * columns

		if arrayPosition > #contents then return end

		local origArrayPos = arrayPosition

		local yCopy = 0
		for i = 1, pageSize + 1 do 
			if arrayPosition >= #contents + 1 then
				break
			end

			local buttonCon
			insertButtons[arrayPosition], buttonCon = buildInsertButton()
			table.insert(insertButtonCons,buttonCon)
			insertButtons[arrayPosition].Parent = setGui.SetPanel.ItemsFrame
			arrayPosition = arrayPosition + 1
		end
		realignButtonGrid(columns)

		local indexCopy = origArrayPos
		for index = origArrayPos, arrayPosition do
			if insertButtons[index] then
				if contents[index] then

					-- we don't want water to have a drop down button
					if contents[index].Name == "Water" then
						if Data.Category[Data.CurrentCategory].SetName == "High Scalability" then
							insertButtons[index]:FindFirstChild("DropDownButton",true):Destroy()
						end
					end

					local assetId
					if useAssetVersionId then
						assetId = contents[index].AssetVersionId
					else
						assetId = contents[index].AssetId
					end
					setInsertButtonImageBehavior(insertButtons[index], true, contents[index].Name, assetId)
				else
					break
				end
			else
				break
			end
			indexCopy = index
		end
	end
	
	local function setSetIndex()
		Data.Category[Data.CurrentCategory].Index = 0

		rows = 7
		columns = math.floor(setGui.SetPanel.ItemsFrame.AbsoluteSize.X/buttonWidth)

		contents = Data.Category[Data.CurrentCategory].Contents
		if contents then
			-- remove our buttons and their connections
			for i = 1, #insertButtons do
				insertButtons[i]:remove()
			end
			for i = 1, #insertButtonCons do
				if insertButtonCons[i] then insertButtonCons[i]:disconnect() end
			end
			insertButtonCons = {}
			insertButtons = {}

			arrayPosition = 1
			loadSectionOfItems(setGui, rows, columns)
		end
	end
	
	local function selectSet(button, setName, setId, setIndex)
		if button and Data.Category[Data.CurrentCategory] ~= nil then
			if button ~= Data.Category[Data.CurrentCategory].Button then
				Data.Category[Data.CurrentCategory].Button = button

				if SetCache[setId] == nil then
					SetCache[setId] = game:GetService("InsertService"):GetCollection(setId)
				end
				Data.Category[Data.CurrentCategory].Contents = SetCache[setId]

				Data.Category[Data.CurrentCategory].SetName = setName
				Data.Category[Data.CurrentCategory].SetId = setId
			end
			setSetIndex()
		end
	end
	
	local function selectCategoryPage(buttons, page)
		if buttons ~= Data.CurrentCategory then
			if Data.CurrentCategory then
				for key, button in pairs(Data.CurrentCategory) do
					button.Visible = false
				end
			end

			Data.CurrentCategory = buttons
			if Data.Category[Data.CurrentCategory] == nil then
				Data.Category[Data.CurrentCategory] = {}
				if #buttons > 0 then
					selectSet(buttons[1], buttons[1].SetName.Value, buttons[1].SetId.Value, 0)
				end
			else
				Data.Category[Data.CurrentCategory].Button = nil
				selectSet(Data.Category[Data.CurrentCategory].ButtonFrame, Data.Category[Data.CurrentCategory].SetName, Data.Category[Data.CurrentCategory].SetId, Data.Category[Data.CurrentCategory].Index)
			end
		end
	end
	
	local function selectCategory(category)
		selectCategoryPage(category, 0)
	end
	
	local function resetAllSetButtonSelection()
		local setButtons = setGui.SetPanel.Sets.SetsLists:GetChildren()
		for i = 1, #setButtons do
			if setButtons[i]:IsA("TextButton") then
				setButtons[i].Selected = false
				setButtons[i].BackgroundTransparency = 1
				setButtons[i].TextColor3 = Color3.new(1,1,1)
				setButtons[i].BackgroundColor3 = Color3.new(1,1,1)
			end
		end
	end
	
	local function populateSetsFrame()
		local currRow = 0
		for i = 1, #userCategoryButtons do
			local button = userCategoryButtons[i]
			button.Visible = true
			button.Position = UDim2.new(0,5,0,currRow * button.Size.Y.Offset)
			button.Parent = setGui.SetPanel.Sets.SetsLists
			
			if i == 1 then -- we will have this selected by default, so show it
				button.Selected = true
				button.BackgroundColor3 = Color3.new(0,204/255,0)
				button.TextColor3 = Color3.new(0,0,0)
				button.BackgroundTransparency = 0
			end

			button.MouseEnter:connect(function()
				if not button.Selected then
					button.BackgroundTransparency = 0
					button.TextColor3 = Color3.new(0,0,0)
				end
			end)
			button.MouseLeave:connect(function()
				if not button.Selected then
					button.BackgroundTransparency = 1
					button.TextColor3 = Color3.new(1,1,1)
				end
			end)
			button.MouseButton1Click:connect(function()
				resetAllSetButtonSelection()
				button.Selected = not button.Selected
				button.BackgroundColor3 = Color3.new(0,204/255,0)
				button.TextColor3 = Color3.new(0,0,0)
				button.BackgroundTransparency = 0
				selectSet(button, button.Text, userCategoryButtons[i].SetId.Value, 0)
			end)

			currRow = currRow + 1
		end

		local buttons =  setGui.SetPanel.Sets.SetsLists:GetChildren()

		-- set first category as loaded for default
		if buttons then
			for i = 1, #buttons do
				if buttons[i]:IsA("TextButton") then
					selectSet(buttons[i], buttons[i].Text, userCategoryButtons[i].SetId.Value, 0)
					selectCategory(userCategoryButtons)
					break
				end
			end
		end
	end

	setGui = createSetGui()
	waterGui, waterTypeChangedEvent = createWaterGui()
	waterGui.Position = UDim2.new(0,55,0,0)
	waterGui.Parent = setGui
	setGui.Changed:connect(function(prop) -- this resizes the preview image to always be the right size
		if prop == "AbsoluteSize" then
			handleResize()
			setSetIndex()
		end
	end)
	
	local scrollFrame, controlFrame = t.CreateTrueScrollingFrame()
	scrollFrame.Size = UDim2.new(0.54,0,0.85,0)
	scrollFrame.Position = UDim2.new(0.24,0,0.085,0)
	scrollFrame.Name = "ItemsFrame"
	scrollFrame.ZIndex = 6
	scrollFrame.Parent = setGui.SetPanel
	scrollFrame.BackgroundTransparency = 1

	drillDownSetZIndex(controlFrame,7)

	controlFrame.Parent = setGui.SetPanel
	controlFrame.Position = UDim2.new(0.76, 5, 0, 0)

	local debounce = false
	controlFrame.ScrollBottom.Changed:connect(function(prop)
		if controlFrame.ScrollBottom.Value == true then
			if debounce then return end
			debounce = true
				loadSectionOfItems(setGui, rows, columns)
			debounce = false
		end
	end)

	local userData = {}
	for id = 1, #userIdsForSets do
		local newUserData = game:GetService("InsertService"):GetUserSets(userIdsForSets[id])
		if newUserData and #newUserData > 2 then
			-- start at #3 to skip over My Decals and My Models for each account
			for category = 3, #newUserData do
				if newUserData[category].Name == "High Scalability" then -- we want high scalability parts to show first
					table.insert(userData,1,newUserData[category])
				else
					table.insert(userData, newUserData[category])
				end
			end
		end
	
	end
	if userData then
		userCategoryButtons = processCategory(userData)
	end

	rows = math.floor(setGui.SetPanel.ItemsFrame.AbsoluteSize.Y/buttonHeight)
	columns = math.floor(setGui.SetPanel.ItemsFrame.AbsoluteSize.X/buttonWidth)

	populateSetsFrame()

	setGui.SetPanel.CancelButton.MouseButton1Click:connect(function()
		setGui.SetPanel.Visible = false
		if dialogClosed then dialogClosed() end
	end)
	
	local setVisibilityFunction = function(visible)
		if visible then
			setGui.SetPanel.Visible = true
		else
			setGui.SetPanel.Visible = false
		end
	end
	
	local getVisibilityFunction = function()
		if setGui then
			if setGui:FindFirstChild("SetPanel") then
				return setGui.SetPanel.Visible
			end
		end
		
		return false
	end
	
	return setGui, setVisibilityFunction, getVisibilityFunction, waterTypeChangedEvent
end

t.CreateTerrainMaterialSelector = function(size,position)
	local terrainMaterialSelectionChanged = Instance.new("BindableEvent")
	terrainMaterialSelectionChanged.Name = "TerrainMaterialSelectionChanged"

	local selectedButton = nil

	local frame = Instance.new("Frame")
	frame.Name = "TerrainMaterialSelector"
	if size then
		frame.Size = size
	else
		frame.Size = UDim2.new(0, 245, 0, 230)
	end
	if position then
		frame.Position = position
	end
	frame.BorderSizePixel = 0
	frame.BackgroundColor3 = Color3.new(0,0,0)
	frame.Active = true

	terrainMaterialSelectionChanged.Parent = frame

	local waterEnabled = true -- todo: turn this on when water is ready

	local materialToImageMap = {}
	local materialNames = {"Grass", "Sand", "Brick", "Granite", "Asphalt", "Iron", "Aluminum", "Gold", "Plank", "Log", "Gravel", "Cinder Block", "Stone Wall", "Concrete", "Plastic (red)", "Plastic (blue)"}
	if waterEnabled then
		table.insert(materialNames,"Water")
	end
	local currentMaterial = 1

	function getEnumFromName(choice)
		if choice == "Grass" then return 1 end
		if choice == "Sand" then return 2 end 
		if choice == "Erase" then return 0 end
		if choice == "Brick" then return 3 end
		if choice == "Granite" then return 4 end
		if choice == "Asphalt" then return 5 end
		if choice == "Iron" then return 6 end
		if choice == "Aluminum" then return 7 end
		if choice == "Gold" then return 8 end
		if choice == "Plank" then return 9 end
		if choice == "Log" then return 10 end
		if choice == "Gravel" then return 11 end
		if choice == "Cinder Block" then return 12 end
		if choice == "Stone Wall" then return 13 end
		if choice == "Concrete" then return 14 end
		if choice == "Plastic (red)" then return 15 end
		if choice == "Plastic (blue)" then return 16 end
		if choice == "Water" then return 17 end
	end

	function getNameFromEnum(choice)
		if choice == Enum.CellMaterial.Grass or choice == 1 then return "Grass"end
		if choice == Enum.CellMaterial.Sand or choice == 2 then return "Sand" end 
		if choice == Enum.CellMaterial.Empty or choice == 0 then return "Erase" end
		if choice == Enum.CellMaterial.Brick or choice == 3 then return "Brick" end
		if choice == Enum.CellMaterial.Granite or choice == 4 then return "Granite" end
		if choice == Enum.CellMaterial.Asphalt or choice == 5 then return "Asphalt" end
		if choice == Enum.CellMaterial.Iron or choice == 6 then return "Iron" end
		if choice == Enum.CellMaterial.Aluminum or choice == 7 then return "Aluminum" end
		if choice == Enum.CellMaterial.Gold or choice == 8 then return "Gold" end
		if choice == Enum.CellMaterial.WoodPlank or choice == 9 then return "Plank" end
		if choice == Enum.CellMaterial.WoodLog or choice == 10 then return "Log" end
		if choice == Enum.CellMaterial.Gravel or choice == 11 then return "Gravel" end
		if choice == Enum.CellMaterial.CinderBlock or choice == 12 then return "Cinder Block" end
		if choice == Enum.CellMaterial.MossyStone or choice == 13 then return "Stone Wall" end
		if choice == Enum.CellMaterial.Cement or choice == 14 then return "Concrete" end
		if choice == Enum.CellMaterial.RedPlastic or choice == 15 then return "Plastic (red)" end
		if choice == Enum.CellMaterial.BluePlastic or choice == 16 then return "Plastic (blue)" end

		if waterEnabled then
			if choice == Enum.CellMaterial.Water or choice == 17 then return "Water" end
		end
	end


	local function updateMaterialChoice(choice)
		currentMaterial = getEnumFromName(choice)
		terrainMaterialSelectionChanged:Fire(currentMaterial)
	end

	-- we so need a better way to do this
	for i,v in pairs(materialNames) do
		materialToImageMap[v] = {}
		if v == "Grass" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=56563112"
		elseif v == "Sand" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=62356652"
		elseif v == "Brick" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=65961537"
		elseif v == "Granite" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532153"
		elseif v == "Asphalt" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532038"
		elseif v == "Iron" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532093"
		elseif v == "Aluminum" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67531995"
		elseif v == "Gold" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532118"
		elseif v == "Plastic (red)" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67531848"
		elseif v == "Plastic (blue)" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67531924"
		elseif v == "Plank" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532015"
		elseif v == "Log" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532051"
		elseif v == "Gravel" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532206"
		elseif v == "Cinder Block" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532103"
		elseif v == "Stone Wall" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67531804"
		elseif v == "Concrete" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=67532059"
		elseif v == "Water" then materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=81407474"
		else materialToImageMap[v].Regular = "https://www.roblox.com/asset/?id=66887593" -- fill in the rest here!!
		end
	end

	local scrollFrame, scrollUp, scrollDown, recalculateScroll = t.CreateScrollingFrame(nil,"grid")
	scrollFrame.Size = UDim2.new(0.85,0,1,0)
	scrollFrame.Position = UDim2.new(0,0,0,0)
	scrollFrame.Parent = frame

	scrollUp.Parent = frame
	scrollUp.Visible = true
	scrollUp.Position = UDim2.new(1,-19,0,0)

	scrollDown.Parent = frame
	scrollDown.Visible = true
	scrollDown.Position = UDim2.new(1,-19,1,-17)

	local function goToNewMaterial(buttonWrap, materialName)
		updateMaterialChoice(materialName)
		buttonWrap.BackgroundTransparency = 0
		selectedButton.BackgroundTransparency = 1
		selectedButton = buttonWrap
	end

	local function createMaterialButton(name)	
		local buttonWrap = Instance.new("TextButton")
		buttonWrap.Text = ""
		buttonWrap.Size = UDim2.new(0,32,0,32)
		buttonWrap.BackgroundColor3 = Color3.new(1,1,1)
		buttonWrap.BorderSizePixel = 0
		buttonWrap.BackgroundTransparency = 1
		buttonWrap.AutoButtonColor = false
		buttonWrap.Name = tostring(name)
		
		local imageButton = Instance.new("ImageButton")
		imageButton.AutoButtonColor = false
		imageButton.BackgroundTransparency = 1
		imageButton.Size = UDim2.new(0,30,0,30)
		imageButton.Position = UDim2.new(0,1,0,1)
		imageButton.Name = tostring(name)
		imageButton.Parent = buttonWrap
		imageButton.Image = materialToImageMap[name].Regular

		local enumType = Instance.new("NumberValue")
		enumType.Name = "EnumType"
		enumType.Parent = buttonWrap
		enumType.Value = 0
		
		imageButton.MouseEnter:connect(function()
			buttonWrap.BackgroundTransparency = 0
		end)
		imageButton.MouseLeave:connect(function()
			if selectedButton ~= buttonWrap then
				buttonWrap.BackgroundTransparency = 1
			end
		end)
		imageButton.MouseButton1Click:connect(function()
			if selectedButton ~= buttonWrap then
				goToNewMaterial(buttonWrap, tostring(name))
			end
		end)
		
		return buttonWrap 
	end

	for i = 1, #materialNames do
		local imageButton = createMaterialButton(materialNames[i])
		
		if materialNames[i] == "Grass" then -- always start with grass as the default
			selectedButton = imageButton
			imageButton.BackgroundTransparency = 0
		end
		
		imageButton.Parent = scrollFrame
	end

	local forceTerrainMaterialSelection = function(newMaterialType)
		if not newMaterialType then return end
		if currentMaterial == newMaterialType then return end

		local matName = getNameFromEnum(newMaterialType)
		local buttons = scrollFrame:GetChildren()
		for i = 1, #buttons do
			if buttons[i].Name == "Plastic (blue)" and matName == "Plastic (blue)" then goToNewMaterial(buttons[i],matName) return end
			if buttons[i].Name == "Plastic (red)" and matName == "Plastic (red)" then goToNewMaterial(buttons[i],matName) return end
			if string.find(buttons[i].Name, matName) then
				goToNewMaterial(buttons[i],matName)
				return
			end
		end
	end

	frame.Changed:connect(function ( prop )
		if prop == "AbsoluteSize" then
			recalculateScroll()
		end
	end)

	recalculateScroll()
	return frame, terrainMaterialSelectionChanged, forceTerrainMaterialSelection
end

t.CreateLoadingFrame = function(name,size,position)
	game:GetService("ContentProvider"):Preload("https://www.roblox.com/asset/?id=35238053")

	local loadingFrame = Instance.new("Frame")
	loadingFrame.Name = "LoadingFrame"
	loadingFrame.Style = Enum.FrameStyle.RobloxRound

	if size then loadingFrame.Size = size
	else loadingFrame.Size = UDim2.new(0,300,0,160) end
	if position then loadingFrame.Position = position 
	else loadingFrame.Position = UDim2.new(0.5, -150, 0.5,-80) end

	local loadingBar = Instance.new("Frame")
	loadingBar.Name = "LoadingBar"
	loadingBar.BackgroundColor3 = Color3.new(0,0,0)
	loadingBar.BorderColor3 = Color3.new(79/255,79/255,79/255)
	loadingBar.Position = UDim2.new(0,0,0,41)
	loadingBar.Size = UDim2.new(1,0,0,30)
	loadingBar.Parent = loadingFrame

		local loadingGreenBar = Instance.new("ImageLabel")
		loadingGreenBar.Name = "LoadingGreenBar"
		loadingGreenBar.Image = "https://www.roblox.com/asset/?id=35238053"
		loadingGreenBar.Position = UDim2.new(0,0,0,0)
		loadingGreenBar.Size = UDim2.new(0,0,1,0)
		loadingGreenBar.Visible = false
		loadingGreenBar.Parent = loadingBar

		local loadingPercent = Instance.new("TextLabel")
		loadingPercent.Name = "LoadingPercent"
		loadingPercent.BackgroundTransparency = 1
		loadingPercent.Position = UDim2.new(0,0,1,0)
		loadingPercent.Size = UDim2.new(1,0,0,14)
		loadingPercent.Font = Enum.Font.Arial
		loadingPercent.Text = "0%"
		loadingPercent.FontSize = Enum.FontSize.Size14
		loadingPercent.TextColor3 = Color3.new(1,1,1)
		loadingPercent.Parent = loadingBar

	local cancelButton = Instance.new("TextButton")
	cancelButton.Name = "CancelButton"
	cancelButton.Position = UDim2.new(0.5,-60,1,-40)
	cancelButton.Size = UDim2.new(0,120,0,40)
	cancelButton.Font = Enum.Font.Arial
	cancelButton.FontSize = Enum.FontSize.Size18
	cancelButton.TextColor3 = Color3.new(1,1,1)
	cancelButton.Text = "Cancel"
	cancelButton.Style = Enum.ButtonStyle.RobloxButton
	cancelButton.Parent = loadingFrame

	local loadingName = Instance.new("TextLabel")
	loadingName.Name = "loadingName"
	loadingName.BackgroundTransparency = 1
	loadingName.Size = UDim2.new(1,0,0,18)
	loadingName.Position = UDim2.new(0,0,0,2)
	loadingName.Font = Enum.Font.Arial
	loadingName.Text = name
	loadingName.TextColor3 = Color3.new(1,1,1)
	loadingName.TextStrokeTransparency = 1
	loadingName.FontSize = Enum.FontSize.Size18
	loadingName.Parent = loadingFrame

	local cancelButtonClicked = Instance.new("BindableEvent")
	cancelButtonClicked.Name = "CancelButtonClicked"
	cancelButtonClicked.Parent = cancelButton
	cancelButton.MouseButton1Click:connect(function()
		cancelButtonClicked:Fire()
	end)

	local updateLoadingGuiPercent = function(percent, tweenAction, tweenLength)
		if percent and type(percent) ~= "number" then
			error("updateLoadingGuiPercent expects number as argument, got",type(percent),"instead")
		end

		local newSize = nil
		if percent < 0 then
			newSize = UDim2.new(0,0,1,0)
		elseif percent > 1 then
			newSize = UDim2.new(1,0,1,0)
		else
			newSize = UDim2.new(percent,0,1,0)
		end

		if tweenAction then
			if not tweenLength then
				error("updateLoadingGuiPercent is set to tween new percentage, but got no tween time length! Please pass this in as third argument")
			end

			if (newSize.X.Scale > 0) then
				loadingGreenBar.Visible = true
				loadingGreenBar:TweenSize(	newSize,
											Enum.EasingDirection.Out,
											Enum.EasingStyle.Quad,
											tweenLength,
											true)
			else
				loadingGreenBar:TweenSize(	newSize,
											Enum.EasingDirection.Out,
											Enum.EasingStyle.Quad,
											tweenLength,
											true,
											function() 
												if (newSize.X.Scale < 0) then
													loadingGreenBar.Visible = false
												end
											end)
			end

		else
			loadingGreenBar.Size = newSize
			loadingGreenBar.Visible = (newSize.X.Scale > 0)
		end
	end

	loadingGreenBar.Changed:connect(function(prop)
		if prop == "Size" then
			loadingPercent.Text = tostring( math.ceil(loadingGreenBar.Size.X.Scale * 100) ) .. "%"
		end
	end)

	return loadingFrame, updateLoadingGuiPercent, cancelButtonClicked
end

t.CreatePluginFrame = function (name,size,position,scrollable,parent)
	local function createMenuButton(size,position,text,fontsize,name,parent)
		local button = Instance.new("TextButton",parent)
		button.AutoButtonColor = false
		button.Name = name
		button.BackgroundTransparency = 1
		button.Position = position
		button.Size = size
		button.Font = Enum.Font.ArialBold
		button.FontSize = fontsize
		button.Text =  text
		button.TextColor3 = Color3.new(1,1,1)
		button.BorderSizePixel = 0
		button.BackgroundColor3 = Color3.new(20/255,20/255,20/255)

		button.MouseEnter:connect(function ( )
			if button.Selected then return end
			button.BackgroundTransparency = 0
		end)
		button.MouseLeave:connect(function ( )
			if button.Selected then return end
			button.BackgroundTransparency = 1
		end)

		return button

	end

	local dragBar = Instance.new("Frame",parent)
	dragBar.Name = tostring(name) .. "DragBar"
	dragBar.BackgroundColor3 = Color3.new(39/255,39/255,39/255)
	dragBar.BorderColor3 = Color3.new(0,0,0)
	if size then
		dragBar.Size =  UDim2.new(size.X.Scale,size.X.Offset,0,20)  + UDim2.new(0,20,0,0)
	else
		dragBar.Size = UDim2.new(0,183,0,20)
	end
	if position then
		dragBar.Position = position
	end
	dragBar.Active = true
	dragBar.Draggable = true
	--dragBar.Visible = false
	dragBar.MouseEnter:connect(function (  )
		dragBar.BackgroundColor3 = Color3.new(49/255,49/255,49/255)
	end)
	dragBar.MouseLeave:connect(function (  )
		dragBar.BackgroundColor3 = Color3.new(39/255,39/255,39/255)
	end)

	-- plugin name label
	local pluginNameLabel = Instance.new("TextLabel",dragBar)
	pluginNameLabel.Name = "BarNameLabel"
	pluginNameLabel.Text = " " .. tostring(name)
	pluginNameLabel.TextColor3 = Color3.new(1,1,1)
	pluginNameLabel.TextStrokeTransparency = 0
	pluginNameLabel.Size = UDim2.new(1,0,1,0)
	pluginNameLabel.Font = Enum.Font.ArialBold
	pluginNameLabel.FontSize = Enum.FontSize.Size18
	pluginNameLabel.TextXAlignment = Enum.TextXAlignment.Left
	pluginNameLabel.BackgroundTransparency = 1

	-- close button
	local closeButton = createMenuButton(UDim2.new(0,15,0,17),UDim2.new(1,-16,0.5,-8),"X",Enum.FontSize.Size14,"CloseButton",dragBar)
	local closeEvent = Instance.new("BindableEvent")
	closeEvent.Name = "CloseEvent"
	closeEvent.Parent = closeButton
	closeButton.MouseButton1Click:connect(function ()
		closeEvent:Fire()
		closeButton.BackgroundTransparency = 1
	end)

	-- help button
	local helpButton = createMenuButton(UDim2.new(0,15,0,17),UDim2.new(1,-51,0.5,-8),"?",Enum.FontSize.Size14,"HelpButton",dragBar)
	local helpFrame = Instance.new("Frame",dragBar)
	helpFrame.Name = "HelpFrame"
	helpFrame.BackgroundColor3 = Color3.new(0,0,0)
	helpFrame.Size = UDim2.new(0,300,0,552)
	helpFrame.Position = UDim2.new(1,5,0,0)
	helpFrame.Active = true
	helpFrame.BorderSizePixel = 0
	helpFrame.Visible = false

	helpButton.MouseButton1Click:connect(function(  )
		helpFrame.Visible = not helpFrame.Visible
		if helpFrame.Visible then
			helpButton.Selected = true
			helpButton.BackgroundTransparency = 0
			local screenGui = getLayerCollectorAncestor(helpFrame)
			if screenGui then
				if helpFrame.AbsolutePosition.X + helpFrame.AbsoluteSize.X > screenGui.AbsoluteSize.X then --position on left hand side
					helpFrame.Position = UDim2.new(0,-5 - helpFrame.AbsoluteSize.X,0,0)
				else -- position on right hand side
					helpFrame.Position = UDim2.new(1,5,0,0)
				end
			else
				helpFrame.Position = UDim2.new(1,5,0,0)
			end
		else
			helpButton.Selected = false
			helpButton.BackgroundTransparency = 1
		end
	end)

	local minimizeButton = createMenuButton(UDim2.new(0,16,0,17),UDim2.new(1,-34,0.5,-8),"-",Enum.FontSize.Size14,"MinimizeButton",dragBar)
	minimizeButton.TextYAlignment = Enum.TextYAlignment.Top

	local minimizeFrame = Instance.new("Frame",dragBar)
	minimizeFrame.Name = "MinimizeFrame"
	minimizeFrame.BackgroundColor3 = Color3.new(73/255,73/255,73/255)
	minimizeFrame.BorderColor3 = Color3.new(0,0,0)
	minimizeFrame.Position = UDim2.new(0,0,1,0)
	if size then
		minimizeFrame.Size =  UDim2.new(size.X.Scale,size.X.Offset,0,50) + UDim2.new(0,20,0,0)
	else
		minimizeFrame.Size = UDim2.new(0,183,0,50)
	end
	minimizeFrame.Visible = false

	local minimizeBigButton = Instance.new("TextButton",minimizeFrame)
	minimizeBigButton.Position = UDim2.new(0.5,-50,0.5,-20)
	minimizeBigButton.Name = "MinimizeButton"
	minimizeBigButton.Size = UDim2.new(0,100,0,40)
	minimizeBigButton.Style = Enum.ButtonStyle.RobloxButton
	minimizeBigButton.Font = Enum.Font.ArialBold
	minimizeBigButton.FontSize = Enum.FontSize.Size18
	minimizeBigButton.TextColor3 = Color3.new(1,1,1)
	minimizeBigButton.Text = "Show"

	local separatingLine = Instance.new("Frame",dragBar)
	separatingLine.Name = "SeparatingLine"
	separatingLine.BackgroundColor3 = Color3.new(115/255,115/255,115/255)
	separatingLine.BorderSizePixel = 0
	separatingLine.Position = UDim2.new(1,-18,0.5,-7)
	separatingLine.Size = UDim2.new(0,1,0,14)

	local otherSeparatingLine = separatingLine:clone()
	otherSeparatingLine.Position = UDim2.new(1,-35,0.5,-7)
	otherSeparatingLine.Parent = dragBar

	local widgetContainer = Instance.new("Frame",dragBar)
	widgetContainer.Name = "WidgetContainer"
	widgetContainer.BackgroundTransparency = 1
	widgetContainer.Position = UDim2.new(0,0,1,0)
	widgetContainer.BorderColor3 = Color3.new(0,0,0)
	if not scrollable then
		widgetContainer.BackgroundTransparency = 0
		widgetContainer.BackgroundColor3 = Color3.new(72/255,72/255,72/255)
	end

	if size then
		if scrollable then
			widgetContainer.Size = size
		else
			widgetContainer.Size = UDim2.new(0,dragBar.AbsoluteSize.X,size.Y.Scale,size.Y.Offset)
		end
	else
		if scrollable then
			widgetContainer.Size = UDim2.new(0,163,0,400)
		else
			widgetContainer.Size = UDim2.new(0,dragBar.AbsoluteSize.X,0,400)
		end
	end
	if position then
		widgetContainer.Position = position + UDim2.new(0,0,0,20)
	end

	local frame,control,verticalDragger = nil
	if scrollable then
		--frame for widgets
		frame,control = t.CreateTrueScrollingFrame()
		frame.Size = UDim2.new(1, 0, 1, 0)
		frame.BackgroundColor3 = Color3.new(72/255,72/255,72/255)
		frame.BorderColor3 = Color3.new(0,0,0)
		frame.Active = true
		frame.Parent = widgetContainer
		control.Parent = dragBar
		control.BackgroundColor3 = Color3.new(72/255,72/255,72/255)
		control.BorderSizePixel = 0
		control.BackgroundTransparency = 0
		control.Position = UDim2.new(1,-21,1,1)
		if size then
			control.Size = UDim2.new(0,21,size.Y.Scale,size.Y.Offset)
		else
			control.Size = UDim2.new(0,21,0,400)
		end
		control:FindFirstChild("ScrollDownButton").Position = UDim2.new(0,0,1,-20)

		local fakeLine = Instance.new("Frame",control)
		fakeLine.Name = "FakeLine"
		fakeLine.BorderSizePixel = 0
		fakeLine.BackgroundColor3 = Color3.new(0,0,0)
		fakeLine.Size = UDim2.new(0,1,1,1)
		fakeLine.Position = UDim2.new(1,0,0,0)

		verticalDragger = Instance.new("TextButton",widgetContainer)
		verticalDragger.ZIndex = 2
		verticalDragger.AutoButtonColor = false
		verticalDragger.Name = "VerticalDragger"
		verticalDragger.BackgroundColor3 = Color3.new(50/255,50/255,50/255)
		verticalDragger.BorderColor3 = Color3.new(0,0,0)
		verticalDragger.Size = UDim2.new(1,20,0,20)
		verticalDragger.Position = UDim2.new(0,0,1,0)
		verticalDragger.Active = true
		verticalDragger.Text = ""

		local scrubFrame = Instance.new("Frame",verticalDragger)
		scrubFrame.Name = "ScrubFrame"
		scrubFrame.BackgroundColor3 = Color3.new(1,1,1)
		scrubFrame.BorderSizePixel = 0
		scrubFrame.Position = UDim2.new(0.5,-5,0.5,0)
		scrubFrame.Size = UDim2.new(0,10,0,1)
		scrubFrame.ZIndex = 5
		local scrubTwo = scrubFrame:clone()
		scrubTwo.Position = UDim2.new(0.5,-5,0.5,-2)
		scrubTwo.Parent = verticalDragger
		local scrubThree = scrubFrame:clone()
		scrubThree.Position = UDim2.new(0.5,-5,0.5,2)
		scrubThree.Parent = verticalDragger

		local areaSoak = Instance.new("TextButton",getLayerCollectorAncestor(parent))
		areaSoak.Name = "AreaSoak"
		areaSoak.Size = UDim2.new(1,0,1,0)
		areaSoak.BackgroundTransparency = 1
		areaSoak.BorderSizePixel = 0
		areaSoak.Text = ""
		areaSoak.ZIndex = 10
		areaSoak.Visible = false
		areaSoak.Active = true

		local draggingVertical = false
		local startYPos = nil
		verticalDragger.MouseEnter:connect(function ()
			verticalDragger.BackgroundColor3 = Color3.new(60/255,60/255,60/255)
		end)
		verticalDragger.MouseLeave:connect(function ()
			verticalDragger.BackgroundColor3 = Color3.new(50/255,50/255,50/255)
		end)
		verticalDragger.MouseButton1Down:connect(function(x,y)
			draggingVertical = true
			areaSoak.Visible = true
			startYPos = y
		end)
		areaSoak.MouseButton1Up:connect(function (  )
			draggingVertical = false
			areaSoak.Visible = false
		end)
		areaSoak.MouseMoved:connect(function(x,y)
			if not draggingVertical then return end

			local yDelta = y - startYPos
			if not control.ScrollDownButton.Visible and yDelta > 0 then
				return
			end

			if (widgetContainer.Size.Y.Offset + yDelta) < 150 then
				widgetContainer.Size = UDim2.new(widgetContainer.Size.X.Scale, widgetContainer.Size.X.Offset,widgetContainer.Size.Y.Scale,150)
				control.Size = UDim2.new (0,21,0,150)
				return 
			end 

			startYPos = y

			if widgetContainer.Size.Y.Offset + yDelta >= 0 then
				widgetContainer.Size = UDim2.new(widgetContainer.Size.X.Scale, widgetContainer.Size.X.Offset,widgetContainer.Size.Y.Scale,widgetContainer.Size.Y.Offset + yDelta)
				control.Size = UDim2.new(0,21,0,control.Size.Y.Offset + yDelta )
			end
		end)
	end

	local function switchMinimize()
		minimizeFrame.Visible = not minimizeFrame.Visible
		if scrollable then
			frame.Visible = not frame.Visible
			verticalDragger.Visible = not verticalDragger.Visible
			control.Visible = not control.Visible
		else
			widgetContainer.Visible = not widgetContainer.Visible
		end

		if minimizeFrame.Visible then
			minimizeButton.Text = "+"
		else
			minimizeButton.Text = "-"
		end
	end

	minimizeBigButton.MouseButton1Click:connect(function (  )
		switchMinimize()
	end)

	minimizeButton.MouseButton1Click:connect(function(  )
		switchMinimize()
	end)

	if scrollable then
		return dragBar, frame, helpFrame, closeEvent
	else
		return dragBar, widgetContainer, helpFrame, closeEvent
	end
end

t.Help = 
	function(funcNameOrFunc) 
		--input argument can be a string or a function.  Should return a description (of arguments and expected side effects)
		if funcNameOrFunc == "CreatePropertyDropDownMenu" or funcNameOrFunc == t.CreatePropertyDropDownMenu then
			return "Function CreatePropertyDropDownMenu.  " ..
				   "Arguments: (instance, propertyName, enumType).  " .. 
				   "Side effect: returns a container with a drop-down-box that is linked to the 'property' field of 'instance' which is of type 'enumType'" 
		end 
		if funcNameOrFunc == "CreateDropDownMenu" or funcNameOrFunc == t.CreateDropDownMenu then
			return "Function CreateDropDownMenu.  " .. 
				   "Arguments: (items, onItemSelected).  " .. 
				   "Side effect: Returns 2 results, a container to the gui object and a 'updateSelection' function for external updating.  The container is a drop-down-box created around a list of items" 
		end 
		if funcNameOrFunc == "CreateMessageDialog" or funcNameOrFunc == t.CreateMessageDialog then
			return "Function CreateMessageDialog.  " .. 
				   "Arguments: (title, message, buttons). " .. 
				   "Side effect: Returns a gui object of a message box with 'title' and 'message' as passed in.  'buttons' input is an array of Tables contains a 'Text' and 'Function' field for the text/callback of each button"
		end		
		if funcNameOrFunc == "CreateStyledMessageDialog" or funcNameOrFunc == t.CreateStyledMessageDialog then
			return "Function CreateStyledMessageDialog.  " .. 
				   "Arguments: (title, message, style, buttons). " .. 
				   "Side effect: Returns a gui object of a message box with 'title' and 'message' as passed in.  'buttons' input is an array of Tables contains a 'Text' and 'Function' field for the text/callback of each button, 'style' is a string, either Error, Notify or Confirm"
		end
		if funcNameOrFunc == "GetFontHeight" or funcNameOrFunc == t.GetFontHeight then
			return "Function GetFontHeight.  " .. 
				   "Arguments: (font, fontSize). " .. 
				   "Side effect: returns the size in pixels of the given font + fontSize"
		end
		if funcNameOrFunc == "LayoutGuiObjects" or funcNameOrFunc == t.LayoutGuiObjects then
		
		end
		if funcNameOrFunc == "CreateScrollingFrame" or funcNameOrFunc == t.CreateScrollingFrame then
			return "Function CreateScrollingFrame.  " .. 
			   "Arguments: (orderList, style) " .. 
			   "Side effect: returns 4 objects, (scrollFrame, scrollUpButton, scrollDownButton, recalculateFunction).  'scrollFrame' can be filled with GuiObjects.  It will lay them out and allow scrollUpButton/scrollDownButton to interact with them.  Orderlist is optional (and specifies the order to layout the children.  Without orderlist, it uses the children order. style is also optional, and allows for a 'grid' styling if style is passed 'grid' as a string.  recalculateFunction can be called when a relayout is needed (when orderList changes)"
		end
		if funcNameOrFunc == "CreateTrueScrollingFrame" or funcNameOrFunc == t.CreateTrueScrollingFrame then
			return "Function CreateTrueScrollingFrame.  " .. 
			   "Arguments: (nil) " .. 
			   "Side effect: returns 2 objects, (scrollFrame, controlFrame).  'scrollFrame' can be filled with GuiObjects, and they will be clipped if not inside the frame's bounds. controlFrame has children scrollup and scrolldown, as well as a slider.  controlFrame can be parented to any guiobject and it will readjust itself to fit."
		end
		if funcNameOrFunc == "AutoTruncateTextObject" or funcNameOrFunc == t.AutoTruncateTextObject then
			return "Function AutoTruncateTextObject.  " .. 
			   "Arguments: (textLabel) " .. 
			   "Side effect: returns 2 objects, (textLabel, changeText).  The 'textLabel' input is modified to automatically truncate text (with ellipsis), if it gets too small to fit.  'changeText' is a function that can be used to change the text, it takes 1 string as an argument"
		end
		if funcNameOrFunc == "CreateSlider" or funcNameOrFunc == t.CreateSlider then
			return "Function CreateSlider.  " ..
				"Arguments: (steps, width, position) " ..
				"Side effect: returns 2 objects, (sliderGui, sliderPosition).  The 'steps' argument specifies how many different positions the slider can hold along the bar.  'width' specifies in pixels how wide the bar should be (modifiable afterwards if desired). 'position' argument should be a UDim2 for slider positioning. 'sliderPosition' is an IntValue whose current .Value specifies the specific step the slider is currently on."
		end
		if funcNameOrFunc == "CreateSliderNew" or funcNameOrFunc == t.CreateSliderNew then
			return "Function CreateSliderNew.  " ..
				"Arguments: (steps, width, position) " ..
				"Side effect: returns 2 objects, (sliderGui, sliderPosition).  The 'steps' argument specifies how many different positions the slider can hold along the bar.  'width' specifies in pixels how wide the bar should be (modifiable afterwards if desired). 'position' argument should be a UDim2 for slider positioning. 'sliderPosition' is an IntValue whose current .Value specifies the specific step the slider is currently on."
		end
		if funcNameOrFunc == "CreateLoadingFrame" or funcNameOrFunc == t.CreateLoadingFrame then
			return "Function CreateLoadingFrame.  " ..
				"Arguments: (name, size, position) " ..
				"Side effect: Creates a gui that can be manipulated to show progress for a particular action.  Name appears above the loading bar, and size and position are udim2 values (both size and position are optional arguments).  Returns 3 arguments, the first being the gui created. The second being updateLoadingGuiPercent, which is a bindable function.  This function takes one argument (two optionally), which should be a number between 0 and 1, representing the percentage the loading gui should be at.  The second argument to this function is a boolean value that if set to true will tween the current percentage value to the new percentage value, therefore our third argument is how long this tween should take. Our third returned argument is a BindableEvent, that when fired means that someone clicked the cancel button on the dialog."
		end
		if funcNameOrFunc == "CreateTerrainMaterialSelector" or funcNameOrFunc == t.CreateTerrainMaterialSelector then
			return "Function CreateTerrainMaterialSelector.  " ..
				"Arguments: (size, position) " ..
				"Side effect: Size and position are UDim2 values that specifies the selector's size and position.  Both size and position are optional arguments. This method returns 3 objects (terrainSelectorGui, terrainSelected, forceTerrainSelection).  terrainSelectorGui is just the gui object that we generate with this function, parent it as you like. TerrainSelected is a BindableEvent that is fired whenever a new terrain type is selected in the gui.  ForceTerrainSelection is a function that takes an argument of Enum.CellMaterial and will force the gui to show that material as currently selected."
		end
	end
	
return t
]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX6f766006b44c4cc19e098e558aff0f22">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">GuiUtilities</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[local module = {}


module.kTitleBarHeight = 27
module.kInlineTitleBarHeight = 24

module.kStandardContentAreaWidth = 180

module.kStandardPropertyHeight = 30
module.kSubSectionLabelHeight = 30

module.kStandardVMargin = 7
module.kStandardHMargin = 16

module.kCheckboxMinLabelWidth = 52
module.kCheckboxMinMargin = 12
module.kCheckboxWidth = 12

module.kRadioButtonsHPadding = 24

module.StandardLineLabelLeftMargin = module.kTitleBarHeight
module.StandardLineElementLeftMargin = (module.StandardLineLabelLeftMargin + module.kCheckboxMinLabelWidth
+ module.kCheckboxMinMargin + module.kCheckboxWidth + module.kRadioButtonsHPadding)
module.StandardLineLabelWidth = (module.StandardLineElementLeftMargin - module.StandardLineLabelLeftMargin - 10 )

module.kDropDownHeight = 55

module.kBottomButtonsFrameHeight = 50
module.kBottomButtonsHeight = 28

module.kShapeButtonSize = 32
module.kTextVerticalFudge = -3
module.kButtonVerticalFudge = -5

module.kStandardWhite = Color3.new(1, 1, 1)

module.kBottomButtonsWidth = 100

module.kStandardTextColor = Color3.new(0, 0, 0)                      --todo: input spec text color
module.kDisabledTextColor = Color3.new(.4, .4, .4)                   --todo: input spec disabled text color
module.kStandardButtonTextColor = Color3.new(0, 0, 0)                --todo: input spec disabled text color
module.kPressedButtonTextColor = Color3.new(1, 1, 1)                 --todo: input spec disabled text color

module.kButtonStandardBackgroundColor = Color3.new(1, 1, 1)          --todo: sync with spec
module.kButtonStandardBorderColor = Color3.new(.4,.4,.4)             --todo: sync with spec
module.kButtonDisabledBackgroundColor = Color3.new(.7,.7,.7)         --todo: sync with spec
module.kButtonDisabledBorderColor = Color3.new(.6,.6,.6)             --todo: sync with spec

module.kButtonHoverColor = Color3.fromRGB(228, 238, 254)
module.kButtonBorderHoverColor = Color3.fromRGB(219, 219, 219)

module.kButtonSelectedColor = Color3.fromRGB(219, 219, 219)
module.kButtonBorderSelectedColor = Color3.fromRGB(186, 186, 186)

module.kButtonBackgroundTransparency = 0.5
module.kButtonBackgroundIntenseTransparency = 0.4

module.kWidgetBorderColor = Color3.fromRGB(182, 182, 182)
module.kStripeBorderColor = Color3.fromRGB(238, 238, 238)

local kDeselectedButtonColor = Color3.new(1, 1, 1)
local kSelectedImageWithTextButtonColor = Color3.new(0.85, 0.85, 0.85)
local kRowStripeColor = Color3.new(0.95, 0.95, 0.95)

local kRowStripeColors = {}
kRowStripeColors[0] = kRowStripeColor
kRowStripeColors[1] = module.kStandardWhite


module.kMainFrame = nil


function module.SetMainFrame(frame)
	module.kMainFrame = frame
end


-- A frame with standard styling.
function module.MakeFrame(name)
	local frame = Instance.new("Frame")
	frame.Name = name
	frame.BackgroundTransparency = 0
	frame.BackgroundColor3 = Color3.new(1, 1, 1)
	frame.BorderSizePixel = 0

	return frame
end
	

-- A frame that is a whole line, containing some arbitrary sized widget.
function module.MakeFixedHeightFrame(name, height)
	local frame = module.MakeFrame(name)
	frame.Size = UDim2.new(1, 0, 0, height)

	return frame
end

-- A frame that is one standard-sized line, containing some standard-sized widget (label, edit box, dropdown, 
-- checkbox)
function module.MakeStandardFixedHeightFrame(name)
	return module.MakeFixedHeightFrame(name, module.kStandardPropertyHeight)
end

function module.AdjustHeightDynamicallyToLayout(frame, uiLayout, optPadding)
	if (not optPadding) then 
		optPadding = 0
	end

	local function updateSizes()
		frame.Size = UDim2.new(1, 0, 0, uiLayout.AbsoluteContentSize.Y + optPadding)
	end
	uiLayout:GetPropertyChangedSignal("AbsoluteContentSize"):connect(updateSizes)
	updateSizes()
end

-- Assumes input frame has a List layout with sort order layout order.
-- Add frames in order as siblings of list layout, they will be laid out in order.
-- Color frame background accordingly.
function module.AddStripedChildrenToListFrame(listFrame, frames)
	for index, frame in ipairs(frames) do 
		frame.Parent = listFrame
		frame.LayoutOrder = index
		frame.BackgroundColor3 = kRowStripeColors[(index + 1) % 2]
		frame.BackgroundTransparency = 0
		frame.BorderSizePixel = 1
		frame.BorderColor3 = module.kStripeBorderColor 
	end
end

local function MakeSectionInternal(parentGui, name, title, contentHeight)
	local frame = Instance.new("Frame")
	frame.Name = name
	frame.BackgroundTransparency = 1
	frame.Parent = parentGui
	frame.BackgroundTransparency = 1
	frame.BorderSizePixel = 0
	
	-- If title is "nil', no title bar.
	local contentYOffset = 0
	local titleBar = nil
	if (title ~= nil) then  
		local titleBarFrame = Instance.new("Frame")
		titleBarFrame.Name = "TitleBarFrame"
		titleBarFrame.Parent = frame
		titleBarFrame.Position = UDim2.new(0, 0, 0, 0)
		titleBarFrame.LayoutOrder = 0

		local titleBar = Instance.new("TextLabel")
		titleBar.Name = "TitleBarLabel"
		titleBar.Text = title
		titleBar.Parent = titleBarFrame
		titleBar.BackgroundTransparency = 1
		titleBar.Position = UDim2.new(0, module.kStandardHMargin, 0, 0)

		contentYOffset = contentYOffset + module.kTitleBarHeight
	end

	frame.Size = UDim2.new(1, 0, 0, contentYOffset + contentHeight)

	return frame
end

function module.MakeStandardPropertyLabel(text)
	local label = Instance.new('TextLabel')
	label.Name = 'Label'
	label.BackgroundTransparency = 1
	label.Font = Enum.Font.SourceSans                    --todo: input spec font
	label.TextSize = 15                                  --todo: input spec font size
	label.TextXAlignment = Enum.TextXAlignment.Left
	label.Text = text
	label.AnchorPoint = Vector2.new(0, 0.5)
	label.Position = UDim2.new(0, module.StandardLineLabelLeftMargin, 0.5, module.kTextVerticalFudge)
	label.Size = UDim2.new(0, module.StandardLineLabelWidth, 1, 0)
	return label
end

function module.MakeFrameWithSubSectionLabel(name, text)
	local row = module.MakeFixedHeightFrame(name, module.kSubSectionLabelHeight)
	row.BackgroundTransparency = 1
		
	local label = module.MakeStandardPropertyLabel(text)
	label.BackgroundTransparency = 1
	label.Parent = row

	return row
end

function module.MakeFrameAutoScalingList(frame)
	local uiListLayout = Instance.new("UIListLayout")
	uiListLayout.Parent = frame
	uiListLayout.SortOrder = Enum.SortOrder.LayoutOrder

	module.AdjustHeightDynamicallyToLayout(frame, uiListLayout)
end


return module]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBXddd9c990b9d94234a7fd24562e9b4d5e">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">StatefulImageButton</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- StatefulImageButton.lua
--
-- Image button.
-- Has custom image for when "selected"
-- Uses shading to indicate hover and click states.
--
----------------------------------------
GuiUtilities = require(script.Parent.GuiUtilities)

StatefulImageButtonClass = {}
StatefulImageButtonClass.__index = StatefulImageButtonClass

function StatefulImageButtonClass.new(buttonName, imageAsset, buttonSize)
	local self = {}
	setmetatable(self, StatefulImageButtonClass)

	local button = Instance.new("ImageButton")
	button.Parent = parent
	button.Image = imageAsset
	button.BackgroundTransparency = 1
	button.BorderSizePixel = 0
	button.Size = buttonSize
	button.Name = buttonName

	self._button = button

	self._hovered = false
	self._clicked = false
	self._selected = false

	button.InputBegan:connect(function(input)
		if (input.UserInputType == Enum.UserInputType.MouseMovement) then               
			self._hovered = true
			self:_updateButtonVisual()
		end
	end)


	button.InputEnded:connect(function(input)
		if (input.UserInputType == Enum.UserInputType.MouseMovement) then               
			self._hovered = false
			self._clicked = false
			self:_updateButtonVisual()
		end
	end)    

	button.MouseButton1Down:connect(function()
		self._clicked = true
		self:_updateButtonVisual()
	end)

	button.MouseButton1Up:connect(function()
		self._clicked = false
		self:_updateButtonVisual()
	end)
	
	self:_updateButtonVisual()

	return self
end

function StatefulImageButtonClass:_updateButtonVisual()
	if (self._selected) then 
		self._button.ImageTransparency = 0
		self._button.ImageColor3 = Color3.new(1,1,1) 
	else 
		self._button.ImageTransparency = 0.5
		self._button.ImageColor3 = Color3.new(.5,.5,.5)
	end

	if (self._clicked) then 
		self._button.BackgroundTransparency = 0.8
	elseif (self._hovered) then 
		self._button.BackgroundTransparency = 0.9
	else
		self._button.BackgroundTransparency = 1
	end
end

function StatefulImageButtonClass:setSelected(selected)
	self._selected = selected
	self:_updateButtonVisual()
end

function StatefulImageButtonClass:getButton()
	return self._button
end

return StatefulImageButtonClass]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX0368312554e0429396caebc8c50b6418">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">VerticallyScalingListFrame</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- VerticallyScalingListFrame
--
-- Creates a frame that organizes children into a list layout.
-- Will scale dynamically as children grow.
--
----------------------------------------
GuiUtilities = require(script.Parent.GuiUtilities)

VerticallyScalingListFrameClass = {}
VerticallyScalingListFrameClass.__index = VerticallyScalingListFrameClass

local kBottomPadding = 10

function VerticallyScalingListFrameClass.new(nameSuffix)
	local self = {}
	setmetatable(self, VerticallyScalingListFrameClass)

	self._resizeCallback = nil
	
	local frame = Instance.new('Frame')
	frame.Name = 'VSLFrame' .. nameSuffix
	frame.Size = UDim2.new(1, 0, 0, height)
	frame.BackgroundTransparency = 0
	frame.BackgroundColor3 = Color3.new(1, 1, 1)
	frame.BorderSizePixel = 0

	self._frame = frame
	
	local uiListLayout = Instance.new('UIListLayout')
	uiListLayout.SortOrder = Enum.SortOrder.LayoutOrder
	uiListLayout.Parent = frame
	self._uiListLayout = uiListLayout

	local function updateSizes()
		self._frame.Size = UDim2.new(1, 0, 0, uiListLayout.AbsoluteContentSize.Y)
		if (self._resizeCallback) then 
			self._resizeCallback()
		end
	end
	self._uiListLayout:GetPropertyChangedSignal("AbsoluteContentSize"):connect(updateSizes)
	updateSizes()

	self._childCount = 0

	return self
end

function VerticallyScalingListFrameClass:AddBottomPadding()
	local frame = Instance.new("Frame")
	frame.Name = "BottomPadding"
	frame.BackgroundTransparency = 1
	frame.Size = UDim2.new(1, 0, 0, kBottomPadding)
	frame.LayoutOrder = 1000
	frame.Parent = self._frame
end

function VerticallyScalingListFrameClass:GetFrame()
	return self._frame
end

function VerticallyScalingListFrameClass:AddChild(childFrame)
	childFrame.LayoutOrder = self._childCount
	self._childCount = self._childCount + 1
	childFrame.Parent = self._frame
end

function VerticallyScalingListFrameClass:SetCallbackOnResize(callback)
	self._resizeCallback = callback
end

return VerticallyScalingListFrameClass]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX35523072cae44ca5aea00625e8014eb5">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">LabeledMultiChoice</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- LabeledMultiChoice.lua
--
-- Creates a frame containing a label and list of choices, of which exactly one 
-- is always selected.
--
----------------------------------------
GuiUtilities = require(script.Parent.GuiUtilities)
LabeledRadioButton = require(script.Parent.LabeledRadioButton)
LabeledCheckbox = require(script.Parent.LabeledCheckbox)
VerticallyScalingListFrame = require(script.Parent.VerticallyScalingListFrame)

local kRadioButtonsHPadding = GuiUtilities.kRadioButtonsHPadding

LabeledMultiChoiceClass = {}
LabeledMultiChoiceClass.__index = LabeledMultiChoiceClass


-- Note: 
-- "choices" is an array of entries.
-- each entry must have at least 2 fields:
-- "Id" - a unique (in the scope of choices) string id.  Not visible to user.
-- "Text" - user-facing string: the label for the choice.
function LabeledMultiChoiceClass.new(nameSuffix, labelText, choices, initChoiceIndex)
	local self = {}
	setmetatable(self, LabeledMultiChoiceClass)

	self._buttonObjsByIndex = {}

	if (not initChoiceIndex ) then 
		initChoiceIndex = 1
	end
	if (initChoiceIndex > #choices) then 
		initChoiceIndex = #choices
	end


	local vsl = VerticallyScalingListFrame.new("MCC_" .. nameSuffix)
	vsl:AddBottomPadding()

	local titleLabel = GuiUtilities.MakeFrameWithSubSectionLabel("Title", labelText)
	vsl:AddChild(titleLabel)

	-- Container for cells.
	local cellFrame = self:_MakeRadioButtons(choices)
	vsl:AddChild(cellFrame)

	self._vsl = vsl

	self:SetSelectedIndex(initChoiceIndex)

	return self
end

function LabeledMultiChoiceClass:SetSelectedIndex(selectedIndex) 
	self._selectedIndex = selectedIndex
	for i = 1, #self._buttonObjsByIndex do 
		self._buttonObjsByIndex[i]:SetValue(i == selectedIndex)
	end

	if (self._valueChangedFunction) then 
		self._valueChangedFunction(self._selectedIndex)
	end
end

function LabeledMultiChoiceClass:GetSelectedIndex()
	return self._selectedIndex
end

function LabeledMultiChoiceClass:SetValueChangedFunction(vcf)
	self._valueChangedFunction = vcf
end

function LabeledMultiChoiceClass:GetFrame()
	return self._vsl:GetFrame()
end


-- Small checkboxes are a different entity.
-- All the bits are smaller.
-- Fixed width instead of flood-fill.
-- Box comes first, then label.
function LabeledMultiChoiceClass:_MakeRadioButtons(choices)
	local frame = GuiUtilities.MakeFrame("RadioButtons")
	frame.BackgroundTransparency = 1

	local padding = Instance.new("UIPadding")
	padding.PaddingLeft = UDim.new(0, GuiUtilities.StandardLineLabelLeftMargin)
	padding.PaddingRight = UDim.new(0, GuiUtilities.StandardLineLabelLeftMargin)
	padding.Parent = frame
	
	-- Make a grid to put checkboxes in.
	local uiGridLayout = Instance.new("UIGridLayout")
	uiGridLayout.CellSize = LabeledCheckbox.kMinFrameSize
	uiGridLayout.CellPadding = UDim2.new(0, 
		kRadioButtonsHPadding,
		0,
		GuiUtilities.kStandardVMargin)
	uiGridLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left
	uiGridLayout.VerticalAlignment = Enum.VerticalAlignment.Top
	uiGridLayout.Parent = frame
	uiGridLayout.SortOrder = Enum.SortOrder.LayoutOrder

	for i, choiceData in ipairs(choices) do 
		self:_AddRadioButton(frame, i, choiceData)
	end

		-- Sync size with content size.
	GuiUtilities.AdjustHeightDynamicallyToLayout(frame, uiGridLayout)

	return frame
end

function LabeledMultiChoiceClass:_AddRadioButton(parentFrame, index, choiceData)
	local radioButtonObj = LabeledRadioButton.new(choiceData.Id, choiceData.Text)
	self._buttonObjsByIndex[index] = radioButtonObj

	radioButtonObj:SetValueChangedFunction(function(value)
		-- If we notice the button going from off to on, and it disagrees with 
		-- our current notion of selection, update selection.
		if (value and self._selectedIndex ~= index) then 
			self:SetSelectedIndex(index)
		end
	end)
	
	radioButtonObj:GetFrame().LayoutOrder = index
	radioButtonObj:GetFrame().Parent = parentFrame
end


return LabeledMultiChoiceClass]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX9ec097327c534554803c540d3c326839">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">CustomTextButton</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- CustomTextButton.lua
--
-- Creates text button with custom look & feel, hover/click effects.
--
----------------------------------------
GuiUtilities = require(script.Parent.GuiUtilities)

local kButtonImageIdDefault = "rbxasset://textures/TerrainTools/button_default.png"
local kButtonImageIdHovered = "rbxasset://textures/TerrainTools/button_hover.png"
local kButtonImageIdPressed = "rbxasset://textures/TerrainTools/button_pressed.png"

CustomTextButtonClass = {}
CustomTextButtonClass.__index = CustomTextButtonClass

function CustomTextButtonClass.new(buttonName, labelText)
	local self = {}
	setmetatable(self, CustomTextButtonClass)

	local button = Instance.new('ImageButton')
	button.Name = buttonName
	button.Image = kButtonImageIdDefault
	button.BackgroundTransparency = 1
	button.ScaleType = Enum.ScaleType.Slice
	button.SliceCenter = Rect.new(7, 7, 156, 36)
	button.AutoButtonColor = false

	local label = Instance.new('TextLabel')
	label.Text = labelText
	label.BackgroundTransparency = 1
	label.Size = UDim2.new(1, 0, 1, GuiUtilities.kButtonVerticalFudge)
	label.Font = Enum.Font.SourceSans                  
	label.TextSize = 15                           
	label.Parent = button

	self._label = label
	self._button = button

	self._clicked = false
	self._hovered = false

	button.InputBegan:connect(function(input)
		if (input.UserInputType == Enum.UserInputType.MouseMovement) then               
			self._hovered = true
			self:_updateButtonVisual()
		end
	end)


	button.InputEnded:connect(function(input)
		if (input.UserInputType == Enum.UserInputType.MouseMovement) then               
			self._hovered = false
			self._clicked = false
			self:_updateButtonVisual()
		end
	end)    

	button.MouseButton1Down:connect(function()
		self._clicked = true
		self:_updateButtonVisual()
	end)

	button.MouseButton1Up:connect(function()
		self._clicked = false
		self:_updateButtonVisual()
	end)
	
	self:_updateButtonVisual()

	return self
end

function CustomTextButtonClass:_updateButtonVisual()
	if (self._clicked) then 
		self._button.Image = kButtonImageIdPressed
		self._label.TextColor3 = GuiUtilities.kPressedButtonTextColor
	elseif (self._hovered) then 
		self._button.Image = kButtonImageIdHovered
		self._label.TextColor3 = GuiUtilities.kStandardButtonTextColor
	else
		self._button.Image = kButtonImageIdDefault
		self._label.TextColor3 = GuiUtilities.kStandardButtonTextColor
	end
end

function CustomTextButtonClass:getButton()
	return self._button
end

return CustomTextButtonClass]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX0f2e0bf4269843ea913365c6ec259fef">
				<Properties>
					<Content name="LinkedSource"><null></null></Content>
					<string name="Name">LabeledTextInput</string>
					<string name="ScriptGuid"></string>
					<ProtectedString name="Source"><![CDATA[----------------------------------------
--
-- LabeledTextInput.lua
--
-- Creates a frame containing a label and a text input control.
--
----------------------------------------
GuiUtilities = require(script.Parent.GuiUtilities)

local kTextInputWidth = 100
local kTextBoxInternalPadding = 4

LabeledTextInputClass = {}
LabeledTextInputClass.__index = LabeledTextInputClass

function LabeledTextInputClass.new(nameSuffix, labelText, defaultValue)
	local self = {}
	setmetatable(self, LabeledTextInputClass)

	-- Note: we are using "graphemes" instead of characters.
	-- In modern text-manipulation-fu, what with internationalization, 
	-- emojis, etc, it's not enough to count characters, particularly when 
	-- concerned with "how many <things> am I rendering?".
	-- We are using the 
	self._MaxGraphemes = 10
	
	self._valueChangedFunction = nil

	local defaultValue = defaultValue or ""

	local frame = GuiUtilities.MakeStandardFixedHeightFrame('TextInput ' .. nameSuffix)
	self._frame = frame

	local label = GuiUtilities.MakeStandardPropertyLabel(labelText)
	label.Parent = frame
	self._label = label

	self._value = defaultValue

	-- Dumb hack to add padding to text box,
	local textBoxWrapperFrame = Instance.new("Frame")
	textBoxWrapperFrame.Name = "Wrapper"
	textBoxWrapperFrame.BackgroundColor3 = GuiUtilities.kStandardWhite
	textBoxWrapperFrame.Size = UDim2.new(0, kTextInputWidth, 0.6, 0)
	textBoxWrapperFrame.Position = UDim2.new(0, GuiUtilities.StandardLineElementLeftMargin, .5, 0)
	textBoxWrapperFrame.AnchorPoint = Vector2.new(0, .5)
	textBoxWrapperFrame.Parent = frame
	textBoxWrapperFrame.BorderColor3 = GuiUtilities.kWidgetBorderColor 

	local textBox = Instance.new("TextBox")
	textBox.Parent = textBoxWrapperFrame
	textBox.Name = "TextBox"
	textBox.Text = defaultValue
	textBox.Font = Enum.Font.SourceSans
	textBox.TextSize = 15
	textBox.BackgroundTransparency = 1
	textBox.TextXAlignment = Enum.TextXAlignment.Left
	textBox.Size = UDim2.new(1, -kTextBoxInternalPadding, 1, GuiUtilities.kTextVerticalFudge)
	textBox.Position = UDim2.new(0, kTextBoxInternalPadding, 0, 0)
	textBox.ClipsDescendants = true
	
	textBox:GetPropertyChangedSignal("Text"):connect(function()
		-- Never let the text be too long.
		-- Careful here: we want to measure number of graphemes, not characters, 
		-- in the text, and we want to clamp on graphemes as well.
		if (utf8.len(self._textBox.Text) > self._MaxGraphemes) then 
			local count = 0
			for start, stop in utf8.graphemes(self._textBox.Text) do
				count = count + 1
				if (count > self._MaxGraphemes) then 
					-- We have gone one too far.
					-- clamp just before the beginning of this grapheme.
					self._textBox.Text = string.sub(self._textBox.Text, 1, start-1)
					break
				end
			end
			-- Don't continue with rest of function: the resetting of "Text" field
			-- above will trigger re-entry.  We don't need to trigger value
			-- changed function twice.
			return
		end

		self._value = self._textBox.Text
		if (self._valueChangedFunction) then 
			self._valueChangedFunction(self._value)
		end
	end)
	
	self._textBox = textBox

	return self
end

function LabeledTextInputClass:SetValueChangedFunction(vcf)
	self._valueChangedFunction = vcf
end

function LabeledTextInputClass:GetFrame()
	return self._frame
end

function LabeledTextInputClass:GetValue()
	return self._value
end

function LabeledTextInputClass:GetMaxGraphemes()
	return self._MaxGraphemes
end

function LabeledTextInputClass:SetMaxGraphemes(newValue)
	self._MaxGraphemes = newValue
end

function LabeledTextInputClass:SetValue(newValue)
	if self._value ~= newValue then
		self._textBox.Text = newValue
	end
end

return LabeledTextInputClass
]]></ProtectedString>
					<BinaryString name="Tags"></BinaryString>
				</Properties>
			</Item>
		</Item>
	</Item>
</roblox>