local c = game.Workspace.Terrain
local SetCell = c.SetCell
local SetWaterCell = c.SetWaterCell
local GetCell = c.GetCell
local GetWaterCell = c.GetWaterCell

-----------------
--DEFAULT VALUES-
-----------------
loaded = false
x1 = 200
y1 = 200
x2 = 300
y2 = 300
h = 20
on = false

-- Represent the different ways a river draws.
-- Use to correctly join continuous river sections.
decreaseX = 'DecreaseX'
increaseX = 'IncreaseX'
decreaseZ = 'DecreaseZ'
increaseZ = 'IncreaseZ'
noChange = 'None'

-- Used by water to set the strength.
FORCE_NAMES = {
	"None",
	"Small",
	"Medium",
	"Large",
	"Max",
}

-- Used by water to set the flow direction.
DIRECTION_NAMES = {
	"Negative X",
	"Positive X",
	"Negative Y",
	"Positive Y",
	"Negative Z",
	"Positive Z",
}

-- Find the water force ID number that goes with the given force name.
-- Return:
-- Value is nil if a match wasn't found.
function ForceToID(name)
	for i = 1, #FORCE_NAMES, 1 do
		if FORCE_NAMES[i] == name then
			return i-1
		end
	end

	return nil
end

-- Find the water direction ID number that goes with the given flow direction name.
-- Return:
-- Value is nil if a match wasn't found.
function WaterDirectionToID(name)
	for i = 1, #DIRECTION_NAMES, 1 do
		if DIRECTION_NAMES[i] == name then
			return i-1
		end
	end

	return nil
end

-- Determine which direction water should flow in the x direction.
-- x1 - Start X		-- Initial water position
-- x2 - End X		-- Ending water position.
function DetermineDirX(x1, x2)
	if x1 < x2 then
		return WaterDirectionToID("Positive X")
	elseif x1 > x2 then
		return WaterDirectionToID("Negative X")
	else
		return nil
	end
end

-- Determine which direction water should flow in the z direction.
-- z1 - Start Z		-- Initial water position
-- z2 - End Z		-- Ending water position.
function DetermineDirZ(z1, z2)
	if z1 < z2 then
		return WaterDirectionToID("Positive Z")
	elseif z1 > z2 then
		return WaterDirectionToID("Negative Z")
	else
		return nil
	end
end

-- Stores the last continuous joint type made.
lastDrawJoint = noChange

-- Whether the river should fill what is above it.
-- If true, the rivers uses a set thickness and material, and sets all terrain above it to the specified thickness to be the material.
-- This can be used to dig a tunnel by setting the material to be air.
fillAboveRiver = true

userInputForce = ForceToID('Large')
userInputDirection = 0

-- What material to use to fill in above the river.  This should be air.
tunnelMaterial = 0

-- How much terrain above the river should be removed.
tunnelHeight = 3

maxTunnelHeight = 15

-- Weither the next click will be the start of a river or the end/continuation of a river.  Value is true for the start of a river and false for the end.
startRiver = true

-- Bridge material.  What the actual bridge is made of (not struts).
riverMaterial = 17

-- How thick the river is.  This should always be a odd number (1, 3, 5, etc).
riverThickness = 1

-- Maximum thickness the user is allowed to make the river.  This should always be a odd number.
maxRiverThickness = 15

-- How wide the river is.  This should always be a odd number(1, 3, 5, etc).
riverWidth = 3

-- Maximum width the user is allowed to make the river.  This should always be a odd number.
maxRiverWidth = 45

---------------
--PLUGIN SETUP-
---------------
self = PluginManager():CreatePlugin()
mouse = self:GetMouse()
mouse.Button1Down:connect(function() onClicked(mouse) end)
self.Deactivation:connect(function()
	Off()
end)
toolbar = self:CreateToolbar("Terrain")
toolbarbutton = toolbar:CreateButton("", "Create a river", "riverIcon.png")
toolbarbutton.Click:connect(function()
	if on then
		Off()
	elseif loaded then
		On()
	end
end)

------
--GUI-
------
--screengui
g = Instance.new("ScreenGui", game:GetService("CoreGui"))

-- UI gui load.  Required for sliders.
local RbxGui = LoadLibrary("RbxGui")


-- Size to use for the river options window.
defaultFrameWidth = .2
defaultFrameHeight = .7

--riverOptionsFrame
riverOptionsFrame = Instance.new("Frame", g)
riverOptionsFrame.Position = UDim2.new(0.00, 0, 0.20, 0)
riverOptionsFrame.Size = UDim2.new(defaultFrameWidth, 0, defaultFrameHeight, 0)
riverOptionsFrame.BackgroundTransparency = 0.7
riverOptionsFrame.Visible = false

riverOptionsFrame.BackgroundColor3 = Color3.new(0,0,0)
riverOptionsFrame.BackgroundTransparency = 0.1
riverOptionsFrame.BorderColor3 = Color3.new(0,0,0)
riverOptionsFrame.Active = true -- Prevents mouse click thorugh.

--title
title = Instance.new("TextLabel", riverOptionsFrame)
title.Position = UDim2.new(0.4, 0, 0.04, 0)
title.Size = UDim2.new(0.2, 0, 0.05, 0)
title.Text = "River Builder"
title.BackgroundColor3 = Color3.new(0.4, 0.4, 0.4)
title.TextColor3 = Color3.new(1, 1, 1)
title.Font = Enum.Font.ArialBold
title.FontSize = Enum.FontSize.Size24
title.BorderColor3 = Color3.new(0, 0, 0)
title.BackgroundTransparency = 1


-- Get the text to use for the toggle tunneling button.
-- tunnelOn - True if the ground above the river will be dug out, false if not.
function GetToggleTunnelText(tunnelOn)
	if tunnelOn then
		return 'Tunneling: On'
	else
		return 'Tunneling: Off'
	end
end

-- Toggle tunneling/digging above the river on/off.
function ToggleTunnel()
	fillAboveRiver = not fillAboveRiver
	toggleTunnelButton.Text = GetToggleTunnelText(fillAboveRiver)
end


-- Get the text to display for the current river width.
function GetRiverWidthText()
	return 'River Width: ' .. riverWidth
end

-- Get the text to display for the current river thickness.
function GetRiverThicknessText()
	return 'River Thickness: ' .. riverThickness
end

-- Get the text to display for the current tunnel height.
function GetTunnelHeightText()
	return 'Tunnel height: ' .. tunnelHeight
end

-------------
-- BUTTONS --
-------------
-- 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.
buttonSize = UDim2.new(0.50, 0, 0.06, 0);
buttonBGColor = Color3.new(0.5, 0.5, 0.5);
buttonTextColor = Color3.new(1, 1, 1);
buttonFont = Enum.Font.ArialBold;
buttonFontSize = Enum.FontSize.Size18;
buttonBorderColor = Color3.new(0, 0, 0);
buttonDefaultBGTransparency = 0.5;

-- These are what to use for a button when it is being mouse hovered.
hoverBorderColor = Color3.new(1, 1, 1);
hoverBGColor = Color3.new(0, 0, 0);
hoverBGTransparency = 0.2;

-- Toggle tunneling mode button.
-- Either digs out above the river or doesn't.
toggleTunnelButton = Instance.new("TextButton", riverOptionsFrame)
toggleTunnelButton.Position = UDim2.new(0.15, 0, 0.43, 0)

toggleTunnelButton.Size = buttonSize
toggleTunnelButton.Text = GetToggleTunnelText(fillAboveRiver)
toggleTunnelButton.BackgroundColor3 = buttonBGColor
toggleTunnelButton.TextColor3 = buttonTextColor
toggleTunnelButton.Font = buttonFont
toggleTunnelButton.FontSize = buttonFontSize
toggleTunnelButton.BorderColor3 = buttonBorderColor
toggleTunnelButton.BackgroundTransparency = buttonDefaultBGTransparency

-- Set the button look when the mouse hovers over it.
toggleTunnelButton.MouseEnter:connect(function()
	toggleTunnelButton.BorderColor3 = hoverBorderColor
	toggleTunnelButton.BackgroundColor3 = hoverBGColor
	toggleTunnelButton.BackgroundTransparency = hoverBGTransparency
end)

-- Toggles between the possible river drawing modes.
toggleTunnelButton.MouseButton1Click:connect(function() ToggleTunnel() end)

-- Change button look when the mouse stops hovering over it.
toggleTunnelButton.MouseLeave:connect(function()
	toggleTunnelButton.BorderColor3 = buttonBorderColor
	toggleTunnelButton.BackgroundColor3 = buttonBGColor
	toggleTunnelButton.BackgroundTransparency = buttonDefaultBGTransparency
end)

-- Tunnel height text and slider.
tunnelHeightSliderTitle = Instance.new("TextLabel", riverOptionsFrame)
tunnelHeightSliderTitle.Position = UDim2.new(0.4, 0, 0.58, 0)
tunnelHeightSliderTitle.Size = UDim2.new(0.2, 0, 0.05, 0)
tunnelHeightSliderTitle.Text = GetTunnelHeightText()
tunnelHeightSliderTitle.BackgroundColor3 = Color3.new(0.4, 0.4, 0.4)
tunnelHeightSliderTitle.TextColor3 = Color3.new(1, 1, 1)
tunnelHeightSliderTitle.Font = Enum.Font.ArialBold
tunnelHeightSliderTitle.FontSize = Enum.FontSize.Size24
tunnelHeightSliderTitle.BorderColor3 = Color3.new(0, 0, 0)
tunnelHeightSliderTitle.BackgroundTransparency = 1
tunnelHeightSliderTitle.Visible = true

tunnelHeightSliderGui, tunnelHeightSliderPosition = RbxGui.CreateSlider(math.floor(maxTunnelHeight/2 + 1), riverOptionsFrame.AbsoluteSize.x * 0.95, UDim2.new(0.025, 0, 0.65, 0))
tunnelHeightSliderGui.Parent = riverOptionsFrame
bar = tunnelHeightSliderGui:FindFirstChild("Bar")
bar.Size = UDim2.new(0.95, 0, 0, 5)
tunnelHeightSliderPosition.Changed:connect(function()
	-- Convert slider value to a odd number
	tunnelHeight = ((tunnelHeightSliderPosition.Value - 1) * 2) + 1
	tunnelHeightSliderTitle.Text = GetTunnelHeightText()
end)
tunnelHeightSliderPosition.Value = math.floor(tunnelHeight/2 + 1)

-- river width text and slider.
riverWidthSliderTitle = Instance.new("TextLabel", riverOptionsFrame)
riverWidthSliderTitle.Position = UDim2.new(0.4, 0, 0.68, 0)
riverWidthSliderTitle.Size = UDim2.new(0.2, 0, 0.05, 0)
riverWidthSliderTitle.Text = GetRiverWidthText()
riverWidthSliderTitle.BackgroundColor3 = Color3.new(0.4, 0.4, 0.4)
riverWidthSliderTitle.TextColor3 = Color3.new(1, 1, 1)
riverWidthSliderTitle.Font = Enum.Font.ArialBold
riverWidthSliderTitle.FontSize = Enum.FontSize.Size24
riverWidthSliderTitle.BorderColor3 = Color3.new(0, 0, 0)
riverWidthSliderTitle.BackgroundTransparency = 1
riverWidthSliderTitle.Visible = true

riverWidthSliderGui, riverWidthSliderPosition = RbxGui.CreateSlider(math.floor(maxRiverWidth/2 + 1), riverOptionsFrame.AbsoluteSize.x * 0.95, UDim2.new(0.025, 0, 0.75, 0))
riverWidthSliderGui.Parent = riverOptionsFrame
bar = riverWidthSliderGui:FindFirstChild("Bar")
bar.Size = UDim2.new(0.95, 0, 0, 5)
riverWidthSliderPosition.Changed:connect(function()
	-- Convert slider value to a odd number
	riverWidth = ((riverWidthSliderPosition.Value - 1) * 2) + 1
	riverWidthSliderTitle.Text = GetRiverWidthText()
end)
riverWidthSliderPosition.Value = math.floor(riverWidth/2 + 1)

-- river thickness text and slider.
riverThicknessSliderTitle = Instance.new("TextLabel", riverOptionsFrame)
riverThicknessSliderTitle.Position = UDim2.new(0.4, 0, 0.78, 0)
riverThicknessSliderTitle.Size = UDim2.new(0.2, 0, 0.05, 0)
riverThicknessSliderTitle.Text = GetRiverThicknessText()
riverThicknessSliderTitle.BackgroundColor3 = Color3.new(0.4, 0.4, 0.4)
riverThicknessSliderTitle.TextColor3 = Color3.new(1, 1, 1)
riverThicknessSliderTitle.Font = Enum.Font.ArialBold
riverThicknessSliderTitle.FontSize = Enum.FontSize.Size24
riverThicknessSliderTitle.BorderColor3 = Color3.new(0, 0, 0)
riverThicknessSliderTitle.BackgroundTransparency = 1
riverThicknessSliderTitle.Visible = true

riverThicknessSliderGui, riverThicknessSliderPosition = RbxGui.CreateSlider(math.floor(maxRiverThickness/2 + 1), riverOptionsFrame.AbsoluteSize.x * 0.95, UDim2.new(0.025, 0, 0.85, 0))
riverThicknessSliderGui.Parent = riverOptionsFrame
bar = riverThicknessSliderGui:FindFirstChild("Bar")
bar.Size = UDim2.new(0.95, 0, 0, 5)
riverThicknessSliderPosition.Changed:connect(function()
	-- Convert slider value to a odd number
	riverThickness = ((riverThicknessSliderPosition.Value - 1) * 2) + 1
	riverThicknessSliderTitle.Text = GetRiverThicknessText()
end)
riverThicknessSliderPosition.Value = math.floor(riverThickness/2 + 1)

-- Start new river button.
-- Resets river drawing so that the next click will be the start of a river.
startRiverButton = Instance.new("TextButton", riverOptionsFrame)
startRiverButton.Size = UDim2.new(0.50, 0, 0.06, 0)
startRiverButton.Position = UDim2.new(0.15, 0, 0.90, 0)

startRiverButton.Text = "End current river"
startRiverButton.BackgroundColor3 = buttonBGColor
startRiverButton.TextColor3 = buttonTextColor;
startRiverButton.Font = buttonFont;
startRiverButton.FontSize = buttonFontSize;
startRiverButton.BorderColor3 = buttonBorderColor;
startRiverButton.BackgroundTransparency = buttonDefaultBGTransparency;

-- Set the button look when the mouse hovers over it.
startRiverButton.MouseEnter:connect(function()
	startRiverButton.BorderColor3 = hoverBorderColor;
	startRiverButton.BackgroundColor3 = hoverBGColor;
	startRiverButton.BackgroundTransparency = hoverBGTransparency
end)

-- Stops a currently created river.  Resets to start a new river.
startRiverButton.MouseButton1Click:connect(function() EndCurrentRiver() end)

-- Change button look when the mouse stops hovering over it.
startRiverButton.MouseLeave:connect(function()
	startRiverButton.BorderColor3 = buttonBorderColor;
	startRiverButton.BackgroundColor3 = buttonBGColor;
	startRiverButton.BackgroundTransparency = buttonDefaultBGTransparency;
end)

-- Stops a river or river section currently being created.
-- Sets the next click to be the start of a new river/river section.
function EndCurrentRiver()
	startRiver = true
	lastDrawJoint = noChange
end

function coordCheck(x, z, height)
	c = game.Workspace.Terrain
	for h = height, 0, -1 do
		material, wedge, rotation = c:GetCell(x, h, z)
		if material.Value > 0 then
			return true
		elseif height == 0 then
			return false
		end
	end
end

function dist(x1, y1, x2, y2)
	return math.sqrt(math.pow(x2-x1, 2) + math.pow(y2-y1, 2))
end

function dist3d(x1, y1, z1, x2, y2, z2)
	return math.sqrt(math.pow(dist(x1, y1, x2, y2), 2) + math.pow(z2-z1, 2))
end

-- Each river section can be thought of as two parts.  This will tell which direction each part will go.
-- x1, x2 - Start and end x positions.
-- z1, z2 - Start and end z positions. 
-- Return:
-- Returns two values.  The first is the x direction that will be drawn.
-- The second is the z direction that will be drawn.
function RiverDirections(x1, x2, z1, z2)
	currJointX = 'None'
	currJointZ = 'None'
	
	if x1 > x2 then
		currJointX = decreaseX
	elseif x1 < x2 then
		currJointX = increaseX		
	end
	
	if z1 > z2 then
		currJointZ = decreaseZ
	elseif z1 < z2 then
		currJointZ = increaseZ				
	end
	
	return currJointX, currJointZ
end

--Create a path between coordinates (x1,z1) and (x2,z2) at height h in terrain object.
-- Builds a river from the start to the end point.  river does not move diagonally.
-- Can draw struts for eleveated rivers and hollow out above the path, based on settings.
--if you want path to come from x direction, make it start at the place
--if you want it to come from z direction, make it end at the place
function makePath(x1, z1, x2, z2, h)
	 print('x1 ' .. x1 .. ' x2 ' .. x2 .. ' z1 ' .. z1 .. ' z2 ' .. z2)
		
	-- Needed for drawing rivers, lining them up.
	-- The decimal part isn't needed.
	halfWidth = math.floor(riverWidth/2)
	
	-- Determine what the last draw direction will be. Needed for joining sections.
	-- It is drawing horizontal.
	-- It will need to adjust the start point for continuous if the new start is perpendicular to the old end.
	if lastDrawJoint ~= noChange then
		currJointX, currJointZ = RiverDirections(x1, x2, z1, z2)
		
		-- Adjust the start of the river section.
		-- river section joins need to be adjusted for 90 degree changes.
		-- Depending on the draw directions, adjust the start river draw point.
		if decreaseZ == lastDrawJoint or increaseZ == lastDrawJoint then
			if decreaseX == currJointX then
				x1 = x1 + halfWidth
			elseif increaseX == currJointX then
				x1 = x1 - halfWidth
			end
		end
	end
	
	if noChange ~= currJointZ then
		lastDrawJoint = currJointZ
	elseif noChange ~= currJointZ then
		lastDrawJoint = currJointZ	
	end

	c = game.Workspace.Terrain
	if x2 < x1 then
		incx = -1
		n = 1
	else 
		incx = 1
		n = -1
	end

	-- Determine were water should flow.
	flowDirectionX = DetermineDirX(x1, x2)
	flowDirectionZ = DetermineDirZ(z1, z2)
	
	-- Handler water not flowing in a direction.
	if nil == flowDirectionX and nil ~= flowDirectionZ then
		flowDirectionX = flowDirectionZ
	elseif nil == flowDirectionZ and nil ~= flowDirectionX then
		flowDirectionZ = flowDirectionX
	elseif nil == flowDirectionZ and nil == flowDirectionZ then
		-- Water must flow.
		-- NOTE: I would probably want the water to be still in this case.
		flowDirectionX = 1
		flowDirectionZ = 1
		--]]
	end
		
	
	for x = x1, x2, incx do
		-- This will draw river in the z direction.
		for drawDepth = 0, riverThickness-1, 1 do		
			for drawZ = -halfWidth, halfWidth, 1 do
				SetWaterCell(c, x, h + drawDepth, z1 + drawZ, userInputForce, flowDirectionX)
			end
		end
		
		-- This will fill in above the river in the z direction.
		if fillAboveRiver then
			for fillHeight = tunnelHeight, 1, -1 do		
				for drawZ = -halfWidth, halfWidth, 1 do
					c:SetCell(x, h + fillHeight + riverThickness-1, z1 + drawZ, tunnelMaterial, 0, 0)
				end
			end		
		end
	end
	
	if z2 < z1 then
		incz = -1
		m = 0
		n = 2
	else
		incz = 1
		m = 0
		n = 2
	end
	
	-- Adjust the join point so it isn't jagged.
	if x1 ~= x2 then
		if z1 < z2 then
			z1 = z1 - halfWidth
		elseif z1 > z2 then
			z1 = z1 + halfWidth
		end	
	end
	
	-- Adjust for join.
	if (currJointZ == increaseZ or currJointZ == decreaseZ) and currJointX == noChange then
		z1 = z1 + 1
	end
	
	-- This is for the x direction.
	for z = z1+m, z2, incz do
		-- This will draw in the x direction.
		for drawDepth = 0, riverThickness-1, 1 do		
			for drawX = -halfWidth, halfWidth, 1 do
				SetWaterCell(c, x2 + drawX, h + drawDepth, z, userInputForce, flowDirectionZ)
			end
		end

		-- This will draw air in the x direction.
		if fillAboveRiver then
			for fillHeight = tunnelHeight, 1, -1 do		
				for drawX = -halfWidth, halfWidth, 1 do
					c:SetCell(x2 + drawX, h+ fillHeight + riverThickness - 1, z, tunnelMaterial, 0, 0)
				end
			end
		end
	end
end

-- Function to connect to the mouse button 1 down event.  This is what will run when the user clicks.
-- It will determine what mode the plugin is in and what should be done with the click, how the river should be built.
-- mouse 	- Mouse data.
function onClicked (mouse)
	if on then
		c = game.Workspace.Terrain
		
		local cellPos = c:WorldToCellPreferSolid(Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
	
		-- Require clicking on solid ground.
		if 0 == c:GetCell(cellPos.X, cellPos.Y, cellPos.Z).Value then
			return
		end
		
		local x = cellPos.x
		local y = cellPos.y
		local z = cellPos.z
		
		-- Create the start of a river/river section.
		if startRiver then
			x1 = x
			z1 = z
			h = y
			startRiver = false
		else
			x2 = x
			z2 = z

			 makePath(x1, z1, x2, z2, h)
		
			-- Use the current point as the starting point of the next section.
			x1 = x2;
			z1 = z2;
		end
	end
end

-- Run when the popup is activated.
function On()
	self:Activate(true)
	toolbarbutton:SetActive(true)
	startRiver = true
	on = true
	
	-- Show the popup gui.
	riverOptionsFrame.Visible = true
	for scale = 0, 1, 0.1 do
		riverOptionsFrame.Size = UDim2.new(defaultFrameWidth * scale, 0, defaultFrameHeight * scale, 0)
		wait(0.0000001)
	end
end

-- Run when the popup is deactivated.
function Off()
	toolbarbutton:SetActive(false)
	on = false
	
	-- Hide the popup gui.
	for scale = 1, 0, -0.1 do
		riverOptionsFrame.Size = UDim2.new(defaultFrameWidth * scale, 0, defaultFrameHeight * scale, 0)
		wait(0.0000001)
	end
	riverOptionsFrame.Visible = false	
end

--------------------------
--SUCCESSFUL LOAD MESSAGE-
--------------------------
loaded = true
print("Rivers Plugin Loaded")
