-----------------------------
--LOCAL FUNCTION DEFINITIONS-
-----------------------------
local c = game.Workspace.Terrain
local WorldToCellPreferSolid = c.WorldToCellPreferSolid
local WorldToCellPreferEmpty = c.WorldToCellPreferEmpty
local CellCenterToWorld = c.CellCenterToWorld
local GetCell = c.GetCell
local SetCell = c.SetCell
local maxYExtents = c.MaxExtents.Max.Y

local emptyMaterial = Enum.CellMaterial.Empty
local waterMaterial = Enum.CellMaterial.Water

local floodFill = nil

-----------------
--DEFAULT VALUES-
-----------------
local startCell = nil
local loaded = false
local on = false
local processing = false

-- gui values
local screenGui = nil
local dragBar, closeEvent, helpFrame, lastCell = nil
local progressBar, setProgressFunc, cancelEvent = nil
local ConfirmationPopupObject = nil

--- exposed values to user via gui
local currentMaterial = 1


---------------
--PLUGIN SETUP-
---------------
self = PluginManager():CreatePlugin()
local mouse = self:GetMouse()
mouse.Button1Down:connect(function() mouseDown(mouse) end)
mouse.Button1Up:connect(function() mouseUp(mouse) end)

self.Deactivation:connect(function()
	Off()
end)
toolbar = self:CreateToolbar("Terrain")
toolbarbutton = toolbar:CreateButton("", "Flood Fill", "floodFill.png")
toolbarbutton.Click:connect(function()
	if on then
		Off()
	elseif loaded then
		On()
	end
end)

-- load our libraries
local RbxGui = LoadLibrary("RbxGui")
local RbxUtil = LoadLibrary("RbxUtility")
game:GetService("ContentProvider"):Preload("http://www.roblox.com/asset/?id=82741829")


------------------------- OBJECT DEFINITIONS ---------------------

-- helper function for objects
function getClosestColorToTerrainMaterial(terrainValue)
	if terrainValue == 1 then return BrickColor.new("Bright green")
	elseif terrainValue == 2 then return BrickColor.new("Bright yellow")
	elseif terrainValue == 3 then return BrickColor.new("Bright red")
	elseif terrainValue == 4 then return BrickColor.new("Sand red")
	elseif terrainValue == 5 then return BrickColor.new("Black")
	elseif terrainValue == 6 then return BrickColor.new("Dark stone grey")
	elseif terrainValue == 7 then return BrickColor.new("Sand blue")
	elseif terrainValue == 8 then return BrickColor.new("Deep orange")
	elseif terrainValue == 9 then return BrickColor.new("Dark orange")
	elseif terrainValue == 10 then return BrickColor.new("Reddish brown")
	elseif terrainValue == 11 then return BrickColor.new("Light orange")
	elseif terrainValue == 12 then return BrickColor.new("Light stone grey")
	elseif terrainValue == 13 then return BrickColor.new("Sand green")
	elseif terrainValue == 14 then return BrickColor.new("Medium stone grey")
	elseif terrainValue == 15 then return BrickColor.new("Really red")
	elseif terrainValue == 16 then return BrickColor.new("Really blue")
	elseif terrainValue == 17 then return BrickColor.new("Bright blue")
	else return BrickColor.new("Bright green")
	end
end



-- Used to create a highlighter that follows the mouse.
-- It is a class mouse highlighter.  To use, call MouseHighlighter.Create(mouse) where mouse is the mouse to track.
MouseHighlighter = {}
MouseHighlighter.__index = MouseHighlighter

-- Create a mouse movement highlighter.
-- plugin - Plugin to get the mouse from.
function MouseHighlighter.Create(mouseUse)
	local highlighter = {}
	
	local mouse = mouseUse
	highlighter.OnClicked = nil
	highlighter.mouseDown = false
	
	-- Store the last point used to draw.
	highlighter.lastUsedPoint = nil
	
	-- Will hold a part the highlighter will be attached to.  This will be moved where the mouse is.
	highlighter.selectionPart = nil

	-- Hook the mouse up to check for movement.
	mouse.Move:connect(function() MouseMoved() end)	
	
	mouse.Button1Down:connect(function() highlighter.mouseDown = true end)
	mouse.Button1Up:connect(function() highlighter.mouseDown = false
                                      end)
	
	
	-- Create the part that the highlighter will be attached to.
	highlighter.selectionPart = Instance.new("Part")
	highlighter.selectionPart.Name = "SelectionPart"
	highlighter.selectionPart.Archivable = false
	highlighter.selectionPart.Transparency = 0.5
	highlighter.selectionPart.Anchored = true
	highlighter.selectionPart.Locked = true
	highlighter.selectionPart.CanCollide = false
	highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom
	highlighter.selectionPart.Size = Vector3.new(4,4,4)
	highlighter.selectionPart.BottomSurface = 0
	highlighter.selectionPart.TopSurface = 0
	highlighter.selectionPart.BrickColor = getClosestColorToTerrainMaterial(currentMaterial)
	highlighter.selectionPart.Parent = game.Workspace

	local billboardGui = Instance.new("BillboardGui",highlighter.selectionPart)
	billboardGui.Size = UDim2.new(5,0,5,0)
	billboardGui.StudsOffset = Vector3.new(0,2.5,0)

	local imageLabel = Instance.new("ImageLabel",billboardGui)
	imageLabel.BackgroundTransparency = 1
	imageLabel.Size = UDim2.new(1,0,1,0)
	imageLabel.Position = UDim2.new(-0.35,0,-0.5,0)
	imageLabel.Image = "http://www.roblox.com/asset/?id=82741829"

	local lastTweenChange = nil

	function startTween()
		while tweenUp or tweenDown do
			wait(0.2)
		end
		lastTweenChange = tick()

		delay(0,function (  )
			local thisTweenStamp = lastTweenChange
			local tweenDown, tweenUp = nil

			tweenDown = function()
				if imageLabel and imageLabel:IsDescendantOf(game.Workspace) and thisTweenStamp == lastTweenChange then 
					imageLabel:TweenPosition(UDim2.new(-0.35,0,-0.5,0), Enum.EasingDirection.In, Enum.EasingStyle.Quad,0.4,true,tweenUp)
				end
			end
			tweenUp = function()
				if imageLabel and imageLabel:IsDescendantOf(game.Workspace) and thisTweenStamp == lastTweenChange then
					imageLabel:TweenPosition(UDim2.new(-0.35,0,-0.7,0), Enum.EasingDirection.Out, Enum.EasingStyle.Sine,0.4,true,tweenDown)
				end
			end

			tweenUp()
		end)
	end

	function stopTween()
		lastTweenChange = tick()
	end

	mouse.TargetFilter = highlighter.selectionPart
	setmetatable(highlighter, MouseHighlighter)

	-- Function to call when the mouse has moved.  Updates where to display the highlighter.
	function MouseMoved()
		if on and not processing then
			UpdatePosition(mouse.Hit)
		end
	end

	-- Update where the highlighter is displayed.
	-- position - Where to display the highlighter, in world space.
	function UpdatePosition(position)
		if not position then 
			return 
		end
		
		if not mouse.Target then 
			stopTween()
			highlighter.selectionPart.Parent = nil
			return 
		end

		if highlighter.selectionPart.Parent ~= game.Workspace then
			highlighter.selectionPart.Parent = game.Workspace
			startTween()
		end
		
		local vectorPos = Vector3.new(position.x,position.y,position.z)
		local cellPos = WorldToCellPreferEmpty(c, vectorPos)

		local regionToSelect = nil

		local cellMaterial = GetCell(c, cellPos.x,cellPos.y,cellPos.z)
		local y = cellPos.y
		-- only select empty cells
		while cellMaterial ~= emptyMaterial do
			y = y + 1
			cellMaterial = GetCell(c, cellPos.x,y,cellPos.z)
		end
		cellPos = Vector3.new(cellPos.x,y,cellPos.z)

		local lowVec = CellCenterToWorld(c, cellPos.x , cellPos.y - 1, cellPos.z)
		local highVec = CellCenterToWorld(c, cellPos.x, cellPos.y + 1, cellPos.z)
		regionToSelect = Region3.new(lowVec, highVec)

		highlighter.selectionPart.CFrame = regionToSelect.CFrame
		
		if nil ~= highlighter.OnClicked and highlighter.mouseDown then
			if nil == highlighter.lastUsedPoint then 
				highlighter.lastUsedPoint = WorldToCellPreferEmpty(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
			else
				cellPos = WorldToCellPreferEmpty(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
				
				holdChange = cellPos - highlighter.lastUsedPoint
			
				-- Require terrain.
				if 0 == GetCell(c, cellPos.X, cellPos.Y, cellPos.Z).Value then
					return
				else
					highlighter.lastUsedPoint = cellPos
				end

			end
		end		
	end	
	
	return highlighter
end

function MouseHighlighter:SetMaterial(newMaterial)
	self.selectionPart.BrickColor = getClosestColorToTerrainMaterial(newMaterial)
end

function MouseHighlighter:GetPosition()
	local position = self.selectionPart.CFrame.p
	return WorldToCellPreferEmpty(c,position)
end

-- Hide the highlighter.
function MouseHighlighter:DisablePreview()
	stopTween()
	self.selectionPart.Parent = nil
end

-- Show the highlighter.
function MouseHighlighter:EnablePreview()
	self.selectionPart.Parent = game.Workspace
	startTween()
end

-- Create the mouse movement highlighter.
local mouseHighlighter = MouseHighlighter.Create(mouse)
mouseHighlighter:DisablePreview()


-- Used to create a highlighter.
-- A highlighter is a open, rectuangular area displayed in 3D.
ConfirmationPopup = {}
ConfirmationPopup.__index = ConfirmationPopup

-- Create a standard text label.  Use this for all lables in the popup so it is easy to standardize.
-- labelName - What to set the text label name as.
-- pos    	 - Where to position the label.  Should be of type UDim2.
-- size   	 - How large to make the label.	 Should be of type UDim2.
-- text   	 - Text to display.
-- parent 	 - What to set the text parent as.
-- Return:
-- Value is the created label.
function CreateStandardLabel(labelName,
                             pos,
							 size,
							 text,
							 parent)
	local label = Instance.new("TextLabel", parent)
	label.Name = labelName
	label.Position = pos
	label.Size = size
	label.Text = text
	label.TextColor3 = Color3.new(0.95, 0.95, 0.95)
	label.Font = Enum.Font.ArialBold
	label.FontSize = Enum.FontSize.Size14
	label.TextXAlignment = Enum.TextXAlignment.Left
	label.BackgroundTransparency = 1	
	label.Parent = parent	
	
	return label
end

-- Keep common button properties here to make it easer to change them all at once.
-- These are the default properties to use for a button.
buttonTextColor = Color3.new(1, 1, 1);
buttonFont = Enum.Font.ArialBold;
buttonFontSize = Enum.FontSize.Size18;

-- Create a standard dropdown.  Use this for all dropdowns in the popup so it is easy to standardize.
-- name - What to use.
-- pos    	 	- Where to position the button.  Should be of type UDim2.
-- text         - Text to show in the button.
-- funcOnPress  - Function to run when the button is pressed.
-- parent 	 	- What to set the parent as.
-- Return:
-- button 	   - The button gui.																																	
function CreateStandardButton(name,
  							  pos,
							  text,
							  funcOnPress,
							  parent,
							  size)					
	button = Instance.new("TextButton", parent)
	button.Name = name
	button.Position = pos

	button.Size = UDim2.new(0,120,0,40)
	button.Text = text

	if size then
		button.Size = size
	end
	
	button.Style = Enum.ButtonStyle.RobloxButton

	button.TextColor3 = buttonTextColor
	button.Font = buttonFont
	button.FontSize = buttonFontSize

	button.MouseButton1Click:connect(funcOnPress)
	
	return button
end	
					 
-- Create a confirmation popup.
--
-- confirmText      - What to display in the popup.
-- textOffset       - Offset to position text at.
-- confirmFunction  - Function to run on confirmation.
-- declineFunction  - Function to run when declining.
--
-- Return:
-- Value a table with confirmation gui and options.	
function ConfirmationPopup.Create(confirmText,
								  confirmSmallText,
								  confirmButtonText,
								  declineButtonText,
							      confirmFunction,
							      declineFunction)			  
	local popup = {}
	popup.confirmButton = nil			-- Hold the button to confirm a choice.
	popup.declineButton = nil			-- Hold the button to decline a choice.		 			 
	popup.confirmationFrame = nil       -- Hold the conformation frame.
	popup.confirmationText = nil        -- Hold the text label to display the conformation message.
	popup.confirmationHelpText = nil    -- Hold the text label to display the conformation message help.

	
	popup.confirmationFrame = Instance.new("Frame",screenGui)
	popup.confirmationFrame.Name = "ConfirmationFrame"
	popup.confirmationFrame.Size = UDim2.new(0, 280, 0, 160)
	popup.confirmationFrame.Position = UDim2.new(.5, -popup.confirmationFrame.Size.X.Offset/2, 0.5, -popup.confirmationFrame.Size.Y.Offset/2)
	popup.confirmationFrame.Style = Enum.FrameStyle.RobloxRound
	popup.confirmLabel = CreateStandardLabel("ConfirmLabel", UDim2.new(0,0,0,15), UDim2.new(1, 0, 0, 24), confirmText, popup.confirmationFrame)
	popup.confirmLabel.FontSize = Enum.FontSize.Size18
	popup.confirmLabel.TextXAlignment = Enum.TextXAlignment.Center

	popup.confirmationHelpText = CreateStandardLabel("ConfirmSmallLabel", UDim2.new(0,0,0,40), UDim2.new(1, 0, 0, 28), confirmSmallText, popup.confirmationFrame)
	popup.confirmationHelpText.FontSize = Enum.FontSize.Size14
	popup.confirmationHelpText.TextWrap = true
	popup.confirmationHelpText.Font = Enum.Font.Arial
	popup.confirmationHelpText.TextXAlignment = Enum.TextXAlignment.Center

	-- Confirm
	popup.confirmButton = CreateStandardButton("ConfirmButton",
										UDim2.new(0.5, -120, 1, -50),
									    confirmButtonText,
									    confirmFunction,
									    popup.confirmationFrame)	
										
	-- Decline
	popup.declineButton  = CreateStandardButton("DeclineButton",
										UDim2.new(0.5, 0, 1, -50),
									    declineButtonText,
									    declineFunction,
									    popup.confirmationFrame)										
	
	setmetatable(popup, ConfirmationPopup)
	
	return popup
end	

-- Clear the popup, free up assets.
function ConfirmationPopup:Clear()

	if nil ~= self.confirmButton then
		self.confirmButton.Parent = nil
	end
	
	if nil ~= self.declineButton then
		self.declineButton.Parent = nil	
	end
	
	if nil ~= self.confirmationFrame then
		self.confirmationFrame.Parent = nil
	end
	
	if nil ~= self.confirmLabel then
		self.confirmLabel.Parent = nil    
	end
	
	self.confirmButton = nil
	self.declineButton = nil			 
	self.conformationFrame = nil
	self.conformText = nil      
end

-----------------------
--FUNCTION DEFINITIONS-
-----------------------

local floodFill = function ( x,y,z )
	LoadProgressBar("Processing")
		breadthFill(x,y,z)
	UnloadProgressBar()
end

-- Function used when we try and flood fill.  Prompts the user first.
-- Will not show if disabled or terrain is being processed.
function ConfirmFloodFill(x,y,z)
	-- Only do if something isn't already being processed.
	if not processing then
		processing = true
		if nil == ConfirmationPopupObject then
			ConfirmationPopupObject = ConfirmationPopup.Create("Flood Fill At Selected Location?",
															 "You will not be able to undo. This operation may take some time.",
														     "Fill",
														     "Cancel",
															 function()
															 	 ConfirmationPopupObject:Clear()
															 	 ConfirmationPopupObject = nil
																 floodFill(x,y,z)
																 ConfirmationPopupObject = nil
															 end,
															 function() 
																ConfirmationPopupObject:Clear()
																ConfirmationPopupObject = nil
																processing = false
															 end)
		end
	end
end

function mouseDown(mouse)
	if on and mouse.Target == game.Workspace.Terrain then
		startCell = mouseHighlighter:GetPosition()
	end
end

function mouseUp(mouse)
	if processing then return end

	local upCell = mouseHighlighter:GetPosition()
	if startCell == upCell then
		ConfirmFloodFill(upCell.x,upCell.y,upCell.z)
	end
end

function getMaterial( x,y,z )
	local material = GetCell(c,x,y,z)
	return material
end

function startLoadingFrame(  )
	
end

-- Load the progress bar to display when drawing a river.
-- text - Text to display.
function LoadProgressBar(text)
	processing = true

	-- Start the progress bar.
	progressBar, setProgressFunc, cancelEvent = RbxGui.CreateLoadingFrame(text)
	
	progressBar.Position = UDim2.new(.5, -progressBar.Size.X.Offset/2, 0, 15)
	progressBar.Parent = screenGui

	local loadingPercent = progressBar:FindFirstChild("LoadingPercent",true)
	if loadingPercent then
		loadingPercent.Parent = nil
	end
	local loadingBar = progressBar:FindFirstChild("LoadingBar",true)
	if loadingBar then
		loadingBar.Parent = nil
	end
	local cancelButton = progressBar:FindFirstChild("CancelButton",true)
	if cancelButton then
		cancelButton.Text = "Stop"
	end

	cancelEvent.Event:connect(function(arguments)
		processing = false
		spin = false
	end)

	local spinnerFrame = Instance.new("Frame",progressBar)
	spinnerFrame.Name = "Spinner"
	spinnerFrame.Size = UDim2.new(0, 80, 0, 80)
	spinnerFrame.Position = UDim2.new(0.5, -40, 0.5, -55)
	spinnerFrame.BackgroundTransparency = 1

	local spinnerIcons = {}
	local spinnerNum = 1
	while spinnerNum <= 8 do
		local spinnerImage = Instance.new("ImageLabel")
	   spinnerImage.Name = "Spinner"..spinnerNum
		spinnerImage.Size = UDim2.new(0, 16, 0, 16)
		spinnerImage.Position = UDim2.new(.5+.3*math.cos(math.rad(45*spinnerNum)), -8, .5+.3*math.sin(math.rad(45*spinnerNum)), -8)
		spinnerImage.BackgroundTransparency = 1
	   spinnerImage.Image = "http://www.roblox.com/Asset?id=45880710"
		spinnerImage.Parent = spinnerFrame

	   spinnerIcons[spinnerNum] = spinnerImage
	   spinnerNum = spinnerNum + 1
	end

	--Make it spin
	delay(0, function()
	  local spinPos = 0
		while processing do
			local pos = 0

			while pos < 8 do
				if pos == spinPos or pos == ((spinPos+1)%8) then
					spinnerIcons[pos+1].Image = "http://www.roblox.com/Asset?id=45880668"
				else
					spinnerIcons[pos+1].Image = "http://www.roblox.com/Asset?id=45880710"
				end
				
				pos = pos + 1
			end
			spinPos = (spinPos + 1) % 8
			wait(0.2)
		end
	end)
end

-- Unload the progress bar.
function UnloadProgressBar()
	processing = false
	
	if progressBar then
		progressBar.Parent = nil
		progressBar = nil
	end
	if setProgressFunc then
		setProgressFunc = nil
	end
	if cancelEvent then
		cancelEvent = nil
	end
end

function breadthFill( x,y,z )
	local yDepthChecks = doBreadthFill(x,y,z)
	while yDepthChecks and #yDepthChecks > 0 do
		local newYChecks = {}
		for i = 1, #yDepthChecks do
			local currYDepthChecks = doBreadthFill(yDepthChecks[i].xPos,yDepthChecks[i].yPos,yDepthChecks[i].zPos)

			if not processing then
				return
			end

			if currYDepthChecks and #currYDepthChecks > 0 then
				for i = 1, #currYDepthChecks do
					table.insert(newYChecks,{xPos = currYDepthChecks[i].xPos, yPos = currYDepthChecks[i].yPos, zPos = currYDepthChecks[i].zPos})
				end
			end
		end
		yDepthChecks = newYChecks
	end
end

function doBreadthFill(x,y,z)
	if getMaterial(x,y,z) ~= emptyMaterial then
		return
	end

	local yDepthChecks = {}
	local cellsToCheck = breadthFillHelper(x,y,z,yDepthChecks)
	local count = 0

	while cellsToCheck and #cellsToCheck > 0 do
		local cellCheckQueue = {}
		for i = 1, #cellsToCheck do
			if not processing then
				return
			end

			count = count + 1
			local newCellsToCheck = breadthFillHelper(cellsToCheck[i].xPos,cellsToCheck[i].yPos,cellsToCheck[i].zPos,yDepthChecks)
			if newCellsToCheck and #newCellsToCheck > 0 then
				for i = 1, #newCellsToCheck do
					table.insert(cellCheckQueue,{xPos = newCellsToCheck[i].xPos, yPos = newCellsToCheck[i].yPos, zPos = newCellsToCheck[i].zPos})
				end
			end
			if count >= 3000 then
				wait()
				count = 0
			end
		end
		cellsToCheck = cellCheckQueue
	end

	return yDepthChecks
end

function cellInTerrain( x,y,z )
	if x < c.MaxExtents.Min.X or x >= c.MaxExtents.Max.X then
		return false
	end

	if y < c.MaxExtents.Min.Y or y >= c.MaxExtents.Max.Y then
		return false
	end

	if z < c.MaxExtents.Min.Z or z >= c.MaxExtents.Max.Z then
		return false
	end

	return true
end

function breadthFillHelper(x,y,z,yDepthChecks)
	-- first, lets try and fill this cell
	if cellInTerrain( x,y,z ) and getMaterial( x, y, z) == emptyMaterial then
		SetCell(c,x,y,z,currentMaterial,0,0)
	end

	local cellsToFill = {}
	if cellInTerrain( x + 1,y,z ) and getMaterial( x + 1, y, z) == emptyMaterial then
		table.insert(cellsToFill,{xPos = x + 1, yPos = y, zPos = z})
	end
	if cellInTerrain( x - 1,y,z ) and getMaterial( x - 1, y, z) == emptyMaterial then
		table.insert(cellsToFill,{xPos = x - 1, yPos = y, zPos = z})
	end
	if cellInTerrain( x,y,z + 1) and getMaterial( x, y, z + 1) == emptyMaterial then
		table.insert(cellsToFill,{xPos = x, yPos = y, zPos = z + 1})
	end
	if cellInTerrain( x,y,z - 1) and getMaterial( x, y, z - 1) == emptyMaterial then
		table.insert(cellsToFill,{xPos = x, yPos = y, zPos = z - 1})
	end
	if cellInTerrain( x,y-1,z) and getMaterial( x, y-1, z) == emptyMaterial then
		table.insert(yDepthChecks,{xPos = x, yPos = y - 1, zPos = z})
	end

	for i = 1, #cellsToFill do
		SetCell(c,cellsToFill[i].xPos,cellsToFill[i].yPos,cellsToFill[i].zPos,currentMaterial,0,0)
	end

	if #cellsToFill <= 0 then
		return nil
	end

	return cellsToFill
end

function On()
	if self then
		self:Activate(true)
	end
	if toolbarbutton then
		toolbarbutton:SetActive(true)
	end
	if dragBar then
		dragBar.Visible = true
	end

	on = true
end

function Off()
	if toolbarbutton then
		toolbarbutton:SetActive(false)
	end
	if mouseHighlighter then
		mouseHighlighter:DisablePreview()
	end
	if dragBar then
		dragBar.Visible = false
	end

	on = false
end

mouseHighlighter.OnClicked = mouseUp

------
--GUI-
------
screenGui = Instance.new("ScreenGui", game:GetService("CoreGui"))
screenGui.Name = "FloodFillGui"

dragBar, containerFrame, helpFrame, closeEvent = RbxGui.CreatePluginFrame("Flood Fill",UDim2.new(0,163,0,195),UDim2.new(0,0,0,0),false,screenGui)
dragBar.Visible = false

helpFrame.Size = UDim2.new(0,200,0,190)
local textHelp = Instance.new("TextLabel",helpFrame)
textHelp.Name = "TextHelp"
textHelp.Font = Enum.Font.ArialBold
textHelp.FontSize = Enum.FontSize.Size12
textHelp.TextColor3 = Color3.new(1,1,1)
textHelp.Size = UDim2.new(1,-6,1,-6)
textHelp.Position = UDim2.new(0,3,0,3)
textHelp.TextXAlignment = Enum.TextXAlignment.Left
textHelp.TextYAlignment = Enum.TextYAlignment.Top
textHelp.BackgroundTransparency = 1
textHelp.TextWrap = true
textHelp.Text = 
[[
Quickly replace empty terrain cells with a selected material.  Clicking the mouse will cause any empty terrain cells around the point clicked to be filled with the current material, and will also spread to surrounding empty cells (including any empty cells below, but not above).

Simply click on a different material to fill with that material.  The floating paint bucket and cube indicate where filling will start.
]]

closeEvent.Event:connect(function() Off() end)

local terrainSelectorGui, terrainSelected, forceTerrainMaterialSelection = RbxGui.CreateTerrainMaterialSelector(UDim2.new(1, -10, 0, 184),UDim2.new(0, 5, 0, 5))
terrainSelectorGui.Parent = containerFrame
terrainSelectorGui.BackgroundTransparency = 1
terrainSelectorGui.BorderSizePixel = 0
terrainSelected.Event:connect(function ( newMaterial )
	currentMaterial = newMaterial
	if mouseHighlighter then
		mouseHighlighter:SetMaterial(currentMaterial)
	end
end)
forceTerrainMaterialSelection(Enum.CellMaterial.Water)

--------------------------
--SUCCESSFUL LOAD MESSAGE-
--------------------------
loaded = true