Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local Terrain = workspace:WaitForChild('Terrain', 86400) or workspace:WaitForChild('Terrain')
- while not Terrain.IsSmooth do
- Terrain.Changed:wait()
- end
- local on = false
- local setup = false
- local currentTool = nil
- local utilityModule = require(script.Parent.Utility)(plugin)
- --[[
- How to add a module:
- 1. Add the require to the modules table below.
- 2. Add a plugin button for it into the pluginButtons table, with the same index as used in the modules table.
- How 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 a Deselect function to turn it off.
- Off = This function will be called when your tool is deselected.
- operation = This function will be called when this toolset's brushing functionality is used
- operation(centerPoint, materialsTable, occupanciesTable, resolution, selectionSize, strength, desiredMaterial, brushShape, minBounds, maxBounds)
- ]]
- local modules = {
- ['Smooth'] = require(script.Parent.SmootherModule),
- ['Generate'] = utilityModule:GetUsingAsPlugin() and require(script.Parent.GenerationModule) or {},
- ['Region Editor'] = require(script.Parent.RegionEditorModule),
- }
- local buttonCreator = utilityModule:GetButtonCreator()
- local plugins = {
- {name = 'Generate',
- button = utilityModule:GetUsingAsPlugin() and buttonCreator:CreateButton(
- 'Generate', --button title
- 'Generate landscapes of terrain.', --hover text
- 'http://www.roblox.com/asset/?id=236006872' --button image
- ),
- studioOnly = true,
- },
- {name = 'Add',
- button = buttonCreator:CreateButton(
- 'Add',
- 'Click and hold to add terrain.',
- 'http://www.roblox.com/asset/?id=225328572'
- ),
- usesMaterials = true,
- },
- {name = 'Subtract',
- button = buttonCreator:CreateButton(
- 'Subtract',
- 'Click and hold to remove terrain.',
- 'http://www.roblox.com/asset/?id=225328818'
- ),
- },
- {name = 'Paint',
- button = buttonCreator:CreateButton(
- 'Paint',
- 'Paint the material of the terrain.',
- 'http://www.roblox.com/asset/?id=225328954'
- ),
- usesMaterials = true,
- },
- {name = 'Grow',
- button = buttonCreator:CreateButton(
- 'Grow',
- 'Click and hold to grow and expand terrain.',
- 'http://www.roblox.com/asset/?id=225329153'
- ),
- usesMaterials = true,
- },
- {name = 'Erode',
- button = buttonCreator:CreateButton(
- 'Erode',
- 'Click and hold to erode and remove terrain.',
- 'http://www.roblox.com/asset/?id=225329301'
- ),
- },
- {name = 'Smooth',
- button = buttonCreator:CreateButton(
- 'Smooth',
- 'Brush to smooth out rough or jagged terrain.',
- 'http://www.roblox.com/asset/?id=225329641'
- ),
- },
- {name = 'Region Editor',
- button = buttonCreator:CreateButton(
- 'Regions',
- 'Manipulate regions of smooth terrain.',
- 'http://www.roblox.com/asset/?id=240631063'
- ),
- },
- }
- for i,tool in pairs(plugins) do
- if not tool.studioOnly or (utilityModule:GetUsingAsPlugin() and tool.studioOnly) then
- tool.button.Click:connect(function()
- if not on or (currentTool ~= nil and tool ~= currentTool) then --if off or on but current tool isn't the desired tool, then select this tool.
- if not setup then --I do this so that things only get set up when this plugin is used.
- FirstTimeSetUp()
- end
- Selected(tool)
- else
- Deselected()
- end
- end)
- if not utilityModule:GetUsingAsPlugin() then
- if tool.button.Deselected then
- tool.button.Deselected:connect(function()
- Deselected()
- end)
- end
- end
- end
- end
- function FirstTimeSetUp()
- setup = true
- local changeHistory = utilityModule:GetUsingAsPlugin() and game:GetService('ChangeHistoryService') or nil
- local terrain = game.Workspace.Terrain
- local coreGui = utilityModule:GetCoreGui()
- local gui = script.Parent:WaitForChild('TerrainBrushGui')
- local guiFrame = gui:WaitForChild('Frame')
- local closeButton = guiFrame:WaitForChild('CloseButton')
- local titlelabel = guiFrame:WaitForChild('TitleLabel')
- local checkBox1 = guiFrame:WaitForChild('CheckBox1')
- local checkBox2 = guiFrame:WaitForChild('CheckBox2')
- local checkBox3 = guiFrame:WaitForChild('CheckBox3')
- local checkBox4 = guiFrame:WaitForChild('CheckBox4')
- local toolTip1 = guiFrame:WaitForChild('ToolTip1')
- local toolTip2 = guiFrame:WaitForChild('ToolTip2')
- local label4 = guiFrame:WaitForChild('Label4')
- local divider2 = guiFrame:WaitForChild('Divider2')
- local library = assert(LoadLibrary('RbxGui'))
- local mouse = utilityModule:GetMouse()
- 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 minSelectionSize = 1
- local maxSelectionSize = 16
- local clickThreshold = .1
- local toolTipShowTime = 3.5
- local materialsTable = require(script.Parent.MaterialsList)
- local brushShapes = {
- ['Sphere'] = {
- name = 'Sphere',
- button = guiFrame:WaitForChild('ShapeButton1'),
- image = 'http://www.roblox.com/asset/?id=225799533',
- selectedImage = 'http://www.roblox.com/asset/?id=225801914',
- },
- ['Box'] = {
- name = 'Box',
- button = guiFrame:WaitForChild('ShapeButton2'),
- image = 'http://www.roblox.com/asset/?id=225799696',
- selectedImage = 'http://www.roblox.com/asset/?id=225802254',
- },
- }
- ----------------
- ----SETTINGS---- (Interface Settings)
- local selectionSize = 6
- local strength = .5
- local snapToGrid = false
- local planeLock = false
- local ignoreWater = true
- local brushShape = 'Sphere'
- local materialSelection = materialsTable[1]
- local dynamicMaterial = false
- ----------------
- ----Variables----
- local forcePlaneLock = false
- local forceSnapToGrid = false
- local forceDynamicMaterial = false
- local forceDynamicMaterialTo = true
- local isDynamic = false
- local forceIgnoreWater = false
- local forceIgnoreWaterTo = true
- local isIgnoreWater = true
- 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
- ---------------
- local selectionSizeSlider, selectionSizeValue = library.CreateSlider(maxSelectionSize, 90, UDim2.new(1, -98, 0, 40))
- selectionSizeSlider.Parent = guiFrame
- selectionSizeValue.Changed:connect(function()
- selectionSize = selectionSizeValue.Value
- if selectionPart then
- selectionPart.Size = Vector3.new(1, 1, 1) * selectionSize * resolution + Vector3.new(.1, .1, .1)
- end
- toolTip1.Visible = true
- local currentToolTip1Change = {}
- toolTip1Change = currentToolTip1Change
- wait(toolTipShowTime)
- if toolTip1Change == currentToolTip1Change then
- toolTip1.Visible = false
- end
- end)
- selectionSizeValue.Value = selectionSize
- toolTip1.Visible = false
- local strengthslider, strengthValue = library.CreateSlider(101, 90, UDim2.new(1, -98, 0, 65))
- strengthslider.Parent = guiFrame
- strengthValue.Changed:connect(function()
- strength = (strengthValue.Value - 1) / 100
- if selectionObject then
- selectionObject.SurfaceTransparency = .95 - strength * .3
- end
- toolTip2.Visible = true
- local currentToolTip2Change = {}
- toolTip2Change = currentToolTip2Change
- wait(toolTipShowTime)
- if toolTip2Change == currentToolTip2Change then
- toolTip2.Visible = false
- end
- end)
- strengthValue.Value = strength * 100
- toolTip2.Visible = false
- function setBrushShape(newBrushShape)
- brushShape = newBrushShape
- for _,v in pairs(brushShapes) do
- --v.button.Image = (newBrushShape == v) and v.selectedImage or v.image
- v.button.ImageTransparency = (newBrushShape == v.name) and 0 or .5
- v.button.ImageColor3 = (newBrushShape == v.name) and Color3.new(1,1,1) or Color3.new(.5,.5,.5)
- end
- clearSelection()
- end
- for _,v in pairs(brushShapes) do
- v.button.MouseButton1Down:connect(function()
- setBrushShape(v.name)
- end)
- end
- local MakeToolTip
- do
- local ActiveToolTip = Instance.new("StringValue")
- function MakeToolTip(Gui, Text)
- local Name = tostring(Gui:GetFullName() .. Text)
- local Frame = Instance.new("Frame", Gui)
- Frame.BackgroundTransparency = 0.3
- Frame.BackgroundColor3 = Color3.new(0, 0, 0)
- Frame.Size = UDim2.new(0, 100, 0, 30)
- Frame.Position = UDim2.new(0.8, 0, 0.8, 0)
- Frame.SizeConstraint = "RelativeYY"
- Frame.ZIndex = Gui.ZIndex + 1
- Frame.Style = "DropShadow"
- 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
- Gui.MouseEnter:connect(function()
- 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)
- ActiveToolTip.Changed:connect(function()
- if ActiveToolTip.Value ~= Name then
- Frame.Visible = false
- end
- end)
- Gui.MouseLeave:connect(function()
- Frame.Visible = false
- end)
- end
- end
- local function setMaterialSelection(newMaterialSelection)
- materialSelection = newMaterialSelection
- updateUsabilityLocks()
- for _, v in pairs(guiFrame:GetChildren()) do
- if string.sub(v.Name,1,14) == 'MaterialButton' then
- if v.Name == 'MaterialButton' .. materialSelection.enum.Name then
- v.BackgroundTransparency = .1
- else
- v.BackgroundTransparency = 1
- end
- end
- end
- end
- for i,materialSubTable in pairs(materialsTable) do
- local newMaterialButton = Instance.new('ImageButton')
- newMaterialButton.Name = 'MaterialButton' .. materialSubTable.enum.Name
- newMaterialButton.BorderSizePixel = 2
- newMaterialButton.BorderColor3 = Color3.new(.2, 1, 1)
- newMaterialButton.BackgroundColor3 = Color3.new(.2, 1, 1)
- newMaterialButton.BackgroundTransparency = 1
- newMaterialButton.Image = materialSubTable.image
- newMaterialButton.Size = UDim2.new(0, 35, 0, 35)
- newMaterialButton.Position = UDim2.new(0, 5 + ((i-1) % 4) * 40, 0, 225 + ceil(i/4) * 40)
- newMaterialButton.MouseButton1Down:connect(function()
- setMaterialSelection(materialsTable[i])
- end)
- newMaterialButton.Parent = guiFrame
- MakeToolTip(newMaterialButton, materialSubTable.enum.Name:gsub("([A-Z])", " %1"):gsub("^%s", "")) -- Add spaces to names
- end
- function resizeGuiFrame()
- local materialsDynamic = dynamicMaterial
- if forceDynamicMaterial then
- materialsDynamic = forceDynamicMaterialTo
- end
- local desiredSize = UDim2.new(0, 180, 0, 240)
- if currentTool and currentTool.usesMaterials then
- checkBox3.Visible=true
- label4.Visible=true
- divider2.Visible=true
- desiredSize = desiredSize + UDim2.new(0, 0, 0, 35)
- if not materialsDynamic then
- desiredSize = desiredSize + UDim2.new(0, 0, 0, 5 + ceil(#materialsTable / 4) * 40) --Dynamically resizes frame if we add more materials later.
- end
- else
- checkBox3.Visible=false
- label4.Visible=false
- divider2.Visible=false
- end
- guiFrame.Size = desiredSize
- --guiFrame:TweenSize(desiredSize, 'Out', 'Quad', .5) --illegal in studio
- end
- function updatePlaneLock()
- checkBox1.Style = forcePlaneLock and Enum.ButtonStyle.RobloxRoundButton or Enum.ButtonStyle.RobloxRoundDefaultButton
- checkBox1.Text = (planeLock or forcePlaneLock) and 'X' or ''
- checkBox1.AutoButtonColor = not forcePlaneLock
- if not (planeLock or forcePlaneLock) then
- clearGrid()
- end
- end
- checkBox1.MouseButton1Down:connect(function()
- planeLock = not planeLock
- updatePlaneLock()
- end)
- function updateSnapToGrid()
- checkBox2.Style = forceSnapToGrid and Enum.ButtonStyle.RobloxRoundButton or Enum.ButtonStyle.RobloxRoundDefaultButton
- checkBox2.Text = (snapToGrid or forceSnapToGrid) and 'X' or ''
- checkBox2.AutoButtonColor = not forceSnapToGrid
- end
- checkBox2.MouseButton1Down:connect(function()
- snapToGrid = not snapToGrid
- updateSnapToGrid()
- end)
- function updateDynamicMaterial()
- isDynamic = dynamicMaterial
- if forceDynamicMaterial then
- isDynamic = forceDynamicMaterialTo
- end
- checkBox3.Style = forceDynamicMaterial and Enum.ButtonStyle.RobloxRoundButton or Enum.ButtonStyle.RobloxRoundDefaultButton
- checkBox3.AutoButtonColor = not forceDynamicMaterial
- checkBox3.Text = isDynamic and 'X' or ''
- resizeGuiFrame()
- for _, v in pairs(guiFrame:GetChildren()) do
- if string.sub(v.Name,1,14) == 'MaterialButton' then
- v.Visible = not isDynamic
- end
- end
- end
- checkBox3.MouseButton1Down:connect(function()
- dynamicMaterial = not dynamicMaterial
- updateDynamicMaterial()
- end)
- function updateIgnoreWater()
- isIgnoreWater = ignoreWater
- if forceIgnoreWater then
- isIgnoreWater = forceIgnoreWaterTo
- end
- checkBox4.Style = forceIgnoreWater and Enum.ButtonStyle.RobloxRoundButton or Enum.ButtonStyle.RobloxRoundDefaultButton
- checkBox4.AutoButtonColor = not forceIgnoreWater
- checkBox4.Text = isIgnoreWater and 'X' or ''
- end
- checkBox4.MouseButton1Down:connect(function()
- ignoreWater = not ignoreWater
- updateIgnoreWater()
- 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 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 = selectionSize * 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 = gui
- 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 = gui
- 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 = materialSelection.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 updateUsabilityLocks()
- if currentTool then
- forceSnapToGrid = currentTool.usesMaterials and materialSelection.forceSnapToGrid
- updateSnapToGrid()
- forcePlaneLock = currentTool.name == 'Add' or currentTool.name == 'Subtract'
- updatePlaneLock()
- forceDynamicMaterial = currentTool.name == 'Subtract' or currentTool.name == 'Erode' or currentTool.name == 'Paint' or currentTool.name == 'Smooth' or currentTool.name == 'Smoother'
- forceDynamicMaterialTo = not (forceDynamicMaterial and currentTool.name == 'Paint')
- isDynamic = dynamicMaterial
- if forceDynamicMaterial then
- isDynamic = forceDynamicMaterialTo
- end
- forceIgnoreWater = (materialSelection.forceIgnoreWater and not isDynamic) or currentTool.name == 'Smooth'
- if materialSelection.forceIgnoreWater then
- forceIgnoreWaterTo = materialSelection.forceIgnoreWaterTo
- end
- if forceIgnoreWater and currentTool.name == 'Smooth' then
- forceIgnoreWaterTo = false
- end
- isIgnoreWater = ignoreWater
- if forceIgnoreWater then
- isIgnoreWater = forceIgnoreWaterTo
- end
- updateIgnoreWater()
- updateDynamicMaterial()
- end
- end
- function operation(centerPoint)
- local desiredMaterial = isDynamic and nearMaterial or materialSelection.enum
- local radius = selectionSize * .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)
- if modules[currentTool.name] then
- if modules[currentTool.name]['operation'] 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 = isDynamic and nearMaterial or desiredMaterial
- end
- end
- end
- modules[currentTool.name]['operation'](centerPoint, materials, occupancies, resolution, selectionSize, strength, desiredMaterial, brushShape, minBounds, maxBounds)
- end
- else
- local airFillerMaterial = materialAir
- local waterHeight = 0
- if isIgnoreWater and (currentTool.name == 'Erode' or currentTool.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 (snapToGrid or forceSnapToGrid) then
- local xOutside = 1 - max(0, abs(cellVectorX / resolution) + .5 - selectionSize * .5)
- local yOutside = 1 - max(0, abs(cellVectorY / resolution) + .5 - selectionSize * .5)
- local zOutside = 1 - max(0, abs(cellVectorZ / resolution) + .5 - selectionSize * .5)
- brushOccupancy = xOutside * yOutside * zOutside
- end
- end
- if cellMaterial ~= materialAir and cellMaterial ~= materialWater and cellMaterial ~= nearMaterial then
- nearMaterial = cellMaterial
- if isDynamic then
- desiredMaterial = nearMaterial
- end
- end
- if isIgnoreWater and cellMaterial == materialWater then
- cellMaterial = materialAir
- cellOccupancy = 0
- end
- local airFillerMaterial = waterHeight >= iy and airFillerMaterial or materialAir
- if currentTool.name == 'Add' then
- if selectionSize <= 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 currentTool.name == 'Subtract' then
- if cellMaterial ~= materialAir then
- if selectionSize <= 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 currentTool.name == 'Grow' then
- if brushOccupancy >= .5 then --working on
- local desiredOccupancy = cellOccupancy
- local neighborOccupancies, fullNeighbor, emptyNeighbor = getNeighborOccupancies(occupancies, ix, iy, iz, isIgnoreWater and materials)
- if cellOccupancy > 0 or fullNeighbor then --problem if selection size is small.
- desiredOccupancy = desiredOccupancy + neighborOccupancies * (strength + .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 currentTool.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 isIgnoreWater 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) * (strength + .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 currentTool.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
- function Selected(tool)
- if plugin then
- plugin:Activate(true)
- end
- if tool.button then
- tool.button:SetActive(true)
- lastTool = tool
- end
- if not userInput.MouseEnabled then
- prevCameraType = game.Workspace.CurrentCamera.CameraType
- game.Workspace.CurrentCamera.CameraType = Enum.CameraType.Fixed
- end
- on = true
- currentTool = tool
- updateUsabilityLocks()
- if modules[tool.name] and modules[tool.name]['On'] then
- modules[tool.name].On(mouse,Deselected)
- end
- if not modules[tool.name] or modules[tool.name]['operation'] then
- resizeGuiFrame()
- titlelabel.Text = tool.name
- gui.Parent = coreGui
- gui.Frame.Visible = true
- local loopTag = {} --using table as a unique value for debouncing
- currentLoopTag = loopTag
- while currentLoopTag and currentLoopTag == loopTag do
- local t = tick()
- local radius = selectionSize * .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, isIgnoreWater)
- if tool.name == 'Add' then
- mainPoint = mainPoint - mouse.UnitRay.Direction * .05
- elseif tool.name == 'Subtract' or tool.name == 'Paint' or tool.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 planeLock or forcePlaneLock then
- mainPoint = lineToPlaneIntersection(cameraPos, mouse.UnitRay.Direction, lastPlanePoint, lastNormal)
- end
- if snapToGrid or forceSnapToGrid 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
- for _, materialTable in pairs(materialsTable) do
- if materialTable.enum == foundMaterial then
- setMaterialSelection(materialTable)
- break
- end
- end
- 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) * selectionSize * resolution + Vector3.new(.1, .1, .1)
- selectionPart.Parent = gui
- 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 - strength * .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 planeLock or forcePlaneLock 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 + selectionSize * resolution * 1.5,(mainPoint - cameraPos).magnitude)
- quickWait()
- end
- end
- end
- function Deselected()
- if not userInput.MouseEnabled then
- game.Workspace.CurrentCamera.CameraType = prevCameraType
- end
- currentLoopTag = nil
- gui.Parent = script.Parent
- gui.Frame.Visible = false
- clearSelection()
- clearGrid()
- if lastTool then
- lastTool.button:SetActive(false)
- end
- mouseDown = false
- on = false
- local lastCurrentTool = currentTool
- currentTool = nil
- if lastCurrentTool and modules[lastCurrentTool.name] and modules[lastCurrentTool.name]['Off'] then
- modules[lastCurrentTool.name].Off()
- end
- end
- closeButton.MouseButton1Down:connect(Deselected)
- --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)
- selectionSize = max(minSelectionSize, min(maxSelectionSize, startingSelectionSize/scale))
- selectionSizeValue.Value = selectionSize
- else
- StartingDiff = difference
- startingSelectionSize = selectionSizeValue.Value
- 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 '..currentTool.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
- selectionSize = max(minSelectionSize, min(maxSelectionSize, selectionSize + change))
- selectionSizeValue.Value = selectionSize
- end
- if downKeys[Enum.KeyCode.LeftControl] or downKeys[Enum.KeyCode.RightControl] then
- strength = max(0, min(1, strength + change * (1/(maxSelectionSize-minSelectionSize))))
- strengthValue.Value = round(strength * 100 + 1)
- end
- end
- end
- mouse.WheelForward:connect(function()
- scrollwheel(1)
- end)
- mouse.WheelBackward:connect(function()
- scrollwheel(-1)
- end)
- if plugin then
- plugin.Deactivation:connect(function()
- if on then
- Deselected()
- end
- end)
- end
- setBrushShape(brushShape)
- setMaterialSelection(materialSelection)
- updatePlaneLock()
- updateSnapToGrid()
- updateDynamicMaterial()
- -- Reset keyboard status on lost focus as key release may never come blocked by popups etc.
- userInput.WindowFocusReleased:connect(function()
- downKeys = {}
- end)
- end
- 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
- return {
- ['operation'] = operation
- }
- local Terrain = workspace:WaitForChild('Terrain', 86400) or workspace:WaitForChild('Terrain')
- while not Terrain.IsSmooth do
- Terrain.Changed:wait()
- end
- local on = false
- local setup = false
- local currentTool = 'Create'
- --[[local toolBar = plugin:CreateToolbar('Smooth Terrain')
- regionEditButton = toolBar:CreateButton(
- 'Region Edit', --button title
- 'Manipulate regions of smoothterrain.', --hover text
- 'http://www.roblox.com/asset/?id=180084957' --icon
- )
- regionEditButton.Click:connect(function()
- if not on then
- if not setup then
- FirstTimeSetUp()
- end
- Selected()
- else
- Deselected()
- end
- end)]]
- function FirstTimeSetUp()
- setup = true
- local changeHistory = game:GetService('ChangeHistoryService')
- local terrain = game.Workspace.Terrain
- local utilityModule = require(script.Parent.Utility)()
- local coreGui = utilityModule:GetUsingAsPlugin() and game:GetService('CoreGui') or game.Players.LocalPlayer:WaitForChild('PlayerGui')
- local gui = script.Parent:WaitForChild('TerrainRegionGui')
- local guiFrame = gui:WaitForChild('Frame')
- local closeButton = guiFrame:WaitForChild('CloseButton')
- local buttonFillAir = guiFrame:WaitForChild('ButtonFillAir')
- local buttonFillWater = guiFrame:WaitForChild('ButtonFillWater')
- local buttonSelect = guiFrame:WaitForChild('ButtonSelect')
- local buttonMove = guiFrame:WaitForChild('ButtonMove')
- local buttonResize = guiFrame:WaitForChild('ButtonResize')
- local buttonRotate = guiFrame:WaitForChild('ButtonRotate')
- local buttonCopy = guiFrame:WaitForChild('ButtonCopy')
- local buttonPaste = guiFrame:WaitForChild('ButtonPaste')
- local buttonDelete = guiFrame:WaitForChild('ButtonDelete')
- local buttonFill = guiFrame:WaitForChild('ButtonFill')
- local fillFrame = gui:WaitForChild('FillFrame')
- local materialsTable = require(script.Parent.MaterialsList)
- local buttonFillFrameClose = fillFrame:WaitForChild('CloseButton')
- local buttonFillConfirm = fillFrame:WaitForChild('ButtonFillConfirm')
- local library = assert(LoadLibrary('RbxGui'))
- --local mouse = plugin:GetMouse()
- ----SETTINGS----
- local mode = 'Select' --Select, Edit
- local tool = 'None' --None, Resize, Move, Rotate
- local button = 'Select' --Select, Move, Resize, Rotate
- 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
- ----------------
- --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
- local materialSelection = 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
- -------------
- local function setMaterialSelection(newMaterialSelection)
- materialSelection = newMaterialSelection
- for _, v in pairs(fillFrame:GetChildren()) do
- if string.sub(v.Name,1,14) == 'MaterialButton' then
- if v.Name == 'MaterialButton' .. materialSelection.enum.Name then
- v.BackgroundTransparency = .1
- else
- v.BackgroundTransparency = 1
- end
- end
- end
- end
- for i,materialSubTable in pairs(materialsTable) do
- local newMaterialButton = Instance.new('ImageButton')
- newMaterialButton.Name = 'MaterialButton' .. materialSubTable.enum.Name
- newMaterialButton.BorderSizePixel = 2
- newMaterialButton.BorderColor3 = Color3.new(.2, 1, 1)
- newMaterialButton.BackgroundColor3 = Color3.new(.2, 1, 1)
- newMaterialButton.BackgroundTransparency = 1
- newMaterialButton.Image = materialSubTable.image
- newMaterialButton.Size = UDim2.new(0, 35, 0, 35)
- newMaterialButton.Position = UDim2.new(0, 5 + ((i-1) % 4) * 40, 0, -5 + ceil(i/4) * 40)
- newMaterialButton.MouseButton1Down:connect(function()
- setMaterialSelection(materialsTable[i])
- end)
- newMaterialButton.Parent = fillFrame
- end
- fillFrame.Size = UDim2.new(0, 180, 0, 95 + ceil(#materialsTable / 4) * 40)
- setMaterialSelection(materialSelection)
- function setButton(newButton)
- lockInMap()
- buttonSelect.Style = newButton == 'Select' and Enum.ButtonStyle.RobloxRoundDropdownButton or Enum.ButtonStyle.RobloxRoundDefaultButton
- buttonSelect.TextColor3 = newButton == 'Select' and textSelectColor or white
- buttonMove.Style = newButton == 'Move' and Enum.ButtonStyle.RobloxRoundDropdownButton or Enum.ButtonStyle.RobloxRoundDefaultButton
- buttonMove.TextColor3 = newButton == 'Move' and textSelectColor or white
- buttonResize.Style = newButton == 'Resize' and Enum.ButtonStyle.RobloxRoundDropdownButton or Enum.ButtonStyle.RobloxRoundDefaultButton
- buttonResize.TextColor3 = newButton == 'Resize' and textSelectColor or white
- buttonRotate.Style = newButton == 'Rotate' and Enum.ButtonStyle.RobloxRoundDropdownButton or Enum.ButtonStyle.RobloxRoundDefaultButton
- buttonRotate.TextColor3 = newButton == 'Rotate' and textSelectColor or white
- if newButton == 'Select' then
- mode = 'Select'
- tool = 'Resize'
- elseif newButton == 'Move' then
- mode = 'Edit'
- tool = 'Move'
- elseif newButton == 'Resize' then
- mode = 'Edit'
- tool = 'Resize'
- elseif newButton == 'Rotate' then
- mode = 'Edit'
- tool = 'Rotate'
- end
- button = newButton
- 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)
- buttonFillAir.MouseButton1Down:connect(function()
- fillAir = not fillAir
- buttonFillAir.Text = fillAir and 'X' or ''
- if button=='Move' or button=='Resize' then
- updateDragOperation()
- elseif button=='Rotate' then
- updateRotateOperation()
- end
- end)
- buttonFillWater.MouseButton1Down:connect(function()
- fillWater = not fillWater
- buttonFillWater.Text = fillWater and 'X' or ''
- if button=='Move' or button=='Resize' then
- updateDragOperation()
- elseif button=='Rotate' then
- updateRotateOperation()
- 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 loopx = #lockedMaterials --Tiling resize --fun but not too many use cases with natural terrain.
- local loopy = #lockedMaterials[1]
- local loopz = #lockedMaterials[1][1]
- local tempRegionSize = Vector3.new(1,1,1) + temporaryEnd - temporaryStart
- local newMat = {}
- local newOcc = {}
- local offsetx = -1
- local offsety = -1
- local offsetz = -1
- if dragStart then
- offsetx = offsetx + (-tempRegionSize.x % loopx)
- offsety = offsety + (-tempRegionSize.y % loopy)
- offsetz = offsetz + (-tempRegionSize.z % loopz)
- end
- for x=1, tempRegionSize.x do
- local targetx = (offsetx + x) % loopx + 1
- local xtm = {}
- local xto = {}
- for y=1, tempRegionSize.y do
- local targety = (offsety + y) % loopy + 1
- local ytm = {}
- local yto = {}
- for z=1, tempRegionSize.z do
- local targetz = (offsetz + z) % loopz + 1
- ytm[z] = lockedMaterials[targetx][targety][targetz]
- yto[z] = lockedOccupancies[targetx][targety][targetz]
- end
- xtm[y] = ytm
- xto[y] = yto
- end
- newMat[x] = xtm
- newOcc[x] = xto
- end]]
- --[[local loopx = #lockedMaterials --Scaling closest neightbor resize --not perfect
- local loopy = #lockedMaterials[1]
- local loopz = #lockedMaterials[1][1]
- local tempRegionSize = Vector3.new(1,1,1) + temporaryEnd - temporaryStart
- local tempSizeX = tempRegionSize.x
- local tempSizeY = tempRegionSize.y
- local tempSizeZ = tempRegionSize.z
- local roundx = tempSizeX < loopx and math.floor or math.ceil
- local roundy = tempSizeY < loopy and math.floor or math.ceil
- local roundz = tempSizeZ < loopz and math.floor or math.ceil
- local newMat = {}
- local newOcc = {}
- for x=1, tempSizeX do
- local targetx = roundx(x/tempSizeX*loopx)
- local xtm = {}
- local xto = {}
- for y=1, tempSizeY do
- local targety = roundy(y/tempSizeY*loopy)
- local ytm = {}
- local yto = {}
- for z=1, tempSizeZ do
- local targetz = roundz(z/tempSizeZ*loopz)
- ytm[z] = lockedMaterials[targetx][targety][targetz]
- yto[z] = lockedOccupancies[targetx][targety][targetz]
- end
- xtm[y] = ytm
- xto[y] = yto
- end
- newMat[x] = xtm
- newOcc[x] = xto
- end]]
- 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')
- if utilityModule:GetUsingAsPlugin() then
- changeHistory:SetWaypoint('Terrain Paste')
- end
- 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')
- if utilityModule:GetUsingAsPlugin() then
- changeHistory:SetWaypoint('Terrain Delete')
- end
- selectionEffect(oldStart,oldEnd,'Really red',1,1.2,.5)
- end
- end)
- buttonFill.MouseButton1Down:connect(function()
- fillFrame.Visible = not fillFrame.Visible
- end)
- buttonFillFrameClose.MouseButton1Down:connect(function()
- fillFrame.Visible = false
- 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 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] = materialSelection.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 = terrain:ReadVoxels(region, resolution)
- fillFrame.Visible = false
- if utilityModule:GetUsingAsPlugin() then
- changeHistory:SetWaypoint('Terrain Fill')
- end
- 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 = gui
- 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
- function Selected()
- --plugin:Activate(true)
- --regionEditButton:SetActive(true)
- on = true
- gui.Parent = coreGui
- gui.Frame.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 = gui
- 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--guiFrame--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--guiFrame--selectionPart
- selectionArcHandles.MouseDrag:connect(dragArcHandles)
- end
- renderSelection()
- setButton(button)
- end
- function Deselected()
- setButton('Select')
- gui.Parent = script.Parent
- gui.Frame.Visible = false
- clearSelection()
- --regionEditButton:SetActive(false)
- behindThis = nil
- on = false
- if turnOff then
- turnOff()
- 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
- if utilityModule:GetUsingAsPlugin() then
- changeHistory:SetWaypoint('Terrain '..button)
- end
- 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()
- if utilityModule:GetUsingAsPlugin() then
- changeHistory:SetWaypoint('Terrain '..button)
- end
- end
- dragVector = nil
- dragAngle = nil
- renderSelection()
- --lockInMap()
- end)
- closeButton.MouseButton1Down:connect(Deselected)
- --[[plugin.Deactivation:connect(function()
- if on then
- Deselected()
- end
- end)]]
- local function historyChanged()
- selectionStart = nil
- selectionEnd = nil
- lockedMaterials = nil
- lockedOccupancies = nil
- setButton('Select')
- end
- if utilityModule:GetUsingAsPlugin() then
- changeHistory.OnUndo:connect(historyChanged)
- changeHistory.OnRedo:connect(historyChanged)
- end
- end
- function On(mouseHandMeDown,turnOffHandMeDown)
- mouse = mouseHandMeDown
- turnOff = turnOffHandMeDown
- if not setup then --I do this so that things only get set up when this plugin is used.
- FirstTimeSetUp()
- end
- Selected()
- end
- function Off()
- if Deselected then
- Deselected()
- end
- end
- return {
- ['On'] = On,
- ['Off'] = Off,
- }
- local initialised = false
- function initialiseUtility(plugin)
- if not initialised then
- initialised = true
- utility.usingAsPlugin = (plugin ~= nil)
- utility.plugin = plugin
- local function createIngameButtonCreator()
- local buttonCreator = {}
- function buttonCreator:CreateButton(name, hint, image)
- local backpack = game.Players.LocalPlayer:WaitForChild("Backpack")
- local tool = Instance.new("Tool")
- tool.TextureId = image
- tool.RequiresHandle = false
- tool.ToolTip = hint
- tool.Parent = backpack
- local result = {}
- result.Click = tool.Equipped
- result.Deselected = tool.Unequipped
- function result:SetActive(active)
- --Do nothing
- end
- return result
- end
- return buttonCreator
- end
- function utility:GetButtonCreator()
- return utility.usingAsPlugin and utility.plugin:CreateToolbar('Smooth Terrain') or createIngameButtonCreator()
- end
- function utility:GetUsingAsPlugin()
- return utility.usingAsPlugin
- end
- function utility:GetCoreGui()
- return utility.usingAsPlugin and game:GetService("CoreGui") or game.Players.LocalPlayer.PlayerGui
- end
- function utility:GetMouse()
- return utility.usingAsPlugin and utility.plugin:GetMouse() or game.Players.LocalPlayer:GetMouse()
- end
- end
- return utility
- end
- local materialsTable = { --Interface order is defined by order here
- {enum = Enum.Material.Grass, image = 'http://www.roblox.com/asset/?id=225314676'},
- {enum = Enum.Material.Sand, image = 'http://www.roblox.com/asset/?id=225315607'},
- {enum = Enum.Material.Rock, image = 'http://www.roblox.com/asset/?id=225315178'},
- {enum = Enum.Material.Water, image = 'http://www.roblox.com/asset/?id=225315529', forceIgnoreWater = true, forceIgnoreWaterTo = false},
- {enum = Enum.Material.Ground, image = 'http://www.roblox.com/asset/?id=254542189'},
- {enum = Enum.Material.Sandstone, image = 'http://www.roblox.com/asset/?id=254541350'},
- {enum = Enum.Material.Slate, image = 'http://www.roblox.com/asset/?id=225315290'},
- {enum = Enum.Material.Snow, image = 'http://www.roblox.com/asset/?id=254541898'},
- {enum = Enum.Material.Mud, image = 'http://www.roblox.com/asset/?id=254541862'},
- {enum = Enum.Material.Brick, image = 'http://www.roblox.com/asset/?id=225315419', forceSnapToGrid = true},
- {enum = Enum.Material.Concrete, image = 'http://www.roblox.com/asset/?id=225314983'},
- {enum = Enum.Material.Glacier, image = 'http://www.roblox.com/asset/?id=254541572'},
- {enum = Enum.Material.WoodPlanks, image = 'http://www.roblox.com/asset/?id=225315705', forceSnapToGrid = true},
- {enum = Enum.Material.CrackedLava, image = 'http://www.roblox.com/asset/?id=254541726'},
- {enum = Enum.Material.Basalt, image = 'http://www.roblox.com/asset/?id=254542066'},
- {enum = Enum.Material.Ice, image = 'rbxassetid://397352205'},
- {enum = Enum.Material.Salt, image = 'rbxassetid://397352299'},
- {enum = Enum.Material.Cobblestone, image = 'rbxassetid://397352378'},
- {enum = Enum.Material.Limestone, image = 'rbxassetid://397352474'},
- {enum = Enum.Material.Asphalt, image = 'rbxassetid://397352644'},
- {enum = Enum.Material.LeafyGrass, image = 'rbxassetid://397720681'},
- {enum = Enum.Material.Pavement, image = 'rbxassetid://397727024'},
- }
- return materialsTable
- local coreGui = game:GetService('CoreGui')
- local changeHistoryService = game:GetService('ChangeHistoryService')
- local terrain = game.Workspace.Terrain
- local gui = script.Parent:WaitForChild('TerrainGenerationGui')
- local mainFrame = gui:WaitForChild('MainFrame')
- local scrollingFrame = mainFrame:WaitForChild('ScrollingFrame')
- local canvas = scrollingFrame:WaitForChild('Canvas')
- local progressFrame = gui:WaitForChild('ProgressFrame')
- local pauseButton = progressFrame:WaitForChild('PuaseButton')
- local cancelButton = progressFrame:WaitForChild('CancelButton')
- local barFill = progressFrame:WaitForChild('Bar'):WaitForChild('Fill')
- local multipleChoiceFrame = gui:WaitForChild('MultipleChoiceFrame')
- local allBiomes = {
- ['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 turnOff = 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 = {}
- ---------------------------------------------
- function createSection(name, pos, size, parent)
- local sectionGui = gui:WaitForChild('TemplateSection'):clone()
- sectionGui.Name = 'Section'..name
- sectionGui.Text = name
- sectionGui.Position = UDim2.new(0,0,0,pos)
- sectionGui.Frame.Size = UDim2.new(1,0,0,size)
- sectionGui.Parent = parent
- sectionGui.Visible = true
- return sectionGui, sectionGui.Frame
- end
- function createTextBox(name, pos, parent, setting, changedFunction)
- local setting = setting and tostring(setting) or ''
- local frame = gui:WaitForChild('TemplateTextBox'):clone()
- local field = frame:WaitForChild('TextBox')
- field.Text = setting
- field.Changed:connect(function(prop)
- if prop == 'Text' then
- changedFunction(field.Text)
- end
- end)
- changedFunction(field.Text)
- frame.Name = 'TextInputBox'..name
- frame.Text = ' '..name..':'
- frame.Position = UDim2.new(0,0,0,pos)
- frame.Parent = parent
- frame.Visible = true
- return frame
- end
- local currentMultiChoiceOpen = nil
- function closeMultiChoiceFrame()
- if currentMultiChoiceOpen then
- currentMultiChoiceOpen.Style = 'RobloxRoundDefaultButton'
- end
- currentMultiChoiceOpen = nil
- multipleChoiceFrame.Visible = false
- end
- function createMultiChoice(name, pos, parent, choices, setting, changedFunction)
- local setting = setting
- local frame = gui:WaitForChild('TemplateMultiChoice'):clone()
- frame.Name = 'MultipleChoice'..name
- frame.Text = name
- frame.Position = UDim2.new(0,0,0,pos)
- local button = frame:WaitForChild('Button')
- button.Text = choices[setting]
- button.MouseButton1Down:connect(function()
- if currentMultiChoiceOpen ~= button then
- currentMultiChoiceOpen = button
- --button.Style = 'RobloxRoundButton' --would be nice, but with autobuttoncolor, not so much.
- multipleChoiceFrame.TitleLabel.Text = name
- for _,item in pairs(multipleChoiceFrame:GetChildren()) do
- if string.sub(item.Name,1,6) == 'Choice' then
- item:Destroy()
- end
- end
- for i, text in ipairs(choices) do
- local choiceButton = gui:WaitForChild('TemplateButton'):clone()
- choiceButton.Name = 'Choice'..text
- choiceButton.Visible = true
- choiceButton.Text = text
- choiceButton.Position = UDim2.new(0,10,0,-3+i*35)
- choiceButton.Size = UDim2.new(1,-20,0,30)
- choiceButton.Parent = multipleChoiceFrame
- choiceButton.MouseButton1Down:connect(function()
- if setting ~= i then
- setting = i
- button.Text = text
- changedFunction(i, text)
- end
- closeMultiChoiceFrame()
- end)
- end
- multipleChoiceFrame.Size = UDim2.new(0,180,0,50 + #choices*35)
- multipleChoiceFrame.Visible = true
- else
- closeMultiChoiceFrame()
- end
- end)
- frame.Parent = parent
- frame.Visible = true
- return frame
- end
- multipleChoiceFrame:WaitForChild('CloseButton').MouseButton1Down:connect(closeMultiChoiceFrame)
- function createCheckBox(name, pos, parent, setting, changedFunction)
- local setting = setting
- local checkBoxGui = gui:WaitForChild('TemplateCheckBox'):clone()
- checkBoxGui.Name = 'CheckBox'..name
- checkBoxGui.Label.Text = name
- checkBoxGui.Text = setting and 'X' or ''
- checkBoxGui.Position = UDim2.new(0,4,0,pos)
- checkBoxGui.Parent = parent
- checkBoxGui.Visible = true
- checkBoxGui.MouseButton1Down:connect(function()
- setting = not setting
- checkBoxGui.Text = setting and 'X' or ''
- changedFunction(setting)
- end)
- return checkBoxGui
- end
- -------------------------------------------------------------------------------------------------------------------
- -------------------------------------------------------------------------------------------------------------------
- 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 = {}
- 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
- randomseed(6180339)
- theseed={}
- for i=1,999 do
- table.insert(theseed,math.random())
- 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 'Paused' or 'Pause'
- end
- function generate()
- if not generating and not clearing 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(allBiomes) 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
- --------------------------------------------------------------------------------------------------------------------
- ------------------------------------------------Setup User Interface------------------------------------------------
- local mapSizes = {128,256,512,1024}
- local mapSizeChangeFunction = function(index,text)
- mapWidth = mapSizes[index]
- end
- createMultiChoice('Map Size', 5, canvas, {'Small (128)','Medium (256)','Large (512)','Massive (1024)'}, 2, mapSizeChangeFunction)
- local seedChangeFunction = function(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
- createTextBox('Seed', 65, canvas, masterSeed, seedChangeFunction)
- local cavesChangeFunction = function(value)
- generateCaves = value
- end
- local cavesCheckBox = createCheckBox('Caves', 100, canvas, generateCaves, cavesChangeFunction)
- local biomeSizes = {50,100,200,500}
- local biomeSizeChangeFunction = function(index,text)
- biomeSize = biomeSizes[index]
- end
- createMultiChoice('Biome Size', 135, canvas, {'Small (50)','Medium (100)','Large (200)','Massive (500)'}, 2, biomeSizeChangeFunction)
- local biomesSettingsButton, biomesFrame = createSection('Biomes',200,275,canvas)
- local waterChangeFunction = function(value)
- allBiomes['Water'] = value
- end
- local waterCheckBox = createCheckBox('Water', 5, biomesFrame, allBiomes['Water'], waterChangeFunction)
- local marshChangeFunction = function(value)
- allBiomes['Marsh'] = value
- end
- local marshCheckBox = createCheckBox('Marsh', 35, biomesFrame, allBiomes['Marsh'], marshChangeFunction)
- local plainsChangeFunction = function(value)
- allBiomes['Plains'] = value
- end
- local plainsCheckBox = createCheckBox('Plains', 65, biomesFrame, allBiomes['Plains'], plainsChangeFunction)
- local hillsChangeFunction = function(value)
- allBiomes['Hills'] = value
- end
- local hillsCheckBox = createCheckBox('Hills', 95, biomesFrame, allBiomes['Hills'], hillsChangeFunction)
- local dunesChangeFunction = function(value)
- allBiomes['Dunes'] = value
- end
- local dunesCheckBox = createCheckBox('Dunes', 125, biomesFrame, allBiomes['Dunes'], dunesChangeFunction)
- local canyonsChangeFunction = function(value)
- allBiomes['Canyons'] = value
- end
- local canyonsCheckBox = createCheckBox('Canyons', 155, biomesFrame, allBiomes['Canyons'], canyonsChangeFunction)
- local mountainsChangeFunction = function(value)
- allBiomes['Mountains'] = value
- end
- local mountainsCheckBox = createCheckBox('Mountains', 185, biomesFrame, allBiomes['Mountains'], mountainsChangeFunction)
- local lavaflowChangeFunction = function(value)
- allBiomes['Lavaflow'] = value
- end
- local lavaflowCheckBox = createCheckBox('Lavascape', 215, biomesFrame, allBiomes['Lavaflow'], lavaflowChangeFunction)
- local arcticChangeFunction = function(value)
- allBiomes['Arctic'] = value
- end
- local arcticCheckBox = createCheckBox('Arctic', 245, biomesFrame, allBiomes['Arctic'], arcticChangeFunction)
- --------------------------------------------------------------------------------------------------------------------
- --------------------------------------------------------------------------------------------------------------------
- function On(mouseHandMeDown,turnOffHandMeDown)
- gui.Parent = coreGui
- on = true
- if mouseHandMeDown then
- mouse = mouseHandMeDown
- end
- if turnOffHandMeDown then
- turnOff = turnOffHandMeDown
- end
- end
- function Off()
- closeMultiChoiceFrame()
- if turnOff then
- turnOff()
- end
- on = false
- gui.Parent = nil
- end
- function clearTerrain()
- if not generating and not clearing then
- clearing = true
- terrain:Clear()
- changeHistoryService:SetWaypoint('Terrain Clear')
- clearing = false
- end
- end
- pauseButton.MouseButton1Down:connect(function()
- paused = not paused
- updatePausedButton()
- end)
- cancelButton.MouseButton1Down:connect(function()
- if not cancelIt then
- cancelIt = true
- print('Canceled')
- end
- end)
- mainFrame:WaitForChild('CloseButton').MouseButton1Down:connect(function()
- Off()
- end)
- mainFrame:WaitForChild('GenerateButton').MouseButton1Down:connect(function()
- generate()
- end)
- mainFrame:WaitForChild('ClearButton').MouseButton1Down:connect(function()
- clearTerrain()
- end)
- return {
- ['On'] = On,
- ['Off'] = Off,
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement