Advertisement
dahpiglz

Untitled

Dec 3rd, 2017
360
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 113.48 KB | None | 0 0
  1.  
  2. local Terrain = workspace:WaitForChild('Terrain', 86400) or workspace:WaitForChild('Terrain')
  3. while not Terrain.IsSmooth do
  4. Terrain.Changed:wait()
  5. end
  6.  
  7. local on = false
  8. local setup = false
  9. local currentTool = nil
  10. local utilityModule = require(script.Parent.Utility)(plugin)
  11.  
  12.  
  13. --[[
  14. How to add a module:
  15. 1. Add the require to the modules table below.
  16. 2. Add a plugin button for it into the pluginButtons table, with the same index as used in the modules table.
  17.  
  18. How modules work
  19. Your ModuleScript should return a table. The table can contain the following functions
  20. 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.
  21. Off = This function will be called when your tool is deselected.
  22. operation = This function will be called when this toolset's brushing functionality is used
  23. operation(centerPoint, materialsTable, occupanciesTable, resolution, selectionSize, strength, desiredMaterial, brushShape, minBounds, maxBounds)
  24. ]]
  25.  
  26. local modules = {
  27. ['Smooth'] = require(script.Parent.SmootherModule),
  28. ['Generate'] = utilityModule:GetUsingAsPlugin() and require(script.Parent.GenerationModule) or {},
  29. ['Region Editor'] = require(script.Parent.RegionEditorModule),
  30. }
  31.  
  32.  
  33. local buttonCreator = utilityModule:GetButtonCreator()
  34.  
  35. local plugins = {
  36. {name = 'Generate',
  37. button = utilityModule:GetUsingAsPlugin() and buttonCreator:CreateButton(
  38. 'Generate', --button title
  39. 'Generate landscapes of terrain.', --hover text
  40. 'http://www.roblox.com/asset/?id=236006872' --button image
  41. ),
  42. studioOnly = true,
  43. },
  44. {name = 'Add',
  45. button = buttonCreator:CreateButton(
  46. 'Add',
  47. 'Click and hold to add terrain.',
  48. 'http://www.roblox.com/asset/?id=225328572'
  49. ),
  50. usesMaterials = true,
  51. },
  52. {name = 'Subtract',
  53. button = buttonCreator:CreateButton(
  54. 'Subtract',
  55. 'Click and hold to remove terrain.',
  56. 'http://www.roblox.com/asset/?id=225328818'
  57. ),
  58. },
  59. {name = 'Paint',
  60. button = buttonCreator:CreateButton(
  61. 'Paint',
  62. 'Paint the material of the terrain.',
  63. 'http://www.roblox.com/asset/?id=225328954'
  64. ),
  65. usesMaterials = true,
  66. },
  67. {name = 'Grow',
  68. button = buttonCreator:CreateButton(
  69. 'Grow',
  70. 'Click and hold to grow and expand terrain.',
  71. 'http://www.roblox.com/asset/?id=225329153'
  72. ),
  73. usesMaterials = true,
  74. },
  75. {name = 'Erode',
  76. button = buttonCreator:CreateButton(
  77. 'Erode',
  78. 'Click and hold to erode and remove terrain.',
  79. 'http://www.roblox.com/asset/?id=225329301'
  80. ),
  81. },
  82. {name = 'Smooth',
  83. button = buttonCreator:CreateButton(
  84. 'Smooth',
  85. 'Brush to smooth out rough or jagged terrain.',
  86. 'http://www.roblox.com/asset/?id=225329641'
  87. ),
  88. },
  89. {name = 'Region Editor',
  90. button = buttonCreator:CreateButton(
  91. 'Regions',
  92. 'Manipulate regions of smooth terrain.',
  93. 'http://www.roblox.com/asset/?id=240631063'
  94. ),
  95. },
  96. }
  97.  
  98. for i,tool in pairs(plugins) do
  99. if not tool.studioOnly or (utilityModule:GetUsingAsPlugin() and tool.studioOnly) then
  100. tool.button.Click:connect(function()
  101. 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.
  102. if not setup then --I do this so that things only get set up when this plugin is used.
  103. FirstTimeSetUp()
  104. end
  105. Selected(tool)
  106. else
  107. Deselected()
  108. end
  109. end)
  110. if not utilityModule:GetUsingAsPlugin() then
  111. if tool.button.Deselected then
  112. tool.button.Deselected:connect(function()
  113. Deselected()
  114. end)
  115. end
  116. end
  117. end
  118. end
  119.  
  120. function FirstTimeSetUp()
  121. setup = true
  122. local changeHistory = utilityModule:GetUsingAsPlugin() and game:GetService('ChangeHistoryService') or nil
  123. local terrain = game.Workspace.Terrain
  124. local coreGui = utilityModule:GetCoreGui()
  125. local gui = script.Parent:WaitForChild('TerrainBrushGui')
  126. local guiFrame = gui:WaitForChild('Frame')
  127. local closeButton = guiFrame:WaitForChild('CloseButton')
  128. local titlelabel = guiFrame:WaitForChild('TitleLabel')
  129. local checkBox1 = guiFrame:WaitForChild('CheckBox1')
  130. local checkBox2 = guiFrame:WaitForChild('CheckBox2')
  131. local checkBox3 = guiFrame:WaitForChild('CheckBox3')
  132. local checkBox4 = guiFrame:WaitForChild('CheckBox4')
  133. local toolTip1 = guiFrame:WaitForChild('ToolTip1')
  134. local toolTip2 = guiFrame:WaitForChild('ToolTip2')
  135. local label4 = guiFrame:WaitForChild('Label4')
  136. local divider2 = guiFrame:WaitForChild('Divider2')
  137. local library = assert(LoadLibrary('RbxGui'))
  138. local mouse = utilityModule:GetMouse()
  139. local userInput = game:GetService('UserInputService')
  140. local prevCameraType = game.Workspace.CurrentCamera.CameraType
  141.  
  142. --SUB SETTINGS-- (Non-userfacing Settings)
  143. local resolution = 4 --This is the size of voxels on Roblox. Why is this a variable? ;)
  144. local minSelectionSize = 1
  145. local maxSelectionSize = 16
  146. local clickThreshold = .1
  147. local toolTipShowTime = 3.5
  148. local materialsTable = require(script.Parent.MaterialsList)
  149. local brushShapes = {
  150. ['Sphere'] = {
  151. name = 'Sphere',
  152. button = guiFrame:WaitForChild('ShapeButton1'),
  153. image = 'http://www.roblox.com/asset/?id=225799533',
  154. selectedImage = 'http://www.roblox.com/asset/?id=225801914',
  155. },
  156. ['Box'] = {
  157. name = 'Box',
  158. button = guiFrame:WaitForChild('ShapeButton2'),
  159. image = 'http://www.roblox.com/asset/?id=225799696',
  160. selectedImage = 'http://www.roblox.com/asset/?id=225802254',
  161. },
  162. }
  163. ----------------
  164.  
  165. ----SETTINGS---- (Interface Settings)
  166. local selectionSize = 6
  167. local strength = .5
  168. local snapToGrid = false
  169. local planeLock = false
  170. local ignoreWater = true
  171. local brushShape = 'Sphere'
  172. local materialSelection = materialsTable[1]
  173. local dynamicMaterial = false
  174. ----------------
  175.  
  176. ----Variables----
  177. local forcePlaneLock = false
  178. local forceSnapToGrid = false
  179. local forceDynamicMaterial = false
  180. local forceDynamicMaterialTo = true
  181. local isDynamic = false
  182. local forceIgnoreWater = false
  183. local forceIgnoreWaterTo = true
  184. local isIgnoreWater = true
  185. local forceMaterial = nil
  186. local nearMaterial = nil
  187. local selectionPart = nil
  188. local selectionObject = nil
  189. local gridLineParts = {}
  190. local currentLoopTag = nil
  191. local lastMainPoint = Vector3.new(0, 0, 0)
  192. local click = false
  193. local firstOperation = tick()
  194. local downKeys = {}
  195. local lastPlanePoint = Vector3.new(0, 0, 0)
  196. local lastNormal = Vector3.new(0, 1, 0)
  197. local lastCursorDistance = 300
  198. 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
  199. local toolTip1Change = nil
  200. local toolTip2Change = nil
  201. local materialAir = Enum.Material.Air
  202. local materialWater = Enum.Material.Water
  203. local ceil = math.ceil
  204. local floor = math.floor
  205. local abs = math.abs
  206. local min = math.min
  207. local max = math.max
  208. local sqrt = math.sqrt
  209. local sin = math.sin
  210. local cos = math.cos
  211. local pi = math.pi
  212. ---------------
  213.  
  214.  
  215. local selectionSizeSlider, selectionSizeValue = library.CreateSlider(maxSelectionSize, 90, UDim2.new(1, -98, 0, 40))
  216. selectionSizeSlider.Parent = guiFrame
  217. selectionSizeValue.Changed:connect(function()
  218. selectionSize = selectionSizeValue.Value
  219. if selectionPart then
  220. selectionPart.Size = Vector3.new(1, 1, 1) * selectionSize * resolution + Vector3.new(.1, .1, .1)
  221. end
  222.  
  223. toolTip1.Visible = true
  224. local currentToolTip1Change = {}
  225. toolTip1Change = currentToolTip1Change
  226. wait(toolTipShowTime)
  227. if toolTip1Change == currentToolTip1Change then
  228. toolTip1.Visible = false
  229. end
  230. end)
  231. selectionSizeValue.Value = selectionSize
  232. toolTip1.Visible = false
  233.  
  234.  
  235. local strengthslider, strengthValue = library.CreateSlider(101, 90, UDim2.new(1, -98, 0, 65))
  236. strengthslider.Parent = guiFrame
  237. strengthValue.Changed:connect(function()
  238. strength = (strengthValue.Value - 1) / 100
  239. if selectionObject then
  240. selectionObject.SurfaceTransparency = .95 - strength * .3
  241. end
  242.  
  243. toolTip2.Visible = true
  244. local currentToolTip2Change = {}
  245. toolTip2Change = currentToolTip2Change
  246. wait(toolTipShowTime)
  247. if toolTip2Change == currentToolTip2Change then
  248. toolTip2.Visible = false
  249. end
  250. end)
  251. strengthValue.Value = strength * 100
  252. toolTip2.Visible = false
  253.  
  254. function setBrushShape(newBrushShape)
  255. brushShape = newBrushShape
  256. for _,v in pairs(brushShapes) do
  257. --v.button.Image = (newBrushShape == v) and v.selectedImage or v.image
  258. v.button.ImageTransparency = (newBrushShape == v.name) and 0 or .5
  259. v.button.ImageColor3 = (newBrushShape == v.name) and Color3.new(1,1,1) or Color3.new(.5,.5,.5)
  260. end
  261. clearSelection()
  262. end
  263. for _,v in pairs(brushShapes) do
  264. v.button.MouseButton1Down:connect(function()
  265. setBrushShape(v.name)
  266. end)
  267. end
  268.  
  269. local MakeToolTip
  270. do
  271. local ActiveToolTip = Instance.new("StringValue")
  272.  
  273. function MakeToolTip(Gui, Text)
  274. local Name = tostring(Gui:GetFullName() .. Text)
  275.  
  276. local Frame = Instance.new("Frame", Gui)
  277. Frame.BackgroundTransparency = 0.3
  278. Frame.BackgroundColor3 = Color3.new(0, 0, 0)
  279. Frame.Size = UDim2.new(0, 100, 0, 30)
  280. Frame.Position = UDim2.new(0.8, 0, 0.8, 0)
  281. Frame.SizeConstraint = "RelativeYY"
  282. Frame.ZIndex = Gui.ZIndex + 1
  283. Frame.Style = "DropShadow"
  284.  
  285. local TextLabel = Instance.new("TextLabel", Frame)
  286. TextLabel.BackgroundTransparency = 1
  287. TextLabel.TextXAlignment = "Left"
  288. TextLabel.Text = Text
  289. TextLabel.BorderSizePixel = 0
  290. TextLabel.TextColor3 = Color3.new(1, 1, 1)
  291. TextLabel.Size = UDim2.new(1, -30, 1, 0);
  292. TextLabel.Position = UDim2.new(0, 10, 0, 0);
  293. TextLabel.FontSize = "Size10"
  294. TextLabel.ZIndex = Frame.ZIndex
  295. TextLabel.TextStrokeColor3 = Color3.new(0, 0, 0)
  296. TextLabel.TextStrokeTransparency = 0.87
  297.  
  298. Frame.Visible = false
  299.  
  300. Gui.MouseEnter:connect(function()
  301. ActiveToolTip.Value = Name
  302. Frame.Visible = true
  303. Frame.Size = UDim2.new(0, math.ceil(TextLabel.TextBounds.X + 36), Frame.Size.Y.Scale, Frame.Size.Y.Offset)
  304. end)
  305. ActiveToolTip.Changed:connect(function()
  306. if ActiveToolTip.Value ~= Name then
  307. Frame.Visible = false
  308. end
  309. end)
  310. Gui.MouseLeave:connect(function()
  311. Frame.Visible = false
  312. end)
  313. end
  314. end
  315.  
  316. local function setMaterialSelection(newMaterialSelection)
  317. materialSelection = newMaterialSelection
  318. updateUsabilityLocks()
  319. for _, v in pairs(guiFrame:GetChildren()) do
  320. if string.sub(v.Name,1,14) == 'MaterialButton' then
  321. if v.Name == 'MaterialButton' .. materialSelection.enum.Name then
  322. v.BackgroundTransparency = .1
  323. else
  324. v.BackgroundTransparency = 1
  325. end
  326. end
  327. end
  328. end
  329. for i,materialSubTable in pairs(materialsTable) do
  330. local newMaterialButton = Instance.new('ImageButton')
  331. newMaterialButton.Name = 'MaterialButton' .. materialSubTable.enum.Name
  332. newMaterialButton.BorderSizePixel = 2
  333. newMaterialButton.BorderColor3 = Color3.new(.2, 1, 1)
  334. newMaterialButton.BackgroundColor3 = Color3.new(.2, 1, 1)
  335. newMaterialButton.BackgroundTransparency = 1
  336. newMaterialButton.Image = materialSubTable.image
  337. newMaterialButton.Size = UDim2.new(0, 35, 0, 35)
  338. newMaterialButton.Position = UDim2.new(0, 5 + ((i-1) % 4) * 40, 0, 225 + ceil(i/4) * 40)
  339. newMaterialButton.MouseButton1Down:connect(function()
  340. setMaterialSelection(materialsTable[i])
  341. end)
  342. newMaterialButton.Parent = guiFrame
  343. MakeToolTip(newMaterialButton, materialSubTable.enum.Name:gsub("([A-Z])", " %1"):gsub("^%s", "")) -- Add spaces to names
  344. end
  345.  
  346. function resizeGuiFrame()
  347. local materialsDynamic = dynamicMaterial
  348. if forceDynamicMaterial then
  349. materialsDynamic = forceDynamicMaterialTo
  350. end
  351. local desiredSize = UDim2.new(0, 180, 0, 240)
  352. if currentTool and currentTool.usesMaterials then
  353. checkBox3.Visible=true
  354. label4.Visible=true
  355. divider2.Visible=true
  356. desiredSize = desiredSize + UDim2.new(0, 0, 0, 35)
  357. if not materialsDynamic then
  358. desiredSize = desiredSize + UDim2.new(0, 0, 0, 5 + ceil(#materialsTable / 4) * 40) --Dynamically resizes frame if we add more materials later.
  359. end
  360. else
  361. checkBox3.Visible=false
  362. label4.Visible=false
  363. divider2.Visible=false
  364. end
  365. guiFrame.Size = desiredSize
  366. --guiFrame:TweenSize(desiredSize, 'Out', 'Quad', .5) --illegal in studio
  367. end
  368.  
  369.  
  370. function updatePlaneLock()
  371. checkBox1.Style = forcePlaneLock and Enum.ButtonStyle.RobloxRoundButton or Enum.ButtonStyle.RobloxRoundDefaultButton
  372. checkBox1.Text = (planeLock or forcePlaneLock) and 'X' or ''
  373. checkBox1.AutoButtonColor = not forcePlaneLock
  374. if not (planeLock or forcePlaneLock) then
  375. clearGrid()
  376. end
  377. end
  378. checkBox1.MouseButton1Down:connect(function()
  379. planeLock = not planeLock
  380. updatePlaneLock()
  381. end)
  382.  
  383. function updateSnapToGrid()
  384. checkBox2.Style = forceSnapToGrid and Enum.ButtonStyle.RobloxRoundButton or Enum.ButtonStyle.RobloxRoundDefaultButton
  385. checkBox2.Text = (snapToGrid or forceSnapToGrid) and 'X' or ''
  386. checkBox2.AutoButtonColor = not forceSnapToGrid
  387. end
  388. checkBox2.MouseButton1Down:connect(function()
  389. snapToGrid = not snapToGrid
  390. updateSnapToGrid()
  391. end)
  392.  
  393. function updateDynamicMaterial()
  394. isDynamic = dynamicMaterial
  395. if forceDynamicMaterial then
  396. isDynamic = forceDynamicMaterialTo
  397. end
  398. checkBox3.Style = forceDynamicMaterial and Enum.ButtonStyle.RobloxRoundButton or Enum.ButtonStyle.RobloxRoundDefaultButton
  399. checkBox3.AutoButtonColor = not forceDynamicMaterial
  400. checkBox3.Text = isDynamic and 'X' or ''
  401. resizeGuiFrame()
  402. for _, v in pairs(guiFrame:GetChildren()) do
  403. if string.sub(v.Name,1,14) == 'MaterialButton' then
  404. v.Visible = not isDynamic
  405. end
  406. end
  407. end
  408. checkBox3.MouseButton1Down:connect(function()
  409. dynamicMaterial = not dynamicMaterial
  410. updateDynamicMaterial()
  411. end)
  412.  
  413. function updateIgnoreWater()
  414. isIgnoreWater = ignoreWater
  415. if forceIgnoreWater then
  416. isIgnoreWater = forceIgnoreWaterTo
  417. end
  418. checkBox4.Style = forceIgnoreWater and Enum.ButtonStyle.RobloxRoundButton or Enum.ButtonStyle.RobloxRoundDefaultButton
  419. checkBox4.AutoButtonColor = not forceIgnoreWater
  420. checkBox4.Text = isIgnoreWater and 'X' or ''
  421. end
  422. checkBox4.MouseButton1Down:connect(function()
  423. ignoreWater = not ignoreWater
  424. updateIgnoreWater()
  425. end)
  426. -------------
  427.  
  428.  
  429. do
  430. local runService = game:GetService('RunService').RenderStepped
  431. function quickWait(waitTime)
  432. if not waitTime then
  433. runService:wait()
  434. elseif waitTime < .033333 then
  435. local startTick = tick()
  436. runService:wait()
  437. local delta = tick() - startTick
  438. if delta <= waitTime * .5 then
  439. quickWait(waitTime - delta)
  440. end
  441. else
  442. wait(waitTime)
  443. end
  444. end
  445. end
  446.  
  447. function deepCast(origin, endPoint, ignoreList, filterFunction, cubeTerrain)
  448. local ray = Ray.new(origin, endPoint - origin)
  449. local hit, pos, normal, material = game.Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList, cubeTerrain)
  450. if hit and filterFunction(hit) then
  451. table.insert(ignoreList, hit)
  452. return deepCast(pos, endPoint, ignoreList, filterFunction, cubeTerrain)
  453. else
  454. return hit, pos, normal, material
  455. end
  456. end
  457.  
  458. function clearSelection()
  459. if selectionObject then
  460. selectionObject:Destroy()
  461. selectionObject = nil
  462. end
  463. if selectionPart then
  464. selectionPart:Destroy()
  465. selectionPart = nil
  466. end
  467. end
  468.  
  469. function clearGrid()
  470. for i, v in pairs(gridLineParts) do
  471. if v then
  472. v:Destroy()
  473. end
  474. gridLineParts[i] = nil
  475. end
  476. end
  477.  
  478. function drawGrid(point, normal, transparency, color)
  479. local transparency = transparency or .95
  480. local color = BrickColor.new(color or 'Institutional white')--'Pastel light blue')
  481. local gridCellSize = selectionSize * resolution
  482. local gridSize = 10
  483. local baseCframe = CFrame.new(point, point + normal)
  484. local normalSpase = CFrame.new(Vector3.new(0, 0, 0), normal):pointToObjectSpace(point)
  485. local roundedNormalOffset = (Vector3.new((normalSpase.x / gridCellSize) % 1, (normalSpase.y / gridCellSize) % 1, 0) - Vector3.new(.5, .5, 0)) * -gridCellSize
  486. for u = 1, gridSize do
  487. local linePart = gridLineParts[u]
  488. if not linePart then
  489. linePart = Instance.new('Part')
  490. linePart.Transparency = 1
  491. linePart.TopSurface = 'Smooth'
  492. linePart.BottomSurface = 'Smooth'
  493. linePart.Anchored = true
  494. linePart.CanCollide = false
  495. local selectionBox = Instance.new('SelectionBox')
  496. selectionBox.Color = color
  497. selectionBox.Transparency = transparency
  498. selectionBox.Adornee = linePart
  499. selectionBox.Parent = linePart
  500. linePart.Parent = gui
  501. gridLineParts[u] = linePart
  502. elseif linePart.SelectionBox.Transparency ~= transparency or linePart.SelectionBox.Color ~= color then
  503. linePart.SelectionBox.Transparency = transparency
  504. linePart.SelectionBox.Color = color
  505. end
  506. local percent = (u - 1) / (gridSize - 1)
  507. linePart.Size = Vector3.new(gridCellSize * gridSize * sin(math.acos(percent * 1.8 - .9)), 0, 0)
  508. linePart.CFrame = baseCframe * CFrame.new(0, (percent - .5) * (gridSize - 1) * gridCellSize, 0) * CFrame.new(roundedNormalOffset)
  509. end
  510. for u = 1, gridSize do
  511. local linePart = gridLineParts[gridSize + u]
  512. if not linePart then
  513. linePart = Instance.new('Part')
  514. linePart.Transparency = 1
  515. linePart.TopSurface = 'Smooth'
  516. linePart.BottomSurface = 'Smooth'
  517. linePart.Anchored = true
  518. linePart.CanCollide = false
  519. local selectionBox = Instance.new('SelectionBox')
  520. selectionBox.Color = color
  521. selectionBox.Transparency = transparency
  522. selectionBox.Adornee = linePart
  523. selectionBox.Parent = linePart
  524. linePart.Parent = gui
  525. gridLineParts[gridSize + u] = linePart
  526. elseif linePart.SelectionBox.Transparency ~= transparency or linePart.SelectionBox.Color ~= color then
  527. linePart.SelectionBox.Transparency = transparency
  528. linePart.SelectionBox.Color = color
  529. end
  530. local percent = (u - 1) / (gridSize - 1)
  531. linePart.Size = Vector3.new(0, gridCellSize * gridSize * sin(math.acos(percent * 1.8 - .9)), 0)
  532. linePart.CFrame = baseCframe * CFrame.new((percent - .5) * (gridSize - 1) * gridCellSize, 0, 0) * CFrame.new(roundedNormalOffset)
  533. end
  534. end
  535.  
  536. local function getCell(list, x, y, z, materialList)
  537. -- only include materialsList if you want to ignore water
  538. return (materialList and materialList[x] and materialList[x][y] and materialList[x][y][z]) == materialWater and 0
  539. or list and list[x] and list[x][y] and list[x][y][z]
  540. end
  541.  
  542. local function getNeighborOccupancies(list, x, y, z, materialsList, includeSelf)
  543. --only include materialsList if you want to ignore water
  544. local fullNeighbor = false
  545. local emptyNeighbor = false
  546. local neighborOccupancies = includeSelf and getCell(list, x, y, z, materialsList) or 0
  547. local totalNeighbors = includeSelf and 1 or 0
  548. local nearMaterial = materialSelection.enum
  549. for axis = 1, 3 do
  550. for offset = -1, 1, 2 do
  551. local neighbor = nil
  552. local neighborMaterial = nil
  553. if axis == 1 then
  554. neighbor = list[x + offset] and list[x + offset][y][z]
  555. elseif axis == 2 then
  556. neighbor = list[x][y + offset] and list[x][y + offset][z]
  557. elseif axis == 3 then
  558. neighbor = list[x][y][z + offset]
  559. end
  560. if neighbor then
  561. if materialsList then
  562. if axis == 1 then
  563. neighborMaterial = materialsList[x + offset] and materialsList[x + offset][y][z]
  564. elseif axis == 2 then
  565. neighborMaterial = materialsList[x][y + offset] and materialsList[x][y + offset][z]
  566. elseif axis == 3 then
  567. neighborMaterial = materialsList[x][y][z + offset]
  568. end
  569. if neighborMaterial == materialWater then
  570. neighbor = 0
  571. end
  572. end
  573. if neighbor >= 1 then
  574. fullNeighbor = true
  575. end
  576. if neighbor <= 0 then
  577. emptyNeighbor = true
  578. end
  579. totalNeighbors = totalNeighbors + 1
  580. neighborOccupancies = neighborOccupancies + neighbor
  581. end
  582. end
  583. end
  584. return neighborOccupancies / (totalNeighbors ~= 0 and totalNeighbors or getCell(list, x, y, z, materialsList)), fullNeighbor, emptyNeighbor
  585. end
  586.  
  587. local function round(n)
  588. return floor(n + .5)
  589. end
  590.  
  591. function findFace()
  592. local cameraLookVector = game.Workspace.CurrentCamera.CoordinateFrame.lookVector
  593. --[[local absx = abs(cameraLookVector.x) --this code is for 90 plane locking
  594. local absy = abs(cameraLookVector.y)
  595. local absz = abs(cameraLookVector.z)
  596. if absy >= absx and absy >= absz then --preference towards y axis planes
  597. return Vector3.new(0, cameraLookVector.y / absy, 0)
  598. elseif absx >= absz then
  599. return Vector3.new(cameraLookVector.x / absx, 0, 0)
  600. end
  601. return Vector3.new(0, 0, cameraLookVector.z / absz)]]
  602.  
  603. return Vector3.new(round(cameraLookVector.x), round(cameraLookVector.y), round(cameraLookVector.z)).unit --this code is for 45 degree plane locking
  604. end
  605.  
  606. function lineToPlaneIntersection(linePoint, lineDirection, planePoint, planeNormal)
  607. local denominator = lineDirection:Dot(planeNormal)
  608. if denominator == 0 then
  609. return linePoint
  610. end
  611. local distance = ((planePoint - linePoint):Dot(planeNormal)) / denominator
  612. return linePoint + lineDirection * distance
  613. end
  614.  
  615. function updateUsabilityLocks()
  616. if currentTool then
  617. forceSnapToGrid = currentTool.usesMaterials and materialSelection.forceSnapToGrid
  618. updateSnapToGrid()
  619.  
  620. forcePlaneLock = currentTool.name == 'Add' or currentTool.name == 'Subtract'
  621. updatePlaneLock()
  622.  
  623. forceDynamicMaterial = currentTool.name == 'Subtract' or currentTool.name == 'Erode' or currentTool.name == 'Paint' or currentTool.name == 'Smooth' or currentTool.name == 'Smoother'
  624. forceDynamicMaterialTo = not (forceDynamicMaterial and currentTool.name == 'Paint')
  625.  
  626. isDynamic = dynamicMaterial
  627. if forceDynamicMaterial then
  628. isDynamic = forceDynamicMaterialTo
  629. end
  630.  
  631. forceIgnoreWater = (materialSelection.forceIgnoreWater and not isDynamic) or currentTool.name == 'Smooth'
  632. if materialSelection.forceIgnoreWater then
  633. forceIgnoreWaterTo = materialSelection.forceIgnoreWaterTo
  634. end
  635.  
  636. if forceIgnoreWater and currentTool.name == 'Smooth' then
  637. forceIgnoreWaterTo = false
  638. end
  639.  
  640. isIgnoreWater = ignoreWater
  641. if forceIgnoreWater then
  642. isIgnoreWater = forceIgnoreWaterTo
  643. end
  644.  
  645. updateIgnoreWater()
  646. updateDynamicMaterial()
  647. end
  648. end
  649.  
  650. function operation(centerPoint)
  651. local desiredMaterial = isDynamic and nearMaterial or materialSelection.enum
  652.  
  653. local radius = selectionSize * .5 * resolution
  654. local minBounds = Vector3.new(
  655. floor((centerPoint.x - radius) / resolution) * resolution,
  656. floor((centerPoint.y - radius) / resolution) * resolution,
  657. floor((centerPoint.z - radius) / resolution) * resolution)
  658. local maxBounds = Vector3.new(
  659. ceil((centerPoint.x + radius) / resolution) * resolution,
  660. ceil((centerPoint.y + radius) / resolution) * resolution,
  661. ceil((centerPoint.z + radius) / resolution) * resolution)
  662. local region = Region3.new(minBounds, maxBounds)
  663.  
  664. local materials, occupancies = terrain:ReadVoxels(region, resolution)
  665.  
  666. if modules[currentTool.name] then
  667. if modules[currentTool.name]['operation'] then
  668.  
  669. local middle = materials[ceil(#materials * .5)] --This little section of code sets nearMaterial to middle of matrix
  670. if middle then --dig X
  671. local middle = middle[ceil(#middle * .5)]
  672. if middle then --dig Y
  673. local middle = middle[ceil(#middle * .5)]
  674. if middle and middle ~= materialAir and middle ~= materialWater then --dig Z
  675. nearMaterial = middle
  676. desiredMaterial = isDynamic and nearMaterial or desiredMaterial
  677. end
  678. end
  679. end
  680.  
  681. modules[currentTool.name]['operation'](centerPoint, materials, occupancies, resolution, selectionSize, strength, desiredMaterial, brushShape, minBounds, maxBounds)
  682. end
  683. else
  684. local airFillerMaterial = materialAir
  685. local waterHeight = 0
  686. if isIgnoreWater and (currentTool.name == 'Erode' or currentTool.name == 'Subtract') then
  687. --[[local centerPointCell = Vector3.new(floor((centerPoint.x+.5)/resolution) * resolution, floor((centerPoint.y+.5)/resolution) * resolution, floor((centerPoint.z+.5)/resolution) * resolution)
  688. local sampleRegion = Region3.new(centerPointCell - Vector3.new(resolution,resolution,resolution), centerPointCell + Vector3.new(resolution,resolution,resolution))
  689. local sampleMaterials, sampleOccupancies = terrain:ReadVoxels(sampleRegion, resolution)]]
  690.  
  691. for ix,vx in ipairs(materials) do
  692. for iy,vy in ipairs(vx) do
  693. for iz, vz in ipairs(vy) do
  694. if vz == materialWater then
  695. airFillerMaterial = materialWater
  696. if iy > waterHeight then
  697. waterHeight = iy
  698. end
  699. end
  700. end
  701. end
  702. end
  703. end
  704.  
  705. for ix, vx in ipairs(occupancies) do
  706. local cellVectorX = minBounds.x + (ix - .5) * resolution - centerPoint.x
  707.  
  708. for iy, vy in pairs(vx) do
  709. local cellVectorY = minBounds.y + (iy - .5) * resolution - centerPoint.y
  710.  
  711. for iz, cellOccupancy in pairs(vy) do
  712. local cellVectorZ = minBounds.z + (iz - .5) * resolution - centerPoint.z
  713.  
  714. local cellMaterial = materials[ix][iy][iz]
  715. local distance = sqrt(cellVectorX * cellVectorX + cellVectorY * cellVectorY + cellVectorZ * cellVectorZ)
  716.  
  717. local magnitudePercent = 1
  718. local brushOccupancy = 1
  719. if brushShape == 'Sphere' then
  720. magnitudePercent = cos(min(1, distance / (radius + resolution * .5)) * pi * .5)
  721. brushOccupancy = max(0, min(1, (radius + .5 * resolution - distance) / resolution))
  722. elseif brushShape == 'Box' then
  723. if not (snapToGrid or forceSnapToGrid) then
  724. local xOutside = 1 - max(0, abs(cellVectorX / resolution) + .5 - selectionSize * .5)
  725. local yOutside = 1 - max(0, abs(cellVectorY / resolution) + .5 - selectionSize * .5)
  726. local zOutside = 1 - max(0, abs(cellVectorZ / resolution) + .5 - selectionSize * .5)
  727. brushOccupancy = xOutside * yOutside * zOutside
  728. end
  729. end
  730.  
  731. if cellMaterial ~= materialAir and cellMaterial ~= materialWater and cellMaterial ~= nearMaterial then
  732. nearMaterial = cellMaterial
  733. if isDynamic then
  734. desiredMaterial = nearMaterial
  735. end
  736. end
  737.  
  738. if isIgnoreWater and cellMaterial == materialWater then
  739. cellMaterial = materialAir
  740. cellOccupancy = 0
  741. end
  742. local airFillerMaterial = waterHeight >= iy and airFillerMaterial or materialAir
  743.  
  744. if currentTool.name == 'Add' then
  745. if selectionSize <= 2 then
  746. if brushOccupancy >= .5 then
  747. if cellMaterial == materialAir or cellOccupancy <= 0 then
  748. materials[ix][iy][iz] = desiredMaterial
  749. end
  750. occupancies[ix][iy][iz] = 1
  751. end
  752. else
  753. if brushOccupancy > cellOccupancy then
  754. occupancies[ix][iy][iz] = brushOccupancy
  755. end
  756. if brushOccupancy >= .5 and cellMaterial == materialAir then
  757. materials[ix][iy][iz] = desiredMaterial
  758. end
  759. end
  760. elseif currentTool.name == 'Subtract' then
  761. if cellMaterial ~= materialAir then
  762. if selectionSize <= 2 then
  763. if brushOccupancy >= .5 then
  764. occupancies[ix][iy][iz] = airFillerMaterial == materialWater and 1 or 0
  765. materials[ix][iy][iz] = airFillerMaterial
  766. end
  767. else
  768. local desiredOccupancy = max(0,1 - brushOccupancy)
  769. if desiredOccupancy < cellOccupancy then
  770. if desiredOccupancy <= one256th then
  771. occupancies[ix][iy][iz] = airFillerMaterial == materialWater and 1 or 0
  772. materials[ix][iy][iz] = airFillerMaterial
  773. else
  774. occupancies[ix][iy][iz] = min(cellOccupancy, desiredOccupancy)
  775. end
  776. end
  777. end
  778. end
  779. elseif currentTool.name == 'Grow' then
  780. if brushOccupancy >= .5 then --working on
  781. local desiredOccupancy = cellOccupancy
  782. local neighborOccupancies, fullNeighbor, emptyNeighbor = getNeighborOccupancies(occupancies, ix, iy, iz, isIgnoreWater and materials)
  783. if cellOccupancy > 0 or fullNeighbor then --problem if selection size is small.
  784. desiredOccupancy = desiredOccupancy + neighborOccupancies * (strength + .1) * .25 * brushOccupancy * magnitudePercent
  785. end
  786. if cellMaterial == materialAir and desiredOccupancy > 0 then
  787. materials[ix][iy][iz] = desiredMaterial
  788. end
  789. if desiredOccupancy ~= cellOccupancy then
  790. occupancies[ix][iy][iz] = desiredOccupancy
  791. end
  792. end
  793. elseif currentTool.name == 'Erode' then
  794. if cellMaterial ~= materialAir then
  795. local flippedBrushOccupancy = 1 - brushOccupancy
  796. if flippedBrushOccupancy <= .5 then
  797. local desiredOccupancy = cellOccupancy
  798. local emptyNeighbor = false
  799. local neighborOccupancies = 6
  800. for axis = 1, 3 do
  801. for offset = -1, 1, 2 do
  802. local neighbor = nil
  803. local neighborMaterial = nil
  804. if axis == 1 then
  805. neighbor = occupancies[ix + offset] and occupancies[ix + offset][iy][iz]
  806. neighborMaterial = materials[ix + offset] and materials[ix + offset][iy][iz]
  807. elseif axis == 2 then
  808. neighbor = occupancies[ix][iy + offset] and occupancies[ix][iy + offset][iz]
  809. neighborMaterial = materials[ix][iy + offset] and materials[ix][iy + offset][iz]
  810. elseif axis == 3 then
  811. neighbor = occupancies[ix][iy][iz + offset]
  812. neighborMaterial = materials[ix][iy][iz + offset]
  813. end
  814. if neighbor then
  815. if isIgnoreWater and neighborMaterial == materialWater then
  816. neighbor = 0
  817. end
  818. if neighbor <= 0 then
  819. emptyNeighbor = true
  820. end
  821. neighborOccupancies = neighborOccupancies - neighbor
  822. end
  823. end
  824. end
  825. if cellOccupancy < 1 or emptyNeighbor then
  826. desiredOccupancy = max(0,desiredOccupancy - (neighborOccupancies / 6) * (strength + .1) * .25 * brushOccupancy * magnitudePercent)
  827. end
  828. if desiredOccupancy <= one256th then
  829. occupancies[ix][iy][iz] = airFillerMaterial == materialWater and 1 or 0
  830. materials[ix][iy][iz] = airFillerMaterial
  831. else
  832. occupancies[ix][iy][iz] = desiredOccupancy
  833. end
  834. end
  835. end
  836. elseif currentTool.name == 'Paint' then
  837. if brushOccupancy > 0 and cellOccupancy > 0 then
  838. materials[ix][iy][iz] = desiredMaterial
  839. end
  840. end
  841. end
  842. end
  843. end
  844. end
  845. terrain:WriteVoxels(region, resolution, materials, occupancies)
  846. end
  847.  
  848.  
  849. function Selected(tool)
  850. if plugin then
  851. plugin:Activate(true)
  852. end
  853. if tool.button then
  854. tool.button:SetActive(true)
  855. lastTool = tool
  856. end
  857. if not userInput.MouseEnabled then
  858. prevCameraType = game.Workspace.CurrentCamera.CameraType
  859. game.Workspace.CurrentCamera.CameraType = Enum.CameraType.Fixed
  860. end
  861. on = true
  862. currentTool = tool
  863.  
  864. updateUsabilityLocks()
  865.  
  866. if modules[tool.name] and modules[tool.name]['On'] then
  867. modules[tool.name].On(mouse,Deselected)
  868. end
  869. if not modules[tool.name] or modules[tool.name]['operation'] then
  870.  
  871. resizeGuiFrame()
  872. titlelabel.Text = tool.name
  873. gui.Parent = coreGui
  874. gui.Frame.Visible = true
  875.  
  876. local loopTag = {} --using table as a unique value for debouncing
  877. currentLoopTag = loopTag
  878.  
  879. while currentLoopTag and currentLoopTag == loopTag do
  880. local t = tick()
  881. local radius = selectionSize * .5 * resolution
  882. local cameraPos = mouse.Origin.p
  883.  
  884. local ignoreModel = nil
  885. if game.Players.LocalPlayer and game.Players.LocalPlayer.Character then
  886. ignoreModel = game.Players.LocalPlayer.Character
  887. end
  888. local mouseRay = Ray.new(cameraPos, mouse.UnitRay.Direction*10000)
  889. local hitObject, mainPoint = game.Workspace:FindPartOnRay(mouseRay, ignoreModel, false, isIgnoreWater)
  890.  
  891. if tool.name == 'Add' then
  892. mainPoint = mainPoint - mouse.UnitRay.Direction * .05
  893. elseif tool.name == 'Subtract' or tool.name == 'Paint' or tool.name == 'Grow' then
  894. mainPoint = mainPoint + mouse.UnitRay.Direction * .05
  895. end
  896.  
  897. if mouse.Target == nil then --cage the cursor so that it does not fly away
  898. mainPoint = cameraPos + mouse.UnitRay.Direction * lastCursorDistance --limits the distance of the mainPoint if the mouse is not hitting an object
  899. end
  900.  
  901. if not mouseDown or click then
  902. lastPlanePoint = mainPoint
  903. lastNormal = findFace()
  904. end
  905.  
  906. if planeLock or forcePlaneLock then
  907. mainPoint = lineToPlaneIntersection(cameraPos, mouse.UnitRay.Direction, lastPlanePoint, lastNormal)
  908. end
  909.  
  910. if snapToGrid or forceSnapToGrid then
  911. local snapOffset = Vector3.new(1, 1, 1) * (radius % resolution) --in studs
  912. local tempMainPoint = (mainPoint - snapOffset) / resolution + Vector3.new(.5, .5, .5) --in voxels
  913. mainPoint = Vector3.new(floor(tempMainPoint.x), floor(tempMainPoint.y), floor(tempMainPoint.z)) * resolution + snapOffset
  914. end
  915.  
  916. if mouseDown then
  917. if click then
  918. firstOperation = t
  919. lastMainPoint = mainPoint
  920. end
  921.  
  922. if click or t > firstOperation + clickThreshold then
  923. click = false
  924. if downKeys[Enum.KeyCode.LeftAlt] or downKeys[Enum.KeyCode.RightAlt] then
  925. --pick color
  926. local function filterNonTerrain(thing)
  927. if thing and thing == terrain then
  928. return false
  929. end
  930. return true
  931. end
  932. local hit, hitPosition, normal, foundMaterial = deepCast(cameraPos, cameraPos + mouse.UnitRay.Direction*10000, {}, filterNonTerrain, true)
  933. if hit then
  934. for _, materialTable in pairs(materialsTable) do
  935. if materialTable.enum == foundMaterial then
  936. setMaterialSelection(materialTable)
  937. break
  938. end
  939. end
  940. end
  941. else
  942. local difference = mainPoint - lastMainPoint
  943. local dragDistance = (difference).magnitude
  944. local crawlDistance = radius * .5 --Maybe adjustable setting? Considering using a different method of crawling, with a percent rather than a finite distance.
  945. if dragDistance > crawlDistance then
  946. local differenceVector = difference.unit
  947. local dragDistance = min(dragDistance, crawlDistance * 2 + 20) --limiting this so that it does not attempt too many operations within a single drag.
  948. local samples = ceil(dragDistance / crawlDistance - .1)
  949. for i = 1, samples do
  950. operation(lastMainPoint + differenceVector * dragDistance * (i / samples))
  951. end
  952. mainPoint = lastMainPoint + differenceVector * dragDistance
  953. else
  954. operation(mainPoint)
  955. end
  956. lastMainPoint = mainPoint
  957. end
  958. end
  959. end
  960.  
  961. if not selectionPart then
  962. selectionPart = Instance.new('Part')
  963. selectionPart.Name = 'SelectionPart'
  964. selectionPart.Transparency = 1
  965. selectionPart.TopSurface = 'Smooth'
  966. selectionPart.BottomSurface = 'Smooth'
  967. selectionPart.Anchored = true
  968. selectionPart.CanCollide = false
  969. selectionPart.Size = Vector3.new(1, 1, 1) * selectionSize * resolution + Vector3.new(.1, .1, .1)
  970. selectionPart.Parent = gui
  971. end
  972. if not selectionObject then
  973. selectionObject = Instance.new(brushShape == 'Sphere' and 'SelectionSphere' or 'SelectionBox')
  974. selectionObject.Name = 'SelectionObject'
  975. selectionObject.Color = BrickColor.new('Toothpaste')
  976. selectionObject.SurfaceTransparency = .95 - strength * .3
  977. selectionObject.SurfaceColor = BrickColor.new('Toothpaste')
  978. selectionObject.Adornee = selectionPart
  979. selectionObject.Parent = selectionPart
  980. end
  981.  
  982. if not userInput.TouchEnabled or mouseDown then
  983. selectionPart.CFrame = CFrame.new(mainPoint)
  984.  
  985. if planeLock or forcePlaneLock then
  986. local mainPointIntersect = lineToPlaneIntersection(mainPoint, mouse.UnitRay.Direction, lastPlanePoint, lastNormal) --we need to get this otherwise the plane can shift whiel drawing
  987. drawGrid(mainPointIntersect, lastNormal, mouseDown and .8)
  988. end
  989. end
  990.  
  991. lastCursorDistance = max(20 + selectionSize * resolution * 1.5,(mainPoint - cameraPos).magnitude)
  992.  
  993. quickWait()
  994. end
  995. end
  996. end
  997.  
  998. function Deselected()
  999. if not userInput.MouseEnabled then
  1000. game.Workspace.CurrentCamera.CameraType = prevCameraType
  1001. end
  1002. currentLoopTag = nil
  1003. gui.Parent = script.Parent
  1004. gui.Frame.Visible = false
  1005.  
  1006. clearSelection()
  1007. clearGrid()
  1008. if lastTool then
  1009. lastTool.button:SetActive(false)
  1010. end
  1011. mouseDown = false
  1012. on = false
  1013. local lastCurrentTool = currentTool
  1014. currentTool = nil
  1015. if lastCurrentTool and modules[lastCurrentTool.name] and modules[lastCurrentTool.name]['Off'] then
  1016. modules[lastCurrentTool.name].Off()
  1017. end
  1018. end
  1019.  
  1020. closeButton.MouseButton1Down:connect(Deselected)
  1021.  
  1022. --Touch controls
  1023. local fingerTouches = {}
  1024. local NumUnsunkTouches = 0
  1025.  
  1026. local StartingDiff = nil
  1027. local startingSelectionSize = nil
  1028.  
  1029. local function OnTouchBegan(input, processed)
  1030. fingerTouches[input] = processed
  1031. if not processed then
  1032. click = true
  1033. NumUnsunkTouches = NumUnsunkTouches + 1
  1034. end
  1035. end
  1036.  
  1037. local function OnTouchChanged(input, processed)
  1038. if fingerTouches[input] == nil then
  1039. fingerTouches[input] = processed
  1040. if not processed then
  1041. NumUnsunkTouches = NumUnsunkTouches + 1
  1042. end
  1043. end
  1044.  
  1045. if NumUnsunkTouches == 1 then
  1046. if fingerTouches[input] == false then
  1047. mouseDown = true
  1048. end
  1049. else
  1050. mouseDown = false
  1051. end
  1052. if NumUnsunkTouches == 2 then
  1053. local unsunkTouches = {}
  1054. for touch, wasSunk in pairs(fingerTouches) do
  1055. if not wasSunk then
  1056. table.insert(unsunkTouches, touch)
  1057. end
  1058. end
  1059. if #unsunkTouches == 2 then
  1060. local difference = (unsunkTouches[1].Position - unsunkTouches[2].Position).magnitude
  1061. if StartingDiff and startingSelectionSize then
  1062. local scale = difference/max(0.01, StartingDiff)
  1063. selectionSize = max(minSelectionSize, min(maxSelectionSize, startingSelectionSize/scale))
  1064. selectionSizeValue.Value = selectionSize
  1065. else
  1066. StartingDiff = difference
  1067. startingSelectionSize = selectionSizeValue.Value
  1068. end
  1069. end
  1070. else
  1071. StartingDiff = nil
  1072. startingSelectionSize = nil
  1073. end
  1074. end
  1075.  
  1076. local function OnTouchEnded(input, processed)
  1077. if fingerTouches[input] == false then
  1078. if NumUnsunkTouches == 1 then
  1079. mouseDown = false
  1080. elseif NumUnsunkTouches == 2 then
  1081. StartingDiff = nil
  1082. startingSelectionSize = nil
  1083. mouseDown = true
  1084. end
  1085. end
  1086.  
  1087. if fingerTouches[input] ~= nil and fingerTouches[input] == false then
  1088. NumUnsunkTouches = NumUnsunkTouches - 1
  1089. end
  1090. fingerTouches[input] = nil
  1091. end
  1092.  
  1093. -- Input Handling
  1094. userInput.InputBegan:connect(function(event, soaked)
  1095. downKeys[event.KeyCode] = true
  1096. if event.UserInputType == Enum.UserInputType.MouseButton1 and not soaked and on then
  1097. mouseDown = true
  1098. click = true
  1099. elseif event.UserInputType == Enum.UserInputType.Touch and on then
  1100. OnTouchBegan(event, soaked)
  1101. end
  1102. end)
  1103.  
  1104. userInput.InputChanged:connect(function(input, processed)
  1105. if input.UserInputType == Enum.UserInputType.Touch then
  1106. OnTouchChanged(input, processed)
  1107. end
  1108. end)
  1109.  
  1110. userInput.InputEnded:connect(function(event, soaked)
  1111. downKeys[event.KeyCode] = nil
  1112. if event.UserInputType == Enum.UserInputType.MouseButton1 and mouseDown then
  1113. mouseDown = false
  1114. if changeHistory then
  1115. changeHistory:SetWaypoint('Terrain '..currentTool.name)
  1116. end
  1117. elseif event.UserInputType == Enum.UserInputType.Touch then
  1118. OnTouchEnded(event, soaked)
  1119. end
  1120. end)
  1121.  
  1122. function scrollwheel(change)
  1123. if on then
  1124. if downKeys[Enum.KeyCode.LeftShift] or downKeys[Enum.KeyCode.RightShift] then
  1125. selectionSize = max(minSelectionSize, min(maxSelectionSize, selectionSize + change))
  1126. selectionSizeValue.Value = selectionSize
  1127. end
  1128. if downKeys[Enum.KeyCode.LeftControl] or downKeys[Enum.KeyCode.RightControl] then
  1129. strength = max(0, min(1, strength + change * (1/(maxSelectionSize-minSelectionSize))))
  1130. strengthValue.Value = round(strength * 100 + 1)
  1131. end
  1132. end
  1133. end
  1134.  
  1135. mouse.WheelForward:connect(function()
  1136. scrollwheel(1)
  1137. end)
  1138.  
  1139. mouse.WheelBackward:connect(function()
  1140. scrollwheel(-1)
  1141. end)
  1142.  
  1143. if plugin then
  1144. plugin.Deactivation:connect(function()
  1145. if on then
  1146. Deselected()
  1147. end
  1148. end)
  1149. end
  1150.  
  1151. setBrushShape(brushShape)
  1152. setMaterialSelection(materialSelection)
  1153. updatePlaneLock()
  1154. updateSnapToGrid()
  1155. updateDynamicMaterial()
  1156.  
  1157. -- Reset keyboard status on lost focus as key release may never come blocked by popups etc.
  1158. userInput.WindowFocusReleased:connect(function()
  1159. downKeys = {}
  1160. end)
  1161.  
  1162. end
  1163.  
  1164.  
  1165.  
  1166.  
  1167.  
  1168. local terrain = game.Workspace.Terrain
  1169.  
  1170.  
  1171. local function getCell(list, x, y, z)
  1172. return list and list[x] and list[x][y] and list[x][y][z]
  1173. end
  1174.  
  1175. local function extendRange(x) --This is very important. It allows cells to fully diminish or fully fill by lying to the algorithm
  1176. return x * 1.5 - .25
  1177. end
  1178.  
  1179. function getNeighborOccupanciesFast(list, x, y, z, includeSelf)
  1180. local fullNeighbor = false
  1181. local emptyNeighbor = false
  1182. local neighborOccupancies = includeSelf and extendRange(getCell(list, x, y, z)) or 0
  1183. local totalNeighbors = includeSelf and 1 or 0
  1184. for axis = 1, 3 do
  1185. for offset = -1, 1, 2 do
  1186. local neighbor = nil
  1187. if axis == 1 then
  1188. neighbor = list[x + offset] and list[x + offset][y][z]
  1189. elseif axis == 2 then
  1190. neighbor = list[x][y + offset] and list[x][y + offset][z]
  1191. elseif axis == 3 then
  1192. neighbor = list[x][y][z + offset]
  1193. end
  1194. if neighbor then
  1195. if neighbor >= 1 then
  1196. fullNeighbor = true
  1197. end
  1198. if neighbor <= 0 then
  1199. emptyNeighbor = true
  1200. end
  1201. neighbor = extendRange(neighbor)
  1202. totalNeighbors = totalNeighbors + 1
  1203. neighborOccupancies = neighborOccupancies + neighbor
  1204. end
  1205. end
  1206. end
  1207. return neighborOccupancies / (totalNeighbors > 0 and totalNeighbors or extendRange(getCell(list, x, y, z))), fullNeighbor, emptyNeighbor
  1208. end
  1209.  
  1210. function getNeighborOccupancies(list, x, y, z, includeSelf, range)
  1211. local fullNeighbor = false
  1212. local emptyNeighbor = false
  1213. local range = range or 1
  1214. local neighborOccupancies = 0
  1215. local totalNeighbors = 0
  1216. local sqrt = math.sqrt
  1217. for ix = -range, range do
  1218. for iy = -range, range do
  1219. for iz = -range, range do
  1220. if includeSelf or not (ix == 0 and iy == 0 and iz == 0) then
  1221. local neighbor = getCell(list, x + ix, y + iy, z + iz)
  1222. if neighbor then
  1223. local distanceScale = 1 - (sqrt(ix * ix + iy * iy + iz * iz) / (range * 2))
  1224. if neighbor >= 1 then
  1225. fullNeighbor = true
  1226. end
  1227. if neighbor <= 0 then
  1228. emptyNeighbor = true
  1229. end
  1230. neighbor = extendRange(neighbor)
  1231. totalNeighbors = totalNeighbors + 1 * distanceScale
  1232. neighborOccupancies = neighborOccupancies + neighbor * distanceScale
  1233. end
  1234. end
  1235. end
  1236. end
  1237. end
  1238. return neighborOccupancies / (totalNeighbors > 0 and totalNeighbors or extendRange(getCell(list, x, y, z))), fullNeighbor, emptyNeighbor
  1239. end
  1240.  
  1241.  
  1242. function operation(centerPoint, materials, occupancies, resolution, selectionSize, strength, desiredMaterial, brushType, minBounds, maxBounds)
  1243. local region = Region3.new(minBounds, maxBounds)
  1244. local readMaterials, readOccupancies = terrain:ReadVoxels(region, resolution)
  1245.  
  1246. local radius = selectionSize * .5 * resolution
  1247.  
  1248. local materialAir = Enum.Material.Air
  1249.  
  1250. for ix, vx in ipairs(readOccupancies) do
  1251. local cellVectorX = minBounds.x + (ix - .5) * resolution - centerPoint.x
  1252.  
  1253. for iy, vy in pairs(vx) do
  1254. local cellVectorY = minBounds.y + (iy - .5) * resolution - centerPoint.y
  1255.  
  1256. for iz, cellOccupancy in pairs(vy) do
  1257. local cellVectorZ = minBounds.z + (iz - .5) * resolution - centerPoint.z
  1258.  
  1259. local cellMaterial = materials[ix][iy][iz]
  1260. local distance = math.sqrt(cellVectorX * cellVectorX + cellVectorY * cellVectorY + cellVectorZ * cellVectorZ)
  1261.  
  1262. local magnitudePercent = 1
  1263. local brushOccupancy = 1
  1264. if brushType == 'Sphere' then
  1265. magnitudePercent = math.cos(math.min(1, distance / (radius + resolution * .5)) * math.pi * .5)
  1266. brushOccupancy = math.max(0, math.min(1, (radius + .5 * resolution - distance) / resolution))
  1267. elseif brushType == 'Box' then
  1268. --leave as default
  1269. end
  1270.  
  1271. if brushOccupancy >= .5 then
  1272. local neighborOccupancies, fullNeighbor, emptyNeighbor = getNeighborOccupancies(readOccupancies, ix, iy, iz, true, 1)
  1273. local difference = (neighborOccupancies - cellOccupancy) * (strength + .1) * .5 * brushOccupancy * magnitudePercent
  1274.  
  1275. if not fullNeighbor and difference > 0 then
  1276. difference = 0
  1277. elseif not emptyNeighbor and difference < 0 then
  1278. difference = 0
  1279. end
  1280.  
  1281. if readMaterials[ix][iy][iz] == materialAir or cellOccupancy <= 0 and difference > 0 then
  1282. materials[ix][iy][iz] = desiredMaterial
  1283. end
  1284. if difference ~= 0 then
  1285. occupancies[ix][iy][iz] = math.max(0, math.min(1, cellOccupancy + difference))
  1286. end
  1287. end
  1288. end
  1289. end
  1290. end
  1291. end
  1292.  
  1293. return {
  1294. ['operation'] = operation
  1295. }
  1296.  
  1297. local Terrain = workspace:WaitForChild('Terrain', 86400) or workspace:WaitForChild('Terrain')
  1298. while not Terrain.IsSmooth do
  1299. Terrain.Changed:wait()
  1300. end
  1301.  
  1302. local on = false
  1303. local setup = false
  1304. local currentTool = 'Create'
  1305.  
  1306.  
  1307. --[[local toolBar = plugin:CreateToolbar('Smooth Terrain')
  1308. regionEditButton = toolBar:CreateButton(
  1309. 'Region Edit', --button title
  1310. 'Manipulate regions of smoothterrain.', --hover text
  1311. 'http://www.roblox.com/asset/?id=180084957' --icon
  1312. )
  1313. regionEditButton.Click:connect(function()
  1314. if not on then
  1315. if not setup then
  1316. FirstTimeSetUp()
  1317. end
  1318. Selected()
  1319. else
  1320. Deselected()
  1321. end
  1322. end)]]
  1323.  
  1324.  
  1325. function FirstTimeSetUp()
  1326. setup = true
  1327. local changeHistory = game:GetService('ChangeHistoryService')
  1328. local terrain = game.Workspace.Terrain
  1329. local utilityModule = require(script.Parent.Utility)()
  1330. local coreGui = utilityModule:GetUsingAsPlugin() and game:GetService('CoreGui') or game.Players.LocalPlayer:WaitForChild('PlayerGui')
  1331. local gui = script.Parent:WaitForChild('TerrainRegionGui')
  1332. local guiFrame = gui:WaitForChild('Frame')
  1333. local closeButton = guiFrame:WaitForChild('CloseButton')
  1334. local buttonFillAir = guiFrame:WaitForChild('ButtonFillAir')
  1335. local buttonFillWater = guiFrame:WaitForChild('ButtonFillWater')
  1336. local buttonSelect = guiFrame:WaitForChild('ButtonSelect')
  1337. local buttonMove = guiFrame:WaitForChild('ButtonMove')
  1338. local buttonResize = guiFrame:WaitForChild('ButtonResize')
  1339. local buttonRotate = guiFrame:WaitForChild('ButtonRotate')
  1340. local buttonCopy = guiFrame:WaitForChild('ButtonCopy')
  1341. local buttonPaste = guiFrame:WaitForChild('ButtonPaste')
  1342. local buttonDelete = guiFrame:WaitForChild('ButtonDelete')
  1343. local buttonFill = guiFrame:WaitForChild('ButtonFill')
  1344. local fillFrame = gui:WaitForChild('FillFrame')
  1345. local materialsTable = require(script.Parent.MaterialsList)
  1346. local buttonFillFrameClose = fillFrame:WaitForChild('CloseButton')
  1347. local buttonFillConfirm = fillFrame:WaitForChild('ButtonFillConfirm')
  1348. local library = assert(LoadLibrary('RbxGui'))
  1349. --local mouse = plugin:GetMouse()
  1350.  
  1351. ----SETTINGS----
  1352. local mode = 'Select' --Select, Edit
  1353. local tool = 'None' --None, Resize, Move, Rotate
  1354. local button = 'Select' --Select, Move, Resize, Rotate
  1355. local fillAir = true
  1356. local fillWater = true
  1357. ----------------
  1358.  
  1359. --SUB SETTINGS--
  1360. local resolution = 4 --This is the size of voxels on Roblox. Why is this a variable? ;)
  1361. local textSelectColor = Color3.new(72/255, 145/255, 212/255)
  1362. local white = Color3.new(238/255, 238/255, 238/255)
  1363. local editColor1 = 'Institutional white'
  1364. local editColor2 = 'Light stone grey'
  1365. local rotationInterval = math.pi * .5
  1366. local regionLengthLimit = 125
  1367. ----------------
  1368.  
  1369. --Variables--
  1370. local faceToNormal = {
  1371. [Enum.NormalId.Top] = Vector3.new(0, 1, 0),
  1372. [Enum.NormalId.Bottom] = Vector3.new(0, -1, 0),
  1373. [Enum.NormalId.Left] = Vector3.new(-1, 0, 0),
  1374. [Enum.NormalId.Right] = Vector3.new(1, 0, 0),
  1375. [Enum.NormalId.Front] = Vector3.new(0, 0, -1),
  1376. [Enum.NormalId.Back] = Vector3.new(0, 0, 1),
  1377. }
  1378. local undefined=0/0
  1379. local selectionStart = nil
  1380. local selectionEnd = nil
  1381. local selectionPart = nil
  1382. local selectionObject = nil
  1383. local selectionHandles = nil
  1384. local materialSelection = materialsTable[5]
  1385. local downLoop = nil
  1386. local clickStart = Vector3.new(0, 0, 0)
  1387. local dragVector = nil
  1388. local dragStart = true
  1389. local lockedMaterials, lockedOccupancies = nil, nil
  1390. local lockedRegion = nil
  1391. local behindThis = nil
  1392. local axis = 'X'
  1393. local materialAir = Enum.Material.Air
  1394. local materialWater = Enum.Material.Water
  1395. local floor = math.floor --Scaling linear resize
  1396. local ceil = math.ceil
  1397. -------------
  1398.  
  1399.  
  1400. local function setMaterialSelection(newMaterialSelection)
  1401. materialSelection = newMaterialSelection
  1402. for _, v in pairs(fillFrame:GetChildren()) do
  1403. if string.sub(v.Name,1,14) == 'MaterialButton' then
  1404. if v.Name == 'MaterialButton' .. materialSelection.enum.Name then
  1405. v.BackgroundTransparency = .1
  1406. else
  1407. v.BackgroundTransparency = 1
  1408. end
  1409. end
  1410. end
  1411. end
  1412.  
  1413. for i,materialSubTable in pairs(materialsTable) do
  1414. local newMaterialButton = Instance.new('ImageButton')
  1415. newMaterialButton.Name = 'MaterialButton' .. materialSubTable.enum.Name
  1416. newMaterialButton.BorderSizePixel = 2
  1417. newMaterialButton.BorderColor3 = Color3.new(.2, 1, 1)
  1418. newMaterialButton.BackgroundColor3 = Color3.new(.2, 1, 1)
  1419. newMaterialButton.BackgroundTransparency = 1
  1420. newMaterialButton.Image = materialSubTable.image
  1421. newMaterialButton.Size = UDim2.new(0, 35, 0, 35)
  1422. newMaterialButton.Position = UDim2.new(0, 5 + ((i-1) % 4) * 40, 0, -5 + ceil(i/4) * 40)
  1423. newMaterialButton.MouseButton1Down:connect(function()
  1424. setMaterialSelection(materialsTable[i])
  1425. end)
  1426. newMaterialButton.Parent = fillFrame
  1427. end
  1428. fillFrame.Size = UDim2.new(0, 180, 0, 95 + ceil(#materialsTable / 4) * 40)
  1429. setMaterialSelection(materialSelection)
  1430.  
  1431.  
  1432. function setButton(newButton)
  1433. lockInMap()
  1434. buttonSelect.Style = newButton == 'Select' and Enum.ButtonStyle.RobloxRoundDropdownButton or Enum.ButtonStyle.RobloxRoundDefaultButton
  1435. buttonSelect.TextColor3 = newButton == 'Select' and textSelectColor or white
  1436. buttonMove.Style = newButton == 'Move' and Enum.ButtonStyle.RobloxRoundDropdownButton or Enum.ButtonStyle.RobloxRoundDefaultButton
  1437. buttonMove.TextColor3 = newButton == 'Move' and textSelectColor or white
  1438. buttonResize.Style = newButton == 'Resize' and Enum.ButtonStyle.RobloxRoundDropdownButton or Enum.ButtonStyle.RobloxRoundDefaultButton
  1439. buttonResize.TextColor3 = newButton == 'Resize' and textSelectColor or white
  1440. buttonRotate.Style = newButton == 'Rotate' and Enum.ButtonStyle.RobloxRoundDropdownButton or Enum.ButtonStyle.RobloxRoundDefaultButton
  1441. buttonRotate.TextColor3 = newButton == 'Rotate' and textSelectColor or white
  1442. if newButton == 'Select' then
  1443. mode = 'Select'
  1444. tool = 'Resize'
  1445. elseif newButton == 'Move' then
  1446. mode = 'Edit'
  1447. tool = 'Move'
  1448. elseif newButton == 'Resize' then
  1449. mode = 'Edit'
  1450. tool = 'Resize'
  1451. elseif newButton == 'Rotate' then
  1452. mode = 'Edit'
  1453. tool = 'Rotate'
  1454. end
  1455. button = newButton
  1456. renderSelection()
  1457. end
  1458.  
  1459. buttonSelect.MouseButton1Down:connect(function()
  1460. setButton('Select')
  1461. end)
  1462. buttonMove.MouseButton1Down:connect(function()
  1463. setButton('Move')
  1464. end)
  1465. buttonResize.MouseButton1Down:connect(function()
  1466. setButton('Resize')
  1467. end)
  1468. buttonRotate.MouseButton1Down:connect(function()
  1469. setButton('Rotate')
  1470. end)
  1471.  
  1472. buttonFillAir.MouseButton1Down:connect(function()
  1473. fillAir = not fillAir
  1474. buttonFillAir.Text = fillAir and 'X' or ''
  1475. if button=='Move' or button=='Resize' then
  1476. updateDragOperation()
  1477. elseif button=='Rotate' then
  1478. updateRotateOperation()
  1479. end
  1480. end)
  1481. buttonFillWater.MouseButton1Down:connect(function()
  1482. fillWater = not fillWater
  1483. buttonFillWater.Text = fillWater and 'X' or ''
  1484. if button=='Move' or button=='Resize' then
  1485. updateDragOperation()
  1486. elseif button=='Rotate' then
  1487. updateRotateOperation()
  1488. end
  1489. end)
  1490.  
  1491. 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.
  1492. if selectionStart and selectionEnd then
  1493. local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
  1494. lockedRegion = region
  1495. lockedMaterials, lockedOccupancies = terrain:ReadVoxels(region, resolution)
  1496. end
  1497. end
  1498.  
  1499. do
  1500. local runService = game:GetService('RunService').RenderStepped
  1501. function quickWait(waitTime)
  1502. if not waitTime then
  1503. runService:wait()
  1504. elseif waitTime < .033333 then
  1505. local startTick = tick()
  1506. runService:wait()
  1507. local delta = tick() - startTick
  1508. if delta <= waitTime * .5 then
  1509. quickWait(waitTime - delta)
  1510. end
  1511. else
  1512. wait(waitTime)
  1513. end
  1514. end
  1515. end
  1516.  
  1517. function clearSelection()
  1518. if selectionArcHandles then
  1519. selectionArcHandles:Destroy()
  1520. selectionArcHandles = nil
  1521. end
  1522. if selectionHandles then
  1523. selectionHandles:Destroy()
  1524. selectionHandles = nil
  1525. end
  1526. if selectionObject then
  1527. selectionObject:Destroy()
  1528. selectionObject = nil
  1529. end
  1530. if selectionPart then
  1531. selectionPart:Destroy()
  1532. selectionPart = nil
  1533. end
  1534. end
  1535.  
  1536. local function round(n)
  1537. return n + .5 - ((n + .5)%1)
  1538. end
  1539.  
  1540. local function positionWorldToVoxel(pos)
  1541. return Vector3.new(ceil(pos.x / resolution), ceil(pos.y / resolution), ceil(pos.z / resolution))
  1542. end
  1543.  
  1544. local function make3DTable(size,fill)
  1545. local size = size or Vector3.new(1,1,1)
  1546. local newTable = {}
  1547. for x = 1, size.x do
  1548. local xt = {}
  1549. for y = 1, size.y do
  1550. local yt = {}
  1551. for z = 1, size.z do
  1552. yt[z] = fill
  1553. end
  1554. xt[y] = yt
  1555. end
  1556. newTable[x] = xt
  1557. end
  1558. return newTable
  1559. end
  1560.  
  1561. local function linInterp(a,b,p)
  1562. return a+(b-a)*p
  1563. end
  1564.  
  1565. local function exaggerate(n,exaggeration)
  1566. return (n-.5)*exaggeration + .5 --optimized
  1567. --return n*exaggeration - exaggeration*.5 + .5
  1568. end
  1569. local function exaggeratedLinInterp(a,b,p,exaggeration)
  1570. local unclamped = (a+(b-a)*p-.5)*exaggeration+.5
  1571. return (unclamped < 0 and 0) or (unclamped > 1 and 1) or unclamped
  1572.  
  1573. --At first I thought this didn't need to be clamped because the terrain clamps that anways.
  1574. --But I then realized I am using this number a bit more before handing it to terrain.
  1575. --After doing some tests. Clamping is necessary for artificial structures being streched. If unclamped, rounding of artificial edges occurs.
  1576. --return (a+(b-a)*p-.5)*exaggeration+.5
  1577. --Maybe this extra dimension of unclamping might be desired for natural terrain, but not artificuial?
  1578. end
  1579.  
  1580. function updateDragOperation()
  1581. local dragVector = dragVector or Vector3.new(0,0,0)
  1582. local temporaryStart = selectionStart
  1583. local temporaryEnd = selectionEnd
  1584. if tool == 'Resize' then
  1585. if dragStart then
  1586. temporaryStart = Vector3.new(
  1587. math.min(
  1588. math.max(temporaryStart.x+dragVector.x,temporaryEnd.x-regionLengthLimit),
  1589. temporaryEnd.x),
  1590. math.min(
  1591. math.max(temporaryStart.y+dragVector.y,temporaryEnd.y-regionLengthLimit),
  1592. temporaryEnd.y),
  1593. math.min(
  1594. math.max(temporaryStart.z+dragVector.z,temporaryEnd.z-regionLengthLimit),
  1595. temporaryEnd.z)
  1596. )
  1597. else
  1598. temporaryEnd = Vector3.new(
  1599. math.max(
  1600. math.min(temporaryEnd.x+dragVector.x,temporaryStart.x+regionLengthLimit),
  1601. temporaryStart.x),
  1602. math.max(
  1603. math.min(temporaryEnd.y+dragVector.y,temporaryStart.y+regionLengthLimit),
  1604. temporaryStart.y),
  1605. math.max(
  1606. math.min(temporaryEnd.z+dragVector.z,temporaryStart.z+regionLengthLimit),
  1607. temporaryStart.z)
  1608. )
  1609. end
  1610. if mode == 'Edit' then
  1611. --[[local loopx = #lockedMaterials --Tiling resize --fun but not too many use cases with natural terrain.
  1612. local loopy = #lockedMaterials[1]
  1613. local loopz = #lockedMaterials[1][1]
  1614. local tempRegionSize = Vector3.new(1,1,1) + temporaryEnd - temporaryStart
  1615. local newMat = {}
  1616. local newOcc = {}
  1617. local offsetx = -1
  1618. local offsety = -1
  1619. local offsetz = -1
  1620. if dragStart then
  1621. offsetx = offsetx + (-tempRegionSize.x % loopx)
  1622. offsety = offsety + (-tempRegionSize.y % loopy)
  1623. offsetz = offsetz + (-tempRegionSize.z % loopz)
  1624. end
  1625. for x=1, tempRegionSize.x do
  1626. local targetx = (offsetx + x) % loopx + 1
  1627. local xtm = {}
  1628. local xto = {}
  1629. for y=1, tempRegionSize.y do
  1630. local targety = (offsety + y) % loopy + 1
  1631. local ytm = {}
  1632. local yto = {}
  1633. for z=1, tempRegionSize.z do
  1634. local targetz = (offsetz + z) % loopz + 1
  1635. ytm[z] = lockedMaterials[targetx][targety][targetz]
  1636. yto[z] = lockedOccupancies[targetx][targety][targetz]
  1637. end
  1638. xtm[y] = ytm
  1639. xto[y] = yto
  1640. end
  1641. newMat[x] = xtm
  1642. newOcc[x] = xto
  1643. end]]
  1644.  
  1645. --[[local loopx = #lockedMaterials --Scaling closest neightbor resize --not perfect
  1646. local loopy = #lockedMaterials[1]
  1647. local loopz = #lockedMaterials[1][1]
  1648. local tempRegionSize = Vector3.new(1,1,1) + temporaryEnd - temporaryStart
  1649. local tempSizeX = tempRegionSize.x
  1650. local tempSizeY = tempRegionSize.y
  1651. local tempSizeZ = tempRegionSize.z
  1652. local roundx = tempSizeX < loopx and math.floor or math.ceil
  1653. local roundy = tempSizeY < loopy and math.floor or math.ceil
  1654. local roundz = tempSizeZ < loopz and math.floor or math.ceil
  1655. local newMat = {}
  1656. local newOcc = {}
  1657. for x=1, tempSizeX do
  1658. local targetx = roundx(x/tempSizeX*loopx)
  1659. local xtm = {}
  1660. local xto = {}
  1661. for y=1, tempSizeY do
  1662. local targety = roundy(y/tempSizeY*loopy)
  1663. local ytm = {}
  1664. local yto = {}
  1665. for z=1, tempSizeZ do
  1666. local targetz = roundz(z/tempSizeZ*loopz)
  1667. ytm[z] = lockedMaterials[targetx][targety][targetz]
  1668. yto[z] = lockedOccupancies[targetx][targety][targetz]
  1669. end
  1670. xtm[y] = ytm
  1671. xto[y] = yto
  1672. end
  1673. newMat[x] = xtm
  1674. newOcc[x] = xto
  1675. end]]
  1676.  
  1677. local region = Region3.new((temporaryStart - Vector3.new(1,1,1)) * resolution, temporaryEnd * resolution)
  1678. if behindThis then
  1679. terrain:WriteVoxels(behindThis.region, resolution, behindThis.materials, behindThis.occupancies)
  1680. else
  1681. if selectionStart and selectionEnd then
  1682. local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
  1683. local regionSize = region.Size / resolution
  1684. terrain:WriteVoxels(region, resolution, make3DTable(regionSize,materialAir), make3DTable(regionSize,0))
  1685. end
  1686. end
  1687. behindThis = {}
  1688. behindThis.region = region
  1689. behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)
  1690.  
  1691. local behindMaterials, behindOccupancies = behindThis.materials, behindThis.occupancies
  1692.  
  1693. local loopx = #lockedMaterials - 1
  1694. local loopy = #lockedMaterials[1] - 1
  1695. local loopz = #lockedMaterials[1][1] - 1
  1696. local tempRegionSize = Vector3.new(1,1,1) + temporaryEnd - temporaryStart
  1697. local tempSizeX = tempRegionSize.x
  1698. local tempSizeY = tempRegionSize.y
  1699. local tempSizeZ = tempRegionSize.z
  1700. local newMat = {}
  1701. local newOcc = {}
  1702. for x=1, tempSizeX do
  1703. local scalex = (x-1)/(tempSizeX-1)*loopx
  1704. if scalex ~= scalex then
  1705. scalex = 0
  1706. end
  1707. local startx = floor(scalex)+1
  1708. local endx = startx+1
  1709. local interpScalex = scalex-startx+1
  1710. if startx > loopx then
  1711. endx = startx
  1712. end
  1713.  
  1714. local xtm = {}
  1715. local xto = {}
  1716. for y=1, tempSizeY do
  1717. local scaley = (y-1)/(tempSizeY-1)*loopy
  1718. if scaley ~= scaley then
  1719. scaley = 0
  1720. end
  1721. local starty = floor(scaley)+1
  1722. local endy = starty+1
  1723. local interpScaley = scaley-starty+1
  1724. if starty > loopy then
  1725. endy = starty
  1726. end
  1727.  
  1728. local ytm = {}
  1729. local yto = {}
  1730. for z=1, tempSizeZ do
  1731. local scalez = (z-1)/(tempSizeZ-1)*loopz --consider adding 1 here and removing +1's elsewhere
  1732. if scalez ~= scalez then --undefined check
  1733. scalez = 0
  1734. end
  1735. local startz = floor(scalez)+1
  1736. local endz = startz+1
  1737. local interpScalez = scalez-startz+1
  1738. if startz > loopz then
  1739. endz = startz
  1740. end
  1741.  
  1742. local interpz1 = exaggeratedLinInterp(lockedOccupancies[startx][starty][startz],lockedOccupancies[startx][starty][endz],interpScalez, tempSizeZ/(loopz+1))
  1743. local interpz2 = exaggeratedLinInterp(lockedOccupancies[startx][endy][startz],lockedOccupancies[startx][endy][endz],interpScalez, tempSizeZ/(loopz+1))
  1744. local interpz3 = exaggeratedLinInterp(lockedOccupancies[endx][starty][startz],lockedOccupancies[endx][starty][endz],interpScalez, tempSizeZ/(loopz+1))
  1745. local interpz4 = exaggeratedLinInterp(lockedOccupancies[endx][endy][startz],lockedOccupancies[endx][endy][endz],interpScalez, tempSizeZ/(loopz+1))
  1746.  
  1747. local interpy1 = exaggeratedLinInterp(interpz1,interpz2,interpScaley, tempSizeY/(loopy+1))
  1748. local interpy2 = exaggeratedLinInterp(interpz3,interpz4,interpScaley, tempSizeY/(loopy+1))
  1749.  
  1750. local interpx1 = exaggeratedLinInterp(interpy1,interpy2,interpScalex, tempSizeX/(loopx+1))
  1751.  
  1752. local newMaterial = lockedMaterials[round(scalex)+1][round(scaley)+1][round(scalez)+1]
  1753.  
  1754. if fillAir and newMaterial == materialAir then
  1755. ytm[z]=behindMaterials[x][y][z]
  1756. yto[z]=behindOccupancies[x][y][z]
  1757. elseif fillWater and newMaterial == materialWater and behindMaterials[x][y][z] ~= materialAir then
  1758. ytm[z]=behindMaterials[x][y][z]
  1759. yto[z]=behindOccupancies[x][y][z]
  1760. else
  1761. ytm[z]=newMaterial
  1762. yto[z]=interpx1
  1763. end
  1764. end
  1765. xtm[y] = ytm
  1766. xto[y] = yto
  1767. end
  1768. newMat[x] = xtm
  1769. newOcc[x] = xto
  1770. end
  1771.  
  1772. terrain:WriteVoxels(region, resolution, newMat, newOcc)
  1773. else
  1774. behindThis = nil
  1775. end
  1776. elseif tool == 'Move' then
  1777. temporaryStart = temporaryStart + dragVector
  1778. temporaryEnd = temporaryEnd + dragVector
  1779. if mode == 'Edit' then
  1780. local region = Region3.new((temporaryStart - Vector3.new(1,1,1)) * resolution, temporaryEnd * resolution)
  1781. if behindThis then
  1782. terrain:WriteVoxels(behindThis.region, resolution, behindThis.materials, behindThis.occupancies)
  1783. else
  1784. if selectionStart and selectionEnd then
  1785. local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
  1786. local regionSize = region.Size / resolution
  1787. terrain:WriteVoxels(region, resolution, make3DTable(regionSize,materialAir), make3DTable(regionSize,0))
  1788. end
  1789. end
  1790. behindThis = {}
  1791. behindThis.region = region
  1792. behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)
  1793.  
  1794. local behindMaterials, behindOccupancies = behindThis.materials, behindThis.occupancies
  1795.  
  1796. if not (fillAir or fillWater) then
  1797. terrain:WriteVoxels(region, resolution, lockedMaterials, lockedOccupancies)
  1798. else
  1799. local newMat = {}
  1800. local newOcc = {}
  1801.  
  1802. for x,xv in ipairs(lockedMaterials) do
  1803. local xtm = {}
  1804. local xto = {}
  1805. for y,yv in ipairs(xv) do
  1806. local ytm = {}
  1807. local yto = {}
  1808. for z,zv in ipairs(yv) do
  1809. if fillAir and zv == materialAir then
  1810. ytm[z]=behindMaterials[x][y][z]
  1811. yto[z]=behindOccupancies[x][y][z]
  1812. elseif fillWater and zv == materialWater and behindMaterials[x][y][z] ~= materialAir then
  1813. ytm[z]=behindMaterials[x][y][z]
  1814. yto[z]=behindOccupancies[x][y][z]
  1815. else
  1816. ytm[z]=lockedMaterials[x][y][z]
  1817. yto[z]=lockedOccupancies[x][y][z]
  1818. end
  1819. end
  1820. xtm[y] = ytm
  1821. xto[y] = yto
  1822. end
  1823. newMat[x] = xtm
  1824. newOcc[x] = xto
  1825. end
  1826. terrain:WriteVoxels(region, resolution, newMat, newOcc)
  1827. end
  1828. end
  1829. end
  1830. renderSelection(temporaryStart,temporaryEnd)
  1831. end
  1832.  
  1833. function dragHandles(face, delta)
  1834. local normal = faceToNormal[face]
  1835. local delta = delta
  1836. local newDragVector = normal * floor((delta + .5) / resolution)
  1837. 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
  1838. if newDragVector ~= dragVector then
  1839. dragVector = newDragVector
  1840. updateDragOperation()
  1841. end
  1842. end
  1843.  
  1844. local function rotate(mx,x,my,y,rotation)
  1845. if rotation == 1 then
  1846. return my + 1 - y, x
  1847. elseif rotation == 2 then
  1848. return mx + 1 - x, my + 1 - y
  1849. elseif rotation == 3 then
  1850. return y, mx + 1 - x
  1851. end
  1852. return x,y
  1853. end
  1854.  
  1855. function updateRotateOperation()
  1856. local dragAngle = dragAngle or 0
  1857. local rotationCFrame = CFrame.Angles(
  1858. axis ~= 'X' and 0 or dragAngle * rotationInterval,
  1859. axis ~= 'Y' and 0 or dragAngle * rotationInterval,
  1860. axis ~= 'Z' and 0 or dragAngle * rotationInterval
  1861. )
  1862. local temporarySize = Vector3.new(1,1,1) + selectionEnd - selectionStart
  1863. local centerOffset = Vector3.new(ceil(temporarySize.x * .5), ceil(temporarySize.y * .5), ceil(temporarySize.z * .5))
  1864.  
  1865. temporarySize = rotationCFrame * temporarySize
  1866. local temporarySizeX = round(math.abs(temporarySize.x)) --I need to round these because of floating point imprecision
  1867. local temporarySizeY = round(math.abs(temporarySize.y))
  1868. local temporarySizeZ = round(math.abs(temporarySize.z))
  1869. centerOffset = centerOffset - Vector3.new(ceil(temporarySizeX * .5), ceil(temporarySizeY * .5), ceil(temporarySizeZ * .5))
  1870.  
  1871. local temporaryEnd = selectionStart + centerOffset + Vector3.new(temporarySizeX, temporarySizeY, temporarySizeZ) - Vector3.new(1, 1, 1)
  1872. local temporaryStart = selectionStart + centerOffset
  1873.  
  1874. if mode == 'Edit' then
  1875. local region = Region3.new((temporaryStart - Vector3.new(1,1,1)) * resolution, temporaryEnd * resolution)
  1876. if behindThis then
  1877. terrain:WriteVoxels(behindThis.region, resolution, behindThis.materials, behindThis.occupancies)
  1878. else
  1879. if selectionStart and selectionEnd then
  1880. local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
  1881. local regionSize = region.Size / resolution
  1882. terrain:WriteVoxels(region, resolution, make3DTable(regionSize,materialAir), make3DTable(regionSize,0))
  1883. end
  1884. --local regionSize = lockedRegion.Size / resolution
  1885. --terrain:WriteVoxels(lockedRegion, resolution, make3DTable(regionSize,materialAir), make3DTable(regionSize,0))
  1886. end
  1887. behindThis = {}
  1888. behindThis.region = region
  1889. behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)
  1890.  
  1891. local newMat = {}
  1892. local newOcc = {}
  1893.  
  1894. for x=1, temporarySizeX do
  1895. local xtm = {}
  1896. local xto = {}
  1897. for y=1, temporarySizeY do
  1898. local ytm = {}
  1899. local yto = {}
  1900. for z=1, temporarySizeZ do
  1901. local targetx = x
  1902. local targety = y
  1903. local targetz = z
  1904. if axis == 'Y' then --prioritize y because I know this is the primary rotation axis
  1905. targetx, targetz = rotate(temporarySizeX, x, temporarySizeZ, z, dragAngle)
  1906. elseif axis == 'X' then
  1907. targetz, targety = rotate(temporarySizeZ, z, temporarySizeY, y, dragAngle)
  1908. elseif axis == 'Z' then
  1909. targety, targetx = rotate(temporarySizeY, y, temporarySizeX, x, dragAngle)
  1910. end
  1911. local newMaterial = lockedMaterials[targetx][targety][targetz]
  1912.  
  1913. if fillAir and newMaterial == materialAir then
  1914. ytm[z]=behindThis.materials[x][y][z]
  1915. yto[z]=behindThis.occupancies[x][y][z]
  1916. elseif fillWater and newMaterial == materialWater and behindThis.materials[x][y][z] ~= materialAir then
  1917. ytm[z]=behindThis.materials[x][y][z]
  1918. yto[z]=behindThis.occupancies[x][y][z]
  1919. else
  1920. ytm[z]=newMaterial
  1921. yto[z]=lockedOccupancies[targetx][targety][targetz]
  1922. end
  1923. end
  1924. xtm[y] = ytm
  1925. xto[y] = yto
  1926. end
  1927. newMat[x] = xtm
  1928. newOcc[x] = xto
  1929. end
  1930.  
  1931. terrain:WriteVoxels(region, resolution, newMat, newOcc)
  1932. end
  1933. renderSelection(temporaryStart,temporaryEnd,rotationCFrame)
  1934. end
  1935.  
  1936. function dragArcHandles(rotationAxis,relativeAngle,deltaRadius)
  1937. axis = rotationAxis.Name
  1938. local newDragAngle = round(relativeAngle / rotationInterval) % 4
  1939. if newDragAngle ~= dragAngle then
  1940. dragAngle = newDragAngle
  1941. updateRotateOperation()
  1942. end
  1943. end
  1944.  
  1945. buttonCopy.MouseButton1Down:connect(function()
  1946. if selectionStart and selectionEnd then
  1947. local selectionStartInt16=Vector3int16.new(selectionStart.x-1,selectionStart.y-1,selectionStart.z-1)
  1948. local selectionEndInt16=Vector3int16.new(selectionEnd.x-1,selectionEnd.y-1,selectionEnd.z-1)
  1949. local region = Region3int16.new(selectionStartInt16,selectionEndInt16)
  1950. copyRegion = terrain:CopyRegion(region)
  1951. selectionEffect(nil,nil,'New Yeller',1,1.2,.5)
  1952. end
  1953. end)
  1954.  
  1955. buttonPaste.MouseButton1Down:connect(function()
  1956. if copyRegion then
  1957. selectionEnd=selectionStart+copyRegion.SizeInCells-Vector3.new(1,1,1)
  1958.  
  1959. local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
  1960. behindThis = {}
  1961. behindThis.region = region
  1962. behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)
  1963.  
  1964. terrain:PasteRegion(copyRegion,Vector3int16.new(selectionStart.x-1,selectionStart.y-1,selectionStart.z-1),true)
  1965. setButton('Move')
  1966. if utilityModule:GetUsingAsPlugin() then
  1967. changeHistory:SetWaypoint('Terrain Paste')
  1968. end
  1969. selectionEffect(nil,nil,'Lime green',1.2,1,.5)
  1970. end
  1971. end)
  1972.  
  1973. buttonDelete.MouseButton1Down:connect(function()
  1974. if selectionStart and selectionEnd then
  1975. local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
  1976. local regionSize = region.Size / resolution
  1977. local emptyMaterialMap = make3DTable(regionSize,materialAir)
  1978. local emptyOccupancyMap = make3DTable(regionSize,0)
  1979.  
  1980. --[[behindThis = {}
  1981. behindThis.region = region
  1982. behindThis.materials, behindThis.occupancies = emptyMaterialMap, emptyOccupancyMap
  1983.  
  1984. terrain:WriteVoxels(region, resolution, emptyMaterialMap, emptyOccupancyMap)]]
  1985. if behindThis then
  1986. terrain:WriteVoxels(behindThis.region, resolution, behindThis.materials, behindThis.occupancies)
  1987. else
  1988. if selectionStart and selectionEnd then
  1989. terrain:WriteVoxels(region, resolution, emptyMaterialMap, emptyOccupancyMap)
  1990. end
  1991. end
  1992. behindThis = {}
  1993. behindThis.region = region
  1994. behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)
  1995.  
  1996. --[[lockedRegion = region
  1997. lockedMaterials, lockedOccupancies = emptyMaterialMap, emptyOccupancyMap]]
  1998. local oldStart, oldEnd = selectionStart, selectionEnd
  1999. selectionStart, selectionEnd = nil, nil
  2000. setButton('Select')
  2001.  
  2002. if utilityModule:GetUsingAsPlugin() then
  2003. changeHistory:SetWaypoint('Terrain Delete')
  2004. end
  2005. selectionEffect(oldStart,oldEnd,'Really red',1,1.2,.5)
  2006. end
  2007. end)
  2008.  
  2009. buttonFill.MouseButton1Down:connect(function()
  2010. fillFrame.Visible = not fillFrame.Visible
  2011. end)
  2012.  
  2013. buttonFillFrameClose.MouseButton1Down:connect(function()
  2014. fillFrame.Visible = false
  2015. end)
  2016.  
  2017. buttonFillConfirm.MouseButton1Down:connect(function()
  2018. if selectionStart and selectionEnd then
  2019. local region = Region3.new((selectionStart - Vector3.new(1,1,1)) * resolution, selectionEnd * resolution)
  2020. local regionSize = region.Size / resolution
  2021.  
  2022. local beforeMaterialMap, beforeOccupancyMap = terrain:ReadVoxels(region, resolution)
  2023. local newMaterialMap = {}
  2024. local newOccupancyMap = {}
  2025. for x = 1, regionSize.x do
  2026. local xtm = {}
  2027. local xto = {}
  2028. for y = 1, regionSize.y do
  2029. local ytm = {}
  2030. local yto = {}
  2031. for z = 1, regionSize.z do
  2032. local beforeMaterial = beforeMaterialMap[x][y][z]
  2033.  
  2034. if beforeMaterial == materialAir or beforeOccupancyMap[x][y][z] == 0 or not fillAir then --'fillAir' variable is actually 'Merge Empty' to the user
  2035. ytm[z] = materialSelection.enum
  2036. else
  2037. ytm[z] = beforeMaterial
  2038. end
  2039.  
  2040. yto[z] = 1
  2041. end
  2042. xtm[y] = ytm
  2043. xto[y] = yto
  2044. end
  2045. newMaterialMap[x] = xtm
  2046. newOccupancyMap[x] = xto
  2047. end
  2048.  
  2049.  
  2050. terrain:WriteVoxels(region, resolution, newMaterialMap, newOccupancyMap)
  2051.  
  2052. behindThis = {}
  2053. behindThis.region = region
  2054. behindThis.materials, behindThis.occupancies = terrain:ReadVoxels(region, resolution)
  2055.  
  2056. fillFrame.Visible = false
  2057.  
  2058. if utilityModule:GetUsingAsPlugin() then
  2059. changeHistory:SetWaypoint('Terrain Fill')
  2060. end
  2061. selectionEffect(nil,nil,'Lime green',1.2,1,.5)
  2062. end
  2063. end)
  2064.  
  2065. function selectionEffect(temporaryStart,temporaryEnd,color,sizeFrom,sizeTo,effectTime)
  2066. local temporaryStart = temporaryStart or selectionStart
  2067. local temporaryEnd = temporaryEnd or selectionEnd
  2068.  
  2069. local effectPart = Instance.new('Part')
  2070. effectPart.Name = 'EffectPart'
  2071. effectPart.Transparency = 1
  2072. effectPart.TopSurface = 'Smooth'
  2073. effectPart.BottomSurface = 'Smooth'
  2074. effectPart.Anchored = true
  2075. effectPart.CanCollide = false
  2076. effectPart.Parent = gui
  2077.  
  2078. local selectionEffectObject = Instance.new('SelectionBox')
  2079. selectionEffectObject.Name = 'SelectionObject'
  2080. selectionEffectObject.Transparency = 1
  2081. selectionEffectObject.SurfaceTransparency = .75
  2082. selectionEffectObject.SurfaceColor = BrickColor.new(color)
  2083. selectionEffectObject.Adornee = effectPart
  2084. selectionEffectObject.Parent = effectPart
  2085.  
  2086. local baseSize = ((temporaryEnd - temporaryStart + Vector3.new(1,1,1)) * resolution + Vector3.new(.21,.21,.21))
  2087. effectPart.CFrame = CFrame.new((temporaryStart + temporaryEnd - Vector3.new(1, 1, 1)) * .5 * resolution)
  2088. effectPart.Size = baseSize * sizeFrom
  2089. local endTick=tick()+effectTime
  2090. while endTick>tick() do
  2091. local percent=1-(endTick-tick())/effectTime
  2092. selectionEffectObject.SurfaceTransparency = .75 + percent*.25
  2093. effectPart.Size = baseSize * (sizeFrom+(sizeTo-sizeFrom)*percent)
  2094. wait()
  2095. end
  2096. effectPart:Destroy()
  2097. end
  2098.  
  2099. function renderSelection(temporaryStart,temporaryEnd,rotation)
  2100. local temporaryStart = temporaryStart or selectionStart
  2101. local temporaryEnd = temporaryEnd or selectionEnd
  2102. local seeable = false
  2103. if temporaryStart and temporaryEnd and selectionPart then
  2104. seeable = true
  2105. local temporarySize = ((temporaryEnd - temporaryStart + Vector3.new(1,1,1)) * resolution + Vector3.new(.2,.2,.2))
  2106. if rotation then
  2107. local rotatedSize = rotation * temporarySize
  2108. temporarySize = Vector3.new(math.abs(rotatedSize.x), math.abs(rotatedSize.y), math.abs(rotatedSize.z))
  2109. end
  2110. selectionPart.Size = temporarySize
  2111. selectionPart.CFrame = CFrame.new((temporaryStart + temporaryEnd - Vector3.new(1, 1, 1)) * .5 * resolution) * (rotation or CFrame.new(0,0,0))
  2112. end
  2113. if selectionObject then
  2114. selectionObject.Visible = seeable
  2115. selectionObject.Color = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor1)
  2116. selectionObject.SurfaceColor = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor1)
  2117. end
  2118. if selectionHandles then
  2119. selectionHandles.Visible = seeable and (tool == 'Move' or tool == 'Resize')
  2120. selectionHandles.Color = BrickColor.new(mode == 'Select' and 'Cyan' or editColor2)
  2121. selectionHandles.Style = tool == 'Move' and Enum.HandlesStyle.Movement or Enum.HandlesStyle.Resize
  2122. end
  2123. if selectionArcHandles then
  2124. selectionArcHandles.Visible = seeable and tool == 'Rotate'
  2125. selectionArcHandles.Color = BrickColor.new(mode == 'Select' and 'Cyan' or editColor2)
  2126. end
  2127. end
  2128.  
  2129. function Selected()
  2130. --plugin:Activate(true)
  2131. --regionEditButton:SetActive(true)
  2132. on = true
  2133. gui.Parent = coreGui
  2134. gui.Frame.Visible = true
  2135.  
  2136. if not selectionPart then
  2137. selectionPart = Instance.new('Part')
  2138. selectionPart.Name = 'SelectionPart'
  2139. selectionPart.Transparency = 1
  2140. selectionPart.TopSurface = 'Smooth'
  2141. selectionPart.BottomSurface = 'Smooth'
  2142. selectionPart.Anchored = true
  2143. selectionPart.CanCollide = false
  2144. selectionPart.Parent = gui
  2145. end
  2146. if not selectionObject then
  2147. selectionObject = Instance.new('SelectionBox')
  2148. selectionObject.Name = 'SelectionObject'
  2149. selectionObject.Color = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor1)
  2150. selectionObject.SurfaceTransparency = .85
  2151. selectionObject.SurfaceColor = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor1)
  2152. selectionObject.Adornee = selectionPart
  2153. selectionObject.Visible = false
  2154. selectionObject.Parent = selectionPart
  2155. end
  2156. if not selectionHandles then
  2157. selectionHandles = Instance.new('Handles')
  2158. selectionHandles.Name = 'SelectionHandles'
  2159. selectionHandles.Color = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor2)
  2160. selectionHandles.Adornee = selectionPart
  2161. selectionHandles.Visible = false
  2162. selectionHandles.Parent = coreGui--game.Workspace--guiFrame--selectionPart
  2163. selectionHandles.MouseDrag:connect(dragHandles)
  2164. end
  2165. if not selectionArcHandles then
  2166. selectionArcHandles = Instance.new('ArcHandles')
  2167. selectionArcHandles.Name = 'SelectionArcHandles'
  2168. selectionArcHandles.Color = BrickColor.new(mode == 'Select' and 'Toothpaste' or editColor2)
  2169. selectionArcHandles.Adornee = selectionPart
  2170. selectionArcHandles.Visible = false
  2171. selectionArcHandles.Parent = coreGui--game.Workspace--guiFrame--selectionPart
  2172. selectionArcHandles.MouseDrag:connect(dragArcHandles)
  2173. end
  2174. renderSelection()
  2175. setButton(button)
  2176. end
  2177.  
  2178. function Deselected()
  2179. setButton('Select')
  2180. gui.Parent = script.Parent
  2181. gui.Frame.Visible = false
  2182.  
  2183. clearSelection()
  2184. --regionEditButton:SetActive(false)
  2185. behindThis = nil
  2186. on = false
  2187. if turnOff then
  2188. turnOff()
  2189. end
  2190. end
  2191.  
  2192. mouse.Button1Down:connect(function()
  2193. if on and mode == 'Select' then
  2194. mouseDown = true
  2195. behindThis = nil
  2196. local mousePos = mouse.Hit.p + mouse.UnitRay.Direction * .05
  2197. if mouse.Target == nil then --cage the cursor so that it does not fly away
  2198. mousePos = game.Workspace.CurrentCamera.CoordinateFrame.p + mouse.UnitRay.Direction * 100
  2199. end
  2200. clickStart = positionWorldToVoxel(mousePos)
  2201. local thisDownLoop = {}
  2202. downLoop = thisDownLoop
  2203. while thisDownLoop == downLoop and mouseDown and on and mode == 'Select' do
  2204. local mousePos = mouse.Hit.p + mouse.UnitRay.Direction * .05
  2205. if mouse.Target == nil then --cage the cursor so that it does not fly away
  2206. mousePos = game.Workspace.CurrentCamera.CoordinateFrame.p + mouse.UnitRay.Direction * 100
  2207. end
  2208. local voxelCurrent = positionWorldToVoxel(mousePos)
  2209. voxelCurrent = Vector3.new(
  2210. math.max(math.min(voxelCurrent.x,clickStart.x+regionLengthLimit),clickStart.x-regionLengthLimit),
  2211. math.max(math.min(voxelCurrent.y,clickStart.y+regionLengthLimit),clickStart.y-regionLengthLimit),
  2212. math.max(math.min(voxelCurrent.z,clickStart.z+regionLengthLimit),clickStart.z-regionLengthLimit))
  2213. selectionStart = Vector3.new(math.min(clickStart.x, voxelCurrent.x), math.min(clickStart.y, voxelCurrent.y), math.min(clickStart.z, voxelCurrent.z))
  2214. selectionEnd = Vector3.new(math.max(clickStart.x, voxelCurrent.x), math.max(clickStart.y, voxelCurrent.y), math.max(clickStart.z, voxelCurrent.z))
  2215. renderSelection()
  2216. quickWait()
  2217. end
  2218. end
  2219. end)
  2220. mouse.Button1Up:connect(function()
  2221. mouseDown = false
  2222. if dragVector and dragVector.magnitude > 0 then
  2223. if tool == 'Resize' then
  2224. --[[if dragStart then
  2225. 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))
  2226. else
  2227. 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))
  2228. end]]
  2229. if dragStart then
  2230. selectionStart = Vector3.new(
  2231. math.min(
  2232. math.max(selectionStart.x+dragVector.x,selectionEnd.x-regionLengthLimit),
  2233. selectionEnd.x),
  2234. math.min(
  2235. math.max(selectionStart.y+dragVector.y,selectionEnd.y-regionLengthLimit),
  2236. selectionEnd.y),
  2237. math.min(
  2238. math.max(selectionStart.z+dragVector.z,selectionEnd.z-regionLengthLimit),
  2239. selectionEnd.z)
  2240. )
  2241. else
  2242. selectionEnd = Vector3.new(
  2243. math.max(
  2244. math.min(selectionEnd.x+dragVector.x,selectionStart.x+regionLengthLimit),
  2245. selectionStart.x),
  2246. math.max(
  2247. math.min(selectionEnd.y+dragVector.y,selectionStart.y+regionLengthLimit),
  2248. selectionStart.y),
  2249. math.max(
  2250. math.min(selectionEnd.z+dragVector.z,selectionStart.z+regionLengthLimit),
  2251. selectionStart.z)
  2252. )
  2253. end
  2254. elseif tool == 'Move' then
  2255. selectionStart = selectionStart + dragVector
  2256. selectionEnd = selectionEnd + dragVector
  2257. end
  2258. if utilityModule:GetUsingAsPlugin() then
  2259. changeHistory:SetWaypoint('Terrain '..button)
  2260. end
  2261. end
  2262. if dragAngle and dragAngle ~= 0 then
  2263. local rotationCFrame = CFrame.Angles(
  2264. axis ~= 'X' and 0 or dragAngle * rotationInterval,
  2265. axis ~= 'Y' and 0 or dragAngle * rotationInterval,
  2266. axis ~= 'Z' and 0 or dragAngle * rotationInterval
  2267. )
  2268. local temporarySize = Vector3.new(1,1,1) + selectionEnd - selectionStart
  2269. local centerOffset = Vector3.new(ceil(temporarySize.x * .5), ceil(temporarySize.y * .5), ceil(temporarySize.z * .5))
  2270. temporarySize = rotationCFrame * temporarySize
  2271. local temporarySizeX = round(math.abs(temporarySize.x)) --I need to round these because of floating point imprecision
  2272. local temporarySizeY = round(math.abs(temporarySize.y))
  2273. local temporarySizeZ = round(math.abs(temporarySize.z))
  2274. centerOffset = centerOffset - Vector3.new(ceil(temporarySizeX * .5), ceil(temporarySizeY * .5), ceil(temporarySizeZ * .5))
  2275.  
  2276. selectionEnd = selectionStart + centerOffset + Vector3.new(temporarySizeX, temporarySizeY, temporarySizeZ) - Vector3.new(1, 1, 1)
  2277. selectionStart = selectionStart + centerOffset
  2278. lockInMap()
  2279. if utilityModule:GetUsingAsPlugin() then
  2280. changeHistory:SetWaypoint('Terrain '..button)
  2281. end
  2282. end
  2283.  
  2284. dragVector = nil
  2285. dragAngle = nil
  2286. renderSelection()
  2287. --lockInMap()
  2288. end)
  2289.  
  2290. closeButton.MouseButton1Down:connect(Deselected)
  2291.  
  2292. --[[plugin.Deactivation:connect(function()
  2293. if on then
  2294. Deselected()
  2295. end
  2296. end)]]
  2297.  
  2298. local function historyChanged()
  2299. selectionStart = nil
  2300. selectionEnd = nil
  2301. lockedMaterials = nil
  2302. lockedOccupancies = nil
  2303. setButton('Select')
  2304. end
  2305.  
  2306. if utilityModule:GetUsingAsPlugin() then
  2307. changeHistory.OnUndo:connect(historyChanged)
  2308. changeHistory.OnRedo:connect(historyChanged)
  2309. end
  2310.  
  2311. end
  2312.  
  2313. function On(mouseHandMeDown,turnOffHandMeDown)
  2314. mouse = mouseHandMeDown
  2315. turnOff = turnOffHandMeDown
  2316. if not setup then --I do this so that things only get set up when this plugin is used.
  2317. FirstTimeSetUp()
  2318. end
  2319. Selected()
  2320. end
  2321.  
  2322. function Off()
  2323. if Deselected then
  2324. Deselected()
  2325. end
  2326. end
  2327.  
  2328. return {
  2329. ['On'] = On,
  2330. ['Off'] = Off,
  2331. }
  2332.  
  2333.  
  2334.  
  2335. local initialised = false
  2336.  
  2337. function initialiseUtility(plugin)
  2338. if not initialised then
  2339. initialised = true
  2340. utility.usingAsPlugin = (plugin ~= nil)
  2341. utility.plugin = plugin
  2342.  
  2343. local function createIngameButtonCreator()
  2344. local buttonCreator = {}
  2345.  
  2346. function buttonCreator:CreateButton(name, hint, image)
  2347. local backpack = game.Players.LocalPlayer:WaitForChild("Backpack")
  2348.  
  2349. local tool = Instance.new("Tool")
  2350. tool.TextureId = image
  2351. tool.RequiresHandle = false
  2352. tool.ToolTip = hint
  2353. tool.Parent = backpack
  2354.  
  2355. local result = {}
  2356. result.Click = tool.Equipped
  2357. result.Deselected = tool.Unequipped
  2358. function result:SetActive(active)
  2359. --Do nothing
  2360. end
  2361. return result
  2362. end
  2363.  
  2364. return buttonCreator
  2365. end
  2366.  
  2367. function utility:GetButtonCreator()
  2368. return utility.usingAsPlugin and utility.plugin:CreateToolbar('Smooth Terrain') or createIngameButtonCreator()
  2369. end
  2370.  
  2371. function utility:GetUsingAsPlugin()
  2372. return utility.usingAsPlugin
  2373. end
  2374.  
  2375. function utility:GetCoreGui()
  2376. return utility.usingAsPlugin and game:GetService("CoreGui") or game.Players.LocalPlayer.PlayerGui
  2377. end
  2378.  
  2379. function utility:GetMouse()
  2380. return utility.usingAsPlugin and utility.plugin:GetMouse() or game.Players.LocalPlayer:GetMouse()
  2381. end
  2382. end
  2383.  
  2384. return utility
  2385. end
  2386.  
  2387. local materialsTable = { --Interface order is defined by order here
  2388. {enum = Enum.Material.Grass, image = 'http://www.roblox.com/asset/?id=225314676'},
  2389. {enum = Enum.Material.Sand, image = 'http://www.roblox.com/asset/?id=225315607'},
  2390. {enum = Enum.Material.Rock, image = 'http://www.roblox.com/asset/?id=225315178'},
  2391. {enum = Enum.Material.Water, image = 'http://www.roblox.com/asset/?id=225315529', forceIgnoreWater = true, forceIgnoreWaterTo = false},
  2392. {enum = Enum.Material.Ground, image = 'http://www.roblox.com/asset/?id=254542189'},
  2393. {enum = Enum.Material.Sandstone, image = 'http://www.roblox.com/asset/?id=254541350'},
  2394. {enum = Enum.Material.Slate, image = 'http://www.roblox.com/asset/?id=225315290'},
  2395. {enum = Enum.Material.Snow, image = 'http://www.roblox.com/asset/?id=254541898'},
  2396. {enum = Enum.Material.Mud, image = 'http://www.roblox.com/asset/?id=254541862'},
  2397. {enum = Enum.Material.Brick, image = 'http://www.roblox.com/asset/?id=225315419', forceSnapToGrid = true},
  2398. {enum = Enum.Material.Concrete, image = 'http://www.roblox.com/asset/?id=225314983'},
  2399. {enum = Enum.Material.Glacier, image = 'http://www.roblox.com/asset/?id=254541572'},
  2400. {enum = Enum.Material.WoodPlanks, image = 'http://www.roblox.com/asset/?id=225315705', forceSnapToGrid = true},
  2401. {enum = Enum.Material.CrackedLava, image = 'http://www.roblox.com/asset/?id=254541726'},
  2402. {enum = Enum.Material.Basalt, image = 'http://www.roblox.com/asset/?id=254542066'},
  2403. {enum = Enum.Material.Ice, image = 'rbxassetid://397352205'},
  2404. {enum = Enum.Material.Salt, image = 'rbxassetid://397352299'},
  2405. {enum = Enum.Material.Cobblestone, image = 'rbxassetid://397352378'},
  2406. {enum = Enum.Material.Limestone, image = 'rbxassetid://397352474'},
  2407. {enum = Enum.Material.Asphalt, image = 'rbxassetid://397352644'},
  2408. {enum = Enum.Material.LeafyGrass, image = 'rbxassetid://397720681'},
  2409. {enum = Enum.Material.Pavement, image = 'rbxassetid://397727024'},
  2410. }
  2411.  
  2412. return materialsTable
  2413.  
  2414.  
  2415. local coreGui = game:GetService('CoreGui')
  2416. local changeHistoryService = game:GetService('ChangeHistoryService')
  2417. local terrain = game.Workspace.Terrain
  2418. local gui = script.Parent:WaitForChild('TerrainGenerationGui')
  2419. local mainFrame = gui:WaitForChild('MainFrame')
  2420. local scrollingFrame = mainFrame:WaitForChild('ScrollingFrame')
  2421. local canvas = scrollingFrame:WaitForChild('Canvas')
  2422. local progressFrame = gui:WaitForChild('ProgressFrame')
  2423. local pauseButton = progressFrame:WaitForChild('PuaseButton')
  2424. local cancelButton = progressFrame:WaitForChild('CancelButton')
  2425. local barFill = progressFrame:WaitForChild('Bar'):WaitForChild('Fill')
  2426. local multipleChoiceFrame = gui:WaitForChild('MultipleChoiceFrame')
  2427.  
  2428.  
  2429.  
  2430. local allBiomes = {
  2431. ['Mountains'] = true,
  2432. ['Canyons'] = false,
  2433. ['Dunes'] = false,
  2434. ['Arctic'] = false,
  2435. ['Lavaflow'] = false,
  2436. ['Hills'] = true,
  2437. ['Plains'] = true,
  2438. ['Marsh'] = false,
  2439. ['Water'] = false,
  2440. }
  2441.  
  2442.  
  2443. local on = false
  2444. local mouse = nil
  2445. local turnOff = nil
  2446. local generating = false
  2447. local clearing = false
  2448. local paused = false
  2449. local cancelIt = false
  2450.  
  2451. local noise = math.noise
  2452. local min = math.min
  2453. local max = math.max
  2454. local sin = math.sin
  2455. local cos = math.cos
  2456. local floor = math.floor
  2457. local ceil = math.ceil
  2458. local sqrt = math.sqrt
  2459. local randomseed = math.randomseed
  2460. local random = math.random
  2461. local pi = math.pi
  2462. local tau = math.pi*2
  2463.  
  2464.  
  2465. ---------Directly used in Generation---------
  2466. local masterSeed = 618033988
  2467. local mapWidth = 256
  2468. local mapHeight = 128
  2469. local biomeSize = 100
  2470. local generateCaves = false
  2471. local waterLevel = .48
  2472. local surfaceThickness = .018
  2473. local biomes = {}
  2474. ---------------------------------------------
  2475.  
  2476.  
  2477.  
  2478. function createSection(name, pos, size, parent)
  2479. local sectionGui = gui:WaitForChild('TemplateSection'):clone()
  2480. sectionGui.Name = 'Section'..name
  2481. sectionGui.Text = name
  2482. sectionGui.Position = UDim2.new(0,0,0,pos)
  2483. sectionGui.Frame.Size = UDim2.new(1,0,0,size)
  2484. sectionGui.Parent = parent
  2485. sectionGui.Visible = true
  2486. return sectionGui, sectionGui.Frame
  2487. end
  2488.  
  2489. function createTextBox(name, pos, parent, setting, changedFunction)
  2490. local setting = setting and tostring(setting) or ''
  2491. local frame = gui:WaitForChild('TemplateTextBox'):clone()
  2492. local field = frame:WaitForChild('TextBox')
  2493. field.Text = setting
  2494. field.Changed:connect(function(prop)
  2495. if prop == 'Text' then
  2496. changedFunction(field.Text)
  2497. end
  2498. end)
  2499. changedFunction(field.Text)
  2500. frame.Name = 'TextInputBox'..name
  2501. frame.Text = ' '..name..':'
  2502. frame.Position = UDim2.new(0,0,0,pos)
  2503. frame.Parent = parent
  2504. frame.Visible = true
  2505. return frame
  2506. end
  2507.  
  2508. local currentMultiChoiceOpen = nil
  2509. function closeMultiChoiceFrame()
  2510. if currentMultiChoiceOpen then
  2511. currentMultiChoiceOpen.Style = 'RobloxRoundDefaultButton'
  2512. end
  2513. currentMultiChoiceOpen = nil
  2514. multipleChoiceFrame.Visible = false
  2515. end
  2516. function createMultiChoice(name, pos, parent, choices, setting, changedFunction)
  2517. local setting = setting
  2518. local frame = gui:WaitForChild('TemplateMultiChoice'):clone()
  2519. frame.Name = 'MultipleChoice'..name
  2520. frame.Text = name
  2521. frame.Position = UDim2.new(0,0,0,pos)
  2522. local button = frame:WaitForChild('Button')
  2523. button.Text = choices[setting]
  2524. button.MouseButton1Down:connect(function()
  2525. if currentMultiChoiceOpen ~= button then
  2526. currentMultiChoiceOpen = button
  2527. --button.Style = 'RobloxRoundButton' --would be nice, but with autobuttoncolor, not so much.
  2528. multipleChoiceFrame.TitleLabel.Text = name
  2529. for _,item in pairs(multipleChoiceFrame:GetChildren()) do
  2530. if string.sub(item.Name,1,6) == 'Choice' then
  2531. item:Destroy()
  2532. end
  2533. end
  2534. for i, text in ipairs(choices) do
  2535. local choiceButton = gui:WaitForChild('TemplateButton'):clone()
  2536. choiceButton.Name = 'Choice'..text
  2537. choiceButton.Visible = true
  2538. choiceButton.Text = text
  2539. choiceButton.Position = UDim2.new(0,10,0,-3+i*35)
  2540. choiceButton.Size = UDim2.new(1,-20,0,30)
  2541. choiceButton.Parent = multipleChoiceFrame
  2542. choiceButton.MouseButton1Down:connect(function()
  2543. if setting ~= i then
  2544. setting = i
  2545. button.Text = text
  2546. changedFunction(i, text)
  2547. end
  2548. closeMultiChoiceFrame()
  2549. end)
  2550. end
  2551. multipleChoiceFrame.Size = UDim2.new(0,180,0,50 + #choices*35)
  2552. multipleChoiceFrame.Visible = true
  2553. else
  2554. closeMultiChoiceFrame()
  2555. end
  2556. end)
  2557. frame.Parent = parent
  2558. frame.Visible = true
  2559. return frame
  2560. end
  2561. multipleChoiceFrame:WaitForChild('CloseButton').MouseButton1Down:connect(closeMultiChoiceFrame)
  2562.  
  2563. function createCheckBox(name, pos, parent, setting, changedFunction)
  2564. local setting = setting
  2565. local checkBoxGui = gui:WaitForChild('TemplateCheckBox'):clone()
  2566. checkBoxGui.Name = 'CheckBox'..name
  2567. checkBoxGui.Label.Text = name
  2568. checkBoxGui.Text = setting and 'X' or ''
  2569. checkBoxGui.Position = UDim2.new(0,4,0,pos)
  2570. checkBoxGui.Parent = parent
  2571. checkBoxGui.Visible = true
  2572. checkBoxGui.MouseButton1Down:connect(function()
  2573. setting = not setting
  2574. checkBoxGui.Text = setting and 'X' or ''
  2575. changedFunction(setting)
  2576. end)
  2577. return checkBoxGui
  2578. end
  2579.  
  2580.  
  2581. -------------------------------------------------------------------------------------------------------------------
  2582. -------------------------------------------------------------------------------------------------------------------
  2583.  
  2584. local rock = Enum.Material.Rock
  2585. local snow = Enum.Material.Snow
  2586. local ice = Enum.Material.Glacier
  2587. local grass = Enum.Material.Grass
  2588. local ground = Enum.Material.Ground
  2589. local mud = Enum.Material.Mud
  2590. local slate = Enum.Material.Slate
  2591. local concrete = Enum.Material.Concrete
  2592. local lava = Enum.Material.CrackedLava
  2593. local basalt = Enum.Material.Basalt
  2594. local air = Enum.Material.Air
  2595. local sand = Enum.Material.Sand
  2596. local sandstone = Enum.Material.Sandstone
  2597. local water = Enum.Material.Water
  2598.  
  2599. local fill = rock
  2600.  
  2601. local range = {}
  2602. function checkRange(v)
  2603. if not range.min or v < range.min then
  2604. range.min = v
  2605. end
  2606. if not range.max or v > range.max then
  2607. range.max = v
  2608. end
  2609. end
  2610.  
  2611. local function getPerlin(x,y,z,seed,scale,raw)
  2612. local seed = seed or 0
  2613. local scale = scale or 1
  2614. if not raw then
  2615. return noise(x/scale+(seed*17)+masterSeed,y/scale-masterSeed,z/scale-seed*seed)*.5 + .5 -- accounts for bleeding from interpolated line
  2616. else
  2617. return noise(x/scale+(seed*17)+masterSeed,y/scale-masterSeed,z/scale-seed*seed)
  2618. end
  2619. end
  2620.  
  2621.  
  2622. randomseed(6180339)
  2623. theseed={}
  2624. for i=1,999 do
  2625. table.insert(theseed,math.random())
  2626. end
  2627.  
  2628. local function getNoise(x,y,z,seed1)
  2629. local x = x or 0
  2630. local y = y or 0
  2631. local z = z or 0
  2632. local seed1 = seed1 or 7
  2633. 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
  2634. return theseed[(floor(wtf%(#theseed)))+1]
  2635. end
  2636.  
  2637. local function thresholdFilter(value, bottom, size)
  2638. if value <= bottom then
  2639. return 0
  2640. elseif value >= bottom+size then
  2641. return 1
  2642. else
  2643. return (value-bottom)/size
  2644. end
  2645. end
  2646.  
  2647. local function ridgedFilter(value) --absolute and flip for ridges. and normalize
  2648. return value<.5 and value*2 or 2-value*2
  2649. end
  2650.  
  2651. local function ridgedFlippedFilter(value) --unflipped
  2652. return value < .5 and 1-value*2 or value*2-1
  2653. end
  2654.  
  2655. local function advancedRidgedFilter(value, cutoff)
  2656. local cutoff = cutoff or .5
  2657. value = value - cutoff
  2658. return 1 - (value < 0 and -value or value) * 1/(1-cutoff)
  2659. end
  2660.  
  2661.  
  2662. local function fractalize(operation,x,y,z, operationCount, scale, offset, gain)
  2663. local operationCount = operationCount or 3
  2664. local scale = scale or .5
  2665. local offset = 0
  2666. local gain = gain or 1
  2667. local totalValue = 0
  2668. local totalScale = 0
  2669. for i=1, operationCount do
  2670. local thisScale = scale^(i-1)
  2671. totalScale = totalScale + thisScale
  2672. totalValue = totalValue + (offset + gain * operation(x,y,z,i))*thisScale
  2673. end
  2674. return totalValue/totalScale
  2675. end
  2676.  
  2677.  
  2678. local function mountainsOperation(x,y,z,i)
  2679. return ridgedFilter(getPerlin(x,y,z,100+i,(1/i)*160))
  2680. end
  2681.  
  2682. local canyonBandingMaterial = {rock,mud,sand,sand,sandstone,sandstone,sandstone,sandstone,sandstone,sandstone,}
  2683.  
  2684. local function findBiomeInfo(choiceBiome,x,y,z,verticalGradientTurbulence)
  2685. local choiceBiomeValue = .5
  2686. local choiceBiomeSurface = grass
  2687. local choiceBiomeFill = rock
  2688. if choiceBiome == 'City' then
  2689. choiceBiomeValue = .55
  2690. choiceBiomeSurface = concrete
  2691. choiceBiomeFill = slate
  2692. elseif choiceBiome == 'Water' then
  2693. choiceBiomeValue = .36+getPerlin(x,y,z,2,50)*.08
  2694. choiceBiomeSurface =
  2695. (1-verticalGradientTurbulence < .44 and slate)
  2696. or sand
  2697. elseif choiceBiome == 'Marsh' then
  2698. 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
  2699. local grassyLedge = thresholdFilter(preLedge,.65,0)
  2700. local largeGradient = getPerlin(x,y,z,4,100)
  2701. local smallGradient = getPerlin(x,y,z,3,20)
  2702. local smallGradientThreshold = thresholdFilter(smallGradient,.5,0)
  2703. choiceBiomeValue = waterLevel-.04
  2704. +preLedge*grassyLedge*.025
  2705. +largeGradient*.035
  2706. +smallGradient*.025
  2707. choiceBiomeSurface =
  2708. (grassyLedge >= 1 and grass)
  2709. or (1-verticalGradientTurbulence < waterLevel-.01 and mud)
  2710. or (1-verticalGradientTurbulence < waterLevel+.01 and ground)
  2711. or grass
  2712. choiceBiomeFill = slate
  2713. elseif choiceBiome == 'Plains' then
  2714. local rivulet = ridgedFlippedFilter(getPerlin(x+getPerlin(x,y,z,17,40)*25,0,z+getPerlin(x,y,z,19,40)*25,2,200))
  2715. local rivuletThreshold = thresholdFilter(rivulet,.01,0)
  2716.  
  2717. local rockMap = thresholdFilter(ridgedFlippedFilter(getPerlin(x,0,z,101,7)),.3,.7) --rocks
  2718. * thresholdFilter(getPerlin(x,0,z,102,50),.6,.05) --zoning
  2719.  
  2720. choiceBiomeValue = .5 --.51
  2721. +getPerlin(x,y,z,2,100)*.02 --.05
  2722. +rivulet*.05 --.02
  2723. +rockMap*.05 --.03
  2724. +rivuletThreshold*.005
  2725.  
  2726. local verticalGradient = 1-((y-1)/(mapHeight-1))
  2727. local surfaceGradient = verticalGradient*.5 + choiceBiomeValue*.5
  2728. local thinSurface = surfaceGradient > .5-surfaceThickness*.4 and surfaceGradient < .5+surfaceThickness*.4
  2729. choiceBiomeSurface =
  2730. (rockMap>0 and rock)
  2731. or (not thinSurface and mud)
  2732. or (thinSurface and rivuletThreshold <=0 and water)
  2733. or (1-verticalGradientTurbulence < waterLevel-.01 and sand)
  2734. or grass
  2735. choiceBiomeFill =
  2736. (rockMap>0 and rock)
  2737. or sandstone
  2738. elseif choiceBiome == 'Canyons' then
  2739. local canyonNoise = ridgedFlippedFilter(getPerlin(x,0,z,2,200))
  2740. 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))
  2741. local sandbank = thresholdFilter(canyonNoiseTurbed,0,.05)
  2742. local canyonTop = thresholdFilter(canyonNoiseTurbed,.125,0)
  2743. local mesaSlope = thresholdFilter(canyonNoise,.33,.12)
  2744. local mesaTop = thresholdFilter(canyonNoiseTurbed,.49,0)
  2745. choiceBiomeValue = .42
  2746. +getPerlin(x,y,z,2,70)*.05
  2747. +canyonNoise*.05
  2748. +sandbank*.04 --canyon bottom slope
  2749. +thresholdFilter(canyonNoiseTurbed,.05,0)*.08 --canyon cliff
  2750. +thresholdFilter(canyonNoiseTurbed,.05,.075)*.04 --canyon cliff top slope
  2751. +canyonTop*.01 --canyon cliff top ledge
  2752.  
  2753. +thresholdFilter(canyonNoiseTurbed,.0575,.2725)*.01 --plane slope
  2754.  
  2755. +mesaSlope*.06 --mesa slope
  2756. +thresholdFilter(canyonNoiseTurbed,.45,0)*.14 --mesa cliff
  2757. +thresholdFilter(canyonNoiseTurbed,.45,.04)*.025 --mesa cap
  2758. +mesaTop*.02 --mesa top ledge
  2759. choiceBiomeSurface =
  2760. (1-verticalGradientTurbulence < waterLevel+.015 and sand) --this for biome blending in to lakes
  2761. or (sandbank>0 and sandbank<1 and sand) --this for canyonbase sandbanks
  2762. --or (canyonTop>0 and canyonTop<=1 and mesaSlope<=0 and grass) --this for grassy canyon tops
  2763. --or (mesaTop>0 and mesaTop<=1 and grass) --this for grassy mesa tops
  2764. or sandstone
  2765. choiceBiomeFill = canyonBandingMaterial[ceil((1-getNoise(1,y,2))*10)]
  2766. elseif choiceBiome == 'Hills' then
  2767. 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)
  2768. local largeHills = getPerlin(x,y,z,3,60)
  2769. choiceBiomeValue = .48
  2770. +largeHills*.05
  2771. +(.05
  2772. +largeHills*.1
  2773. +getPerlin(x,y,z,4,25)*.125)
  2774. *rivulet
  2775. local surfaceMaterialGradient = (1-verticalGradientTurbulence)*.9 + rivulet*.1
  2776. choiceBiomeSurface =
  2777. (surfaceMaterialGradient < waterLevel-.015 and mud)
  2778. or (surfaceMaterialGradient < waterLevel and ground)
  2779. or grass
  2780. choiceBiomeFill = slate
  2781. elseif choiceBiome == 'Dunes' then
  2782. local duneTurbulence = getPerlin(x,0,z,227,20)*24
  2783. local layer1 = ridgedFilter(getPerlin(x,0,z,201,40))
  2784. local layer2 = ridgedFilter(getPerlin(x/10+duneTurbulence,0,z+duneTurbulence,200,48))
  2785. choiceBiomeValue = .4+.1*(layer1 + layer2)
  2786. choiceBiomeSurface = sand
  2787. choiceBiomeFill = sandstone
  2788. elseif choiceBiome == 'Mountains' then
  2789. local rivulet = ridgedFlippedFilter(getPerlin(x+getPerlin(x,y,z,17,20)*20,0,z+getPerlin(x,y,z,19,20)*20,2,200))
  2790. choiceBiomeValue = -.4 --.3
  2791. +fractalize(mountainsOperation,x,y/20,z, 8, .65)*1.2
  2792. +rivulet*.2
  2793. choiceBiomeSurface =
  2794. (verticalGradientTurbulence < .275 and snow)
  2795. or (verticalGradientTurbulence < .35 and rock)
  2796. or (verticalGradientTurbulence < .4 and ground)
  2797. or (1-verticalGradientTurbulence < waterLevel and rock)
  2798. or (1-verticalGradientTurbulence < waterLevel+.01 and mud)
  2799. or (1-verticalGradientTurbulence < waterLevel+.015 and ground)
  2800. or grass
  2801. elseif choiceBiome == 'Lavaflow' then
  2802. local crackX = x+getPerlin(x,y*.25,z,21,8,true)*5
  2803. local crackY = y+getPerlin(x,y*.25,z,22,8,true)*5
  2804. local crackZ = z+getPerlin(x,y*.25,z,23,8,true)*5
  2805. 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))
  2806. local crack2 = ridgedFilter(getPerlin(crackX,crackY,crackZ,3,40))*(crack1*.25+.75)
  2807. local crack3 = ridgedFilter(getPerlin(crackX,crackY,crackZ,4,20))*(crack2*.25+.75)
  2808.  
  2809. local generalHills = thresholdFilter(getPerlin(x,y,z,9,40),.25,.5)*getPerlin(x,y,z,10,60)
  2810.  
  2811. local cracks = max(0,1-thresholdFilter(crack1,.975,0)-thresholdFilter(crack2,.925,0)-thresholdFilter(crack3,.9,0))
  2812.  
  2813. local spireVec = CFrame.Angles(.7,.7,0)*Vector3.new(crackX,crackY,crackZ)
  2814. local spires = thresholdFilter(getPerlin(spireVec.x/40,spireVec.y/300,spireVec.z/30,123,1),.6,.4)
  2815.  
  2816. choiceBiomeValue = waterLevel+.02
  2817. +cracks*(.5+generalHills*.5)*.02
  2818. +generalHills*.05
  2819. +spires*.3
  2820. +((1-verticalGradientTurbulence > waterLevel+.01 or spires>0) and .04 or 0) --This lets it lip over water
  2821.  
  2822. choiceBiomeFill = (spires>0 and rock) or (cracks<1 and lava) or basalt
  2823. choiceBiomeSurface = (choiceBiomeFill == lava and 1-verticalGradientTurbulence < waterLevel and basalt) or choiceBiomeFill
  2824. elseif choiceBiome == 'Arctic' then
  2825. 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)
  2826. --local cliffs = thresholdFilter(preBoundary,.5,0)
  2827. local boundary = ridgedFilter(preBoundary)
  2828. local roughChunks = getPerlin(x,y/4,z,436,2)
  2829. local boundaryMask = thresholdFilter(boundary,.8,.1) --,.7,.25)
  2830. local boundaryTypeMask = getPerlin(x,0,z,6,74)-.5
  2831. local boundaryComp = 0
  2832. if boundaryTypeMask < 0 then --divergent
  2833. boundaryComp = (boundary > (1+boundaryTypeMask*.5) and -.17 or 0)
  2834. --* boundaryTypeMask*-2
  2835. else --convergent
  2836. boundaryComp = boundaryMask*.1*roughChunks
  2837. * boundaryTypeMask
  2838. end
  2839. choiceBiomeValue = .55
  2840. +boundary*.05*boundaryTypeMask --.1 --soft slope up or down to boundary
  2841. +boundaryComp --convergent/divergent effects
  2842. +getPerlin(x,0,z,123,25)*.025 --*cliffs --gentle rolling slopes
  2843.  
  2844. choiceBiomeSurface = (1-verticalGradientTurbulence < waterLevel-.1 and ice) or (boundaryMask>.6 and boundaryTypeMask>.1 and roughChunks>.5 and ice) or snow
  2845. choiceBiomeFill = ice
  2846. end
  2847. return choiceBiomeValue, choiceBiomeSurface, choiceBiomeFill
  2848. end
  2849.  
  2850. function findBiomeTransitionValue(biome,weight,value,averageValue)
  2851. if biome == 'Arctic' then
  2852. return (weight>.2 and 1 or 0)*value
  2853. elseif biome == 'Canyons' then
  2854. return (weight>.7 and 1 or 0)*value
  2855. elseif biome == 'Mountains' then
  2856. local weight = weight^3 --This improves the ease of mountains transitioning to other biomes
  2857. return averageValue*(1-weight)+value*weight
  2858. else
  2859. return averageValue*(1-weight)+value*weight
  2860. end
  2861. end
  2862.  
  2863. function updatePausedButton()
  2864. pauseButton.Style = paused and 'RobloxRoundButton' or 'RobloxRoundDefaultButton'
  2865. pauseButton.Text = paused and 'Paused' or 'Pause'
  2866. end
  2867.  
  2868. function generate()
  2869. if not generating and not clearing then
  2870. generating = true
  2871. paused = false
  2872. updatePausedButton()
  2873. cancelIt = false
  2874. progressFrame.Visible = true
  2875.  
  2876. local mapWidth = mapWidth
  2877. local biomeSize = biomeSize
  2878. local biomeBlendPercent = .25 --(biomeSize==50 or biomeSize == 100) and .5 or .25
  2879. local biomeBlendPercentInverse = 1-biomeBlendPercent
  2880. local biomeBlendDistortion = biomeBlendPercent
  2881. local smoothScale = .5/mapHeight
  2882.  
  2883. local startTime = tick()
  2884. biomes = {}
  2885. for i,v in pairs(allBiomes) do
  2886. if v then
  2887. table.insert(biomes,i)
  2888. end
  2889. end
  2890. if #biomes<=0 then
  2891. table.insert(biomes,'Hills')
  2892. end
  2893. --local oMap = {}
  2894. --local mMap = {}
  2895. for x = 1, mapWidth do
  2896. local oMapX = {}
  2897. --oMap[x] = oMapX
  2898. local mMapX = {}
  2899. --mMap[x] = mMapX
  2900. for z = 1, mapWidth do
  2901. local biomeNoCave = false
  2902. local cellToBiomeX = x/biomeSize + getPerlin(x,0,z,233,biomeSize*.3)*.25 + getPerlin(x,0,z,235,biomeSize*.05)*.075
  2903. local cellToBiomeZ = z/biomeSize + getPerlin(x,0,z,234,biomeSize*.3)*.25 + getPerlin(x,0,z,236,biomeSize*.05)*.075
  2904. local closestPoint = Vector3.new(0,0,0)
  2905. local closestDistance = 1000000
  2906. local biomePoints = {}
  2907. for vx=-1,1 do
  2908. for vz=-1,1 do
  2909. local gridPointX = floor(cellToBiomeX+vx+.5)
  2910. local gridPointZ = floor(cellToBiomeZ+vz+.5)
  2911. --local pointX, pointZ = getBiomePoint(gridPointX,gridPointZ)
  2912. local pointX = gridPointX+(getNoise(gridPointX,gridPointZ,53)-.5)*.75 --de-uniforming grid for vornonoi
  2913. local pointZ = gridPointZ+(getNoise(gridPointX,gridPointZ,73)-.5)*.75
  2914.  
  2915. local dist = sqrt((pointX-cellToBiomeX)^2 + (pointZ-cellToBiomeZ)^2)
  2916. if dist < closestDistance then
  2917. closestPoint = Vector3.new(pointX,0,pointZ)
  2918. closestDistance = dist
  2919. end
  2920. table.insert(biomePoints,{
  2921. x = pointX,
  2922. z = pointZ,
  2923. dist = dist,
  2924. biomeNoise = getNoise(gridPointX,gridPointZ),
  2925. weight = 0
  2926. })
  2927. end
  2928. end
  2929. local weightTotal = 0
  2930. local weightPoints = {}
  2931. for _,point in pairs(biomePoints) do
  2932. local weight = point.dist == closestDistance and 1 or ((closestDistance / point.dist)-biomeBlendPercentInverse)/biomeBlendPercent
  2933. if weight > 0 then
  2934. local weight = weight^2.1 --this smooths the biome transition from linear to cubic InOut
  2935. weightTotal = weightTotal + weight
  2936. 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
  2937. weightPoints[biome] = {
  2938. weight = weightPoints[biome] and weightPoints[biome].weight + weight or weight
  2939. }
  2940. end
  2941. end
  2942. for biome,info in pairs(weightPoints) do
  2943. info.weight = info.weight / weightTotal
  2944. if biome == 'Arctic' then --biomes that don't have caves that breach the surface
  2945. biomeNoCave = true
  2946. end
  2947. end
  2948.  
  2949.  
  2950. for y = 1, mapHeight do
  2951. local oMapY = oMapX[y] or {}
  2952. oMapX[y] = oMapY
  2953. local mMapY = mMapX[y] or {}
  2954. mMapX[y] = mMapY
  2955.  
  2956. --[[local oMapY = {}
  2957. oMapX[y] = oMapY
  2958. local mMapY = {}
  2959. mMapX[z] = mMapY]]
  2960.  
  2961.  
  2962. local verticalGradient = 1-((y-1)/(mapHeight-1))
  2963. local caves = 0
  2964. local verticalGradientTurbulence = verticalGradient*.9 + .1*getPerlin(x,y,z,107,15)
  2965. local choiceValue = 0
  2966. local choiceSurface = lava
  2967. local choiceFill = rock
  2968.  
  2969. if verticalGradient > .65 or verticalGradient < .1 then
  2970. --under surface of every biome; don't get biome data; waste of time.
  2971. choiceValue = .5
  2972. elseif #biomes == 1 then
  2973. choiceValue, choiceSurface, choiceFill = findBiomeInfo(biomes[1],x,y,z,verticalGradientTurbulence)
  2974. else
  2975. local averageValue = 0
  2976. --local findChoiceMaterial = -getNoise(x,y,z,19)
  2977. for biome,info in pairs(weightPoints) do
  2978. local biomeValue, biomeSurface, biomeFill = findBiomeInfo(biome,x,y,z,verticalGradientTurbulence)
  2979. info.biomeValue = biomeValue
  2980. info.biomeSurface = biomeSurface
  2981. info.biomeFill = biomeFill
  2982. local value = biomeValue * info.weight
  2983. averageValue = averageValue + value
  2984. --[[if findChoiceMaterial < 0 and findChoiceMaterial + weight >= 0 then
  2985. choiceMaterial = biomeMaterial
  2986. end
  2987. findChoiceMaterial = findChoiceMaterial + weight]]
  2988. end
  2989. for biome,info in pairs(weightPoints) do
  2990. local value = findBiomeTransitionValue(biome,info.weight,info.biomeValue,averageValue)
  2991. if value > choiceValue then
  2992. choiceValue = value
  2993. choiceSurface = info.biomeSurface
  2994. choiceFill = info.biomeFill
  2995. end
  2996. end
  2997. end
  2998.  
  2999. local preCaveComp = verticalGradient*.5 + choiceValue*.5
  3000.  
  3001. local surface = preCaveComp > .5-surfaceThickness and preCaveComp < .5+surfaceThickness
  3002.  
  3003. if generateCaves --user wants caves
  3004. and (not biomeNoCave or verticalGradient > .65) --biome allows caves or deep enough
  3005. and not (surface and (1-verticalGradient) < waterLevel+.005) --caves only breach surface above waterlevel
  3006. and not (surface and (1-verticalGradient) > waterLevel+.58) then --caves don't go too high so that they don't cut up mountain tops
  3007. local ridged2 = ridgedFilter(getPerlin(x,y,z,4,30))
  3008. local caves2 = thresholdFilter(ridged2,.84,.01)
  3009. local ridged3 = ridgedFilter(getPerlin(x,y,z,5,30))
  3010. local caves3 = thresholdFilter(ridged3,.84,.01)
  3011. local ridged4 = ridgedFilter(getPerlin(x,y,z,6,30))
  3012. local caves4 = thresholdFilter(ridged4,.84,.01)
  3013. local caveOpenings = (surface and 1 or 0) * thresholdFilter(getPerlin(x,0,z,143,62),.35,0) --.45
  3014. caves = caves2 * caves3 * caves4 - caveOpenings
  3015. caves = caves < 0 and 0 or caves > 1 and 1 or caves
  3016. end
  3017.  
  3018. local comp = preCaveComp - caves
  3019.  
  3020. local smoothedResult = thresholdFilter(comp,.5,smoothScale)
  3021.  
  3022. ---below water level -above surface -no terrain
  3023. if 1-verticalGradient < waterLevel and preCaveComp <= .5 and smoothedResult <= 0 then
  3024. smoothedResult = 1
  3025. choiceSurface = water
  3026. choiceFill = water
  3027. surface = true
  3028. end
  3029.  
  3030. oMapY[z] = (y == 1 and 1) or smoothedResult
  3031. mMapY[z] = (y == 1 and lava) or (smoothedResult <= 0 and air) or (surface and choiceSurface) or choiceFill
  3032. end
  3033. end
  3034.  
  3035. local regionStart = Vector3.new(mapWidth*-2+(x-1)*4,mapHeight*-2,mapWidth*-2)
  3036. local regionEnd = Vector3.new(mapWidth*-2+x*4,mapHeight*2,mapWidth*2)
  3037. local mapRegion = Region3.new(regionStart, regionEnd)
  3038. terrain:WriteVoxels(mapRegion, 4, {mMapX}, {oMapX})
  3039.  
  3040. local completionPercent = x/mapWidth
  3041. barFill.Size = UDim2.new(completionPercent,0,1,0)
  3042.  
  3043. wait()
  3044. while paused and not cancelIt do
  3045. wait()
  3046. end
  3047. if cancelIt then
  3048. break
  3049. end
  3050. end
  3051. changeHistoryService:SetWaypoint('Terrain Generation')
  3052. progressFrame.Visible = false
  3053. generating = false
  3054. print('Generation Complete',tick()-startTime)
  3055. end
  3056. end
  3057.  
  3058.  
  3059. --------------------------------------------------------------------------------------------------------------------
  3060. ------------------------------------------------Setup User Interface------------------------------------------------
  3061.  
  3062. local mapSizes = {128,256,512,1024}
  3063. local mapSizeChangeFunction = function(index,text)
  3064. mapWidth = mapSizes[index]
  3065. end
  3066. createMultiChoice('Map Size', 5, canvas, {'Small (128)','Medium (256)','Large (512)','Massive (1024)'}, 2, mapSizeChangeFunction)
  3067.  
  3068. local seedChangeFunction = function(text)
  3069. local compositeNumber = 0
  3070. for i=1,#text do
  3071. local character = string.sub(text,i,i)
  3072. local number = tonumber(character)
  3073. if number then
  3074. compositeNumber = (compositeNumber+6)*(number+5)
  3075. else
  3076. compositeNumber = (compositeNumber+7)*(string.byte(character)+3)
  3077. end
  3078. compositeNumber = compositeNumber%61803 --yes, this does need to be done after every character iteration, otherwise number loses precision by the end
  3079. end
  3080. masterSeed = compositeNumber
  3081. end
  3082. createTextBox('Seed', 65, canvas, masterSeed, seedChangeFunction)
  3083.  
  3084. local cavesChangeFunction = function(value)
  3085. generateCaves = value
  3086. end
  3087. local cavesCheckBox = createCheckBox('Caves', 100, canvas, generateCaves, cavesChangeFunction)
  3088.  
  3089. local biomeSizes = {50,100,200,500}
  3090. local biomeSizeChangeFunction = function(index,text)
  3091. biomeSize = biomeSizes[index]
  3092. end
  3093. createMultiChoice('Biome Size', 135, canvas, {'Small (50)','Medium (100)','Large (200)','Massive (500)'}, 2, biomeSizeChangeFunction)
  3094.  
  3095. local biomesSettingsButton, biomesFrame = createSection('Biomes',200,275,canvas)
  3096.  
  3097. local waterChangeFunction = function(value)
  3098. allBiomes['Water'] = value
  3099. end
  3100. local waterCheckBox = createCheckBox('Water', 5, biomesFrame, allBiomes['Water'], waterChangeFunction)
  3101.  
  3102. local marshChangeFunction = function(value)
  3103. allBiomes['Marsh'] = value
  3104. end
  3105. local marshCheckBox = createCheckBox('Marsh', 35, biomesFrame, allBiomes['Marsh'], marshChangeFunction)
  3106.  
  3107. local plainsChangeFunction = function(value)
  3108. allBiomes['Plains'] = value
  3109. end
  3110. local plainsCheckBox = createCheckBox('Plains', 65, biomesFrame, allBiomes['Plains'], plainsChangeFunction)
  3111.  
  3112. local hillsChangeFunction = function(value)
  3113. allBiomes['Hills'] = value
  3114. end
  3115. local hillsCheckBox = createCheckBox('Hills', 95, biomesFrame, allBiomes['Hills'], hillsChangeFunction)
  3116.  
  3117. local dunesChangeFunction = function(value)
  3118. allBiomes['Dunes'] = value
  3119. end
  3120. local dunesCheckBox = createCheckBox('Dunes', 125, biomesFrame, allBiomes['Dunes'], dunesChangeFunction)
  3121.  
  3122. local canyonsChangeFunction = function(value)
  3123. allBiomes['Canyons'] = value
  3124. end
  3125. local canyonsCheckBox = createCheckBox('Canyons', 155, biomesFrame, allBiomes['Canyons'], canyonsChangeFunction)
  3126.  
  3127. local mountainsChangeFunction = function(value)
  3128. allBiomes['Mountains'] = value
  3129. end
  3130. local mountainsCheckBox = createCheckBox('Mountains', 185, biomesFrame, allBiomes['Mountains'], mountainsChangeFunction)
  3131.  
  3132. local lavaflowChangeFunction = function(value)
  3133. allBiomes['Lavaflow'] = value
  3134. end
  3135. local lavaflowCheckBox = createCheckBox('Lavascape', 215, biomesFrame, allBiomes['Lavaflow'], lavaflowChangeFunction)
  3136.  
  3137. local arcticChangeFunction = function(value)
  3138. allBiomes['Arctic'] = value
  3139. end
  3140. local arcticCheckBox = createCheckBox('Arctic', 245, biomesFrame, allBiomes['Arctic'], arcticChangeFunction)
  3141.  
  3142.  
  3143. --------------------------------------------------------------------------------------------------------------------
  3144. --------------------------------------------------------------------------------------------------------------------
  3145.  
  3146.  
  3147. function On(mouseHandMeDown,turnOffHandMeDown)
  3148. gui.Parent = coreGui
  3149. on = true
  3150. if mouseHandMeDown then
  3151. mouse = mouseHandMeDown
  3152. end
  3153. if turnOffHandMeDown then
  3154. turnOff = turnOffHandMeDown
  3155. end
  3156. end
  3157.  
  3158. function Off()
  3159. closeMultiChoiceFrame()
  3160. if turnOff then
  3161. turnOff()
  3162. end
  3163. on = false
  3164. gui.Parent = nil
  3165. end
  3166.  
  3167. function clearTerrain()
  3168. if not generating and not clearing then
  3169. clearing = true
  3170. terrain:Clear()
  3171. changeHistoryService:SetWaypoint('Terrain Clear')
  3172. clearing = false
  3173. end
  3174. end
  3175.  
  3176. pauseButton.MouseButton1Down:connect(function()
  3177. paused = not paused
  3178. updatePausedButton()
  3179. end)
  3180.  
  3181. cancelButton.MouseButton1Down:connect(function()
  3182. if not cancelIt then
  3183. cancelIt = true
  3184. print('Canceled')
  3185. end
  3186. end)
  3187.  
  3188. mainFrame:WaitForChild('CloseButton').MouseButton1Down:connect(function()
  3189. Off()
  3190. end)
  3191.  
  3192. mainFrame:WaitForChild('GenerateButton').MouseButton1Down:connect(function()
  3193. generate()
  3194. end)
  3195.  
  3196. mainFrame:WaitForChild('ClearButton').MouseButton1Down:connect(function()
  3197. clearTerrain()
  3198. end)
  3199.  
  3200.  
  3201. return {
  3202. ['On'] = On,
  3203. ['Off'] = Off,
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement