From 98561c76e0f32efb566944d22c090fc3805b0d60 Mon Sep 17 00:00:00 2001 From: Lewin Kelly Date: Wed, 10 Apr 2024 07:44:11 +0100 Subject: [PATCH] Formatting improvements and modularisation of terrain plugins --- Modules/Terrain/CreateStandardButton.luau | 34 ++ Modules/Terrain/CreateStandardLabel.luau | 30 + terrain plugins/00 - terrain.luau | 459 +++++++-------- terrain plugins/01 - builder.luau | 63 +-- terrain plugins/02 - remover.luau | 35 +- terrain plugins/03 - elevation.luau | 106 ++-- terrain plugins/04 - brush.luau | 70 +-- terrain plugins/06 - craters.luau | 24 +- terrain plugins/08 - roads.luau | 11 +- terrain plugins/09 - materialpaint.luau | 645 +++++++++++----------- terrain plugins/10 - stamper.luau | 393 +++++++------ terrain plugins/11 - floodfill.luau | 415 +++++++------- 12 files changed, 1087 insertions(+), 1198 deletions(-) create mode 100644 Modules/Terrain/CreateStandardButton.luau create mode 100644 Modules/Terrain/CreateStandardLabel.luau diff --git a/Modules/Terrain/CreateStandardButton.luau b/Modules/Terrain/CreateStandardButton.luau new file mode 100644 index 0000000..17d9ff7 --- /dev/null +++ b/Modules/Terrain/CreateStandardButton.luau @@ -0,0 +1,34 @@ +local New = require("../New").New + +-- Create a standard dropdown. Use this for all dropdowns in the popup so it is easy to standardize. +-- name - What to use. +-- pos - Where to position the button. Should be of type UDim2. +-- text - Text to show in the button. +-- funcOnPress - Function to run when the button is pressed. +-- parent - What to set the parent as. +-- Return: +-- button - The button gui. +return function( + name: string, + pos: UDim2, + text: string, + funcOnPress: (...any) -> (), + parent: Instance, + size: UDim2? +) + local button = New "TextButton" { + Name = name, + Position = pos, + Size = size or UDim2.new(0, 120, 0, 40), + Text = text, + Style = Enum.ButtonStyle.RobloxButton, + TextColor3 = Color3.new(1, 1, 1), + Font = Enum.Font.ArialBold, + FontSize = Enum.FontSize.Size18, + } + + button.MouseButton1Click:connect(funcOnPress) + button.Parent = parent + + return button +end diff --git a/Modules/Terrain/CreateStandardLabel.luau b/Modules/Terrain/CreateStandardLabel.luau new file mode 100644 index 0000000..e3b7cb9 --- /dev/null +++ b/Modules/Terrain/CreateStandardLabel.luau @@ -0,0 +1,30 @@ +local New = require("../New").New + +-- Create a standard text label. Use this for all lables in the popup so it is easy to standardize. +-- labelName - What to set the text label name as. +-- pos - Where to position the label. Should be of type UDim2. +-- size - How large to make the label. Should be of type UDim2. +-- text - Text to display. +-- parent - What to set the text parent as. +-- Return: +-- Value is the created label. +return function( + labelName: string, + pos: UDim2, + size: UDim2, + text: string, + parent: Instance? +) + return New "TextLabel" { + Name = labelName, + Position = pos, + Size = size, + Text = text, + TextColor3 = Color3.new(0.95, 0.95, 0.95), + Font = Enum.Font.ArialBold, + FontSize = Enum.FontSize.Size14, + TextXAlignment = Enum.TextXAlignment.Left, + BackgroundTransparency = 1, + Parent = parent, + } +end diff --git a/terrain plugins/00 - terrain.luau b/terrain plugins/00 - terrain.luau index ac6f424..59e7873 100644 --- a/terrain plugins/00 - terrain.luau +++ b/terrain plugins/00 - terrain.luau @@ -1,11 +1,17 @@ --- Local function definitions -while game == nil do +--!strict +while not game do wait() end local ChangeHistoryService = game:GetService "ChangeHistoryService" local CoreGui = game:GetService "CoreGui" +local CreateStandardButton = require "../Modules/Terrain/CreateStandardButton" +local CreateStandardLabel = require "../Modules/Terrain/CreateStandardLabel" +local News = require "../Modules/New" +local New = News.New +local Hydrate = News.Hydrate + --------------- --PLUGIN SETUP- --------------- @@ -147,18 +153,20 @@ end) terrainHelpFrame.Size = UDim2.new(0, 300, 0, 350) -local terrainHelpText = Instance.new "TextLabel" -terrainHelpText.Name = "HelpText" -terrainHelpText.Font = Enum.Font.ArialBold -terrainHelpText.FontSize = Enum.FontSize.Size12 -terrainHelpText.TextColor3 = Color3.new(227 / 255, 227 / 255, 227 / 255) -terrainHelpText.TextXAlignment = Enum.TextXAlignment.Left -terrainHelpText.TextYAlignment = Enum.TextYAlignment.Top -terrainHelpText.Position = UDim2.new(0, 4, 0, 4) -terrainHelpText.Size = UDim2.new(1, -8, 0, 157) -terrainHelpText.BackgroundTransparency = 1 -terrainHelpText.TextWrap = true -terrainHelpText.Text = [[Create terrain with hills, water. +local function helpText() + return New "TextLabel" { + Font = Enum.Font.ArialBold, + FontSize = Enum.FontSize.Size12, + TextColor3 = Color3.new(227 / 255, 227 / 255, 227 / 255), + TextXAlignment = Enum.TextXAlignment.Left, + TextYAlignment = Enum.TextYAlignment.Top, + BackgroundTransparency = 1, + TextWrap = true, + Parent = terrainHelpFrame, + } +end + +local helpText1 = [[Create terrain with hills, water. X-Offset and Z-Offset: Center point of terrain that will be created. Terrain must be in a specific region. If part of the terrain is outside that region, it won't be created. @@ -170,13 +178,7 @@ Length: Terrain size in the Z direction Amplitude: Maximum height of hills. ]] -terrainHelpText.Parent = terrainHelpFrame - -local helpSecondText = terrainHelpText:clone() -helpSecondText.Name = "HelpSecondText" -helpSecondText.Position = UDim2.new(0, 0, 1, 0) -helpSecondText.Size = UDim2.new(1, 0, 0, 180) -helpSecondText.Text = [[Frequency: +local helpText2 = [[Frequency: How often hills are made. Base Height: @@ -185,21 +187,34 @@ How high the base of terrain should be. Water Height: How high water should be. Terrain will overwrite water. ]] -helpSecondText.Parent = terrainHelpText - -local helpThirdText = helpSecondText:clone() -helpThirdText.Name = "HelpThirdText" -helpThirdText.Position = UDim2.new(0, 0, 0.6, 0) -helpThirdText.Size = UDim2.new(1, 0, 0, 180) -helpThirdText.Text = [[ - +local helpText3 = [[ + Generate Button: Create the terrain. Clear Button: Remove all terrain. ]] -helpThirdText.Parent = helpSecondText + +Hydrate(helpText()) { + Name = "HelpText", + Position = UDim2.new(0, 4, 0, 4), + Size = UDim2.new(1, -8, 0, 157), + Text = helpText1, + Parent = terrainHelpFrame, + Hydrate(helpText()) { + Name = "HelpSecondText", + Position = UDim2.new(0, 0, 1, 0), + Size = UDim2.new(1, 0, 0, 180), + Text = helpText2, + Hydrate(helpText()) { + Name = "HelpThirdText", + Position = UDim2.new(0, 0, 0.6, 0), + Size = UDim2.new(1, 0, 0, 180), + Text = helpText3, + }, + }, +} -- Used to create a highlighter. -- A highlighter is a open, rectuangular area displayed in 3D. @@ -225,21 +240,22 @@ function Highlighter.Create(_, ID, color) highlighter.z = 0 -- Create the part that the highlighter will be attached to. - highlighter.selectionPart = Instance.new "Part" - highlighter.selectionPart.Name = "SelectionPart" - highlighter.selectionPart.Archivable = false - highlighter.selectionPart.Transparency = 1 - highlighter.selectionPart.Anchored = true - highlighter.selectionPart.Locked = true - highlighter.selectionPart.CanCollide = false - highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom + highlighter.selectionPart = New "Part" { + Name = "SelectionPart", + Archivable = false, + Transparency = 1, + Anchored = true, + Locked = true, + CanCollide = false, + FormFactor = Enum.FormFactor.Custom, + } - highlighter.selectionBox = Instance.new "SelectionBox" - highlighter.selectionBox.Name = "box" .. ID - - highlighter.selectionBox.Archivable = false - highlighter.selectionBox.Color = color - highlighter.selectionBox.Adornee = highlighter.selectionPart + highlighter.selectionBox = New "SelectionBox" { + Name = "box" .. ID, + Archivable = false, + Color = color, + Adornee = highlighter.selectionPart, + } setmetatable(highlighter, Highlighter) return highlighter @@ -257,7 +273,7 @@ end -- Update where the highlighter is displayed. -- cellPos - Where to display the highlighter, in cells. -function Highlighter:UpdatePosition(cellPos) +function Highlighter:UpdatePosition(cellPos: Vector3) if not cellPos then self:DisablePreview() return @@ -269,17 +285,20 @@ function Highlighter:UpdatePosition(cellPos) self.y = cellPos.y self.z = cellPos.z + local width, length, height = + self.width :: number, self.length :: number, self.height :: number + local lowVec = CellCenterToWorld( c, - cellPos.x - self.width / 2, + cellPos.x - width / 2, cellPos.y - 1, - cellPos.z - self.length / 2 + cellPos.z - length / 2 ) local highVec = CellCenterToWorld( c, - cellPos.x + self.width / 2, - cellPos.y + self.height, - cellPos.z + self.length / 2 + cellPos.x + width / 2, + cellPos.y + height, + cellPos.z + length / 2 ) regionToSelect = Region3.new(lowVec, highVec) @@ -292,37 +311,17 @@ end -- length - Length of the area (z direction) -- width - Width of the area (x direction) -- height - Height of the area (y direction) -function Highlighter:UpdateDimensions(length, width, height) +function Highlighter:UpdateDimensions( + length: number, + width: number, + height: number +) self.length = length self.width = width self.height = height self:UpdatePosition(Vector3.new(self.x, self.y, self.z)) end --- Create a standard text label. Use this for all lables in the popup so it is easy to standardize. --- labelName - What to set the text label name as. --- pos - Where to position the label. Should be of type UDim2. --- size - How large to make the label. Should be of type UDim2. --- text - Text to display. --- parent - What to set the text parent as. --- Return: --- Value is the created label. -function CreateStandardLabel(labelName, pos, size, text, parent) - local label = Instance.new "TextLabel" - label.Name = labelName - label.Position = pos - label.Size = size - label.Text = text - label.TextColor3 = Color3.new(0.95, 0.95, 0.95) - label.Font = Enum.Font.ArialBold - label.FontSize = Enum.FontSize.Size14 - label.TextXAlignment = Enum.TextXAlignment.Left - label.BackgroundTransparency = 1 - label.Parent = parent - - return label -end - -- Create a standardized slider. -- name - Name to use for the slider. -- pos - Position to display the slider at. @@ -333,7 +332,7 @@ end -- Return: -- sliderGui - Slider gui object. -- sliderPosition - Object that can set the slider value. -function CreateStandardSlider( +local function CreateStandardSlider( name, pos, lengthBarPos, @@ -345,21 +344,24 @@ function CreateStandardSlider( local sliderGui, sliderPosition = RbxGui.CreateSlider(steps, 0, UDim2.new(0, 0, 0, 0)) - sliderGui.Name = name - sliderGui.Parent = parent - sliderGui.Position = pos - sliderGui.Size = UDim2.new(1, 0, 0, 20) - local lengthBar = sliderGui:FindFirstChild "Bar" - lengthBar.Size = UDim2.new(1, -21, 0, 5) - lengthBar.Position = lengthBarPos + Hydrate(sliderGui) { + Name = name, + Parent = parent, + Position = pos, + Size = UDim2.new(1, 0, 0, 20), + } + Hydrate(sliderGui.Bar) { + Size = UDim2.new(1, -21, 0, 5), + Position = lengthBarPos, + } - if nil ~= funcOnChange then + if funcOnChange then sliderPosition.Changed:connect(function() funcOnChange(sliderPosition) end) end - if nil ~= initValue then + if initValue then sliderPosition.Value = initValue end @@ -390,44 +392,6 @@ end -- return dropdown, updateSelection -- end --- Keep common button properties here to make it easer to change them all at once. --- These are the default properties to use for a button. -local buttonTextColor = Color3.new(1, 1, 1) -local buttonFont = Enum.Font.ArialBold -local buttonFontSize = Enum.FontSize.Size18 - --- Create a standard dropdown. Use this for all dropdowns in the popup so it is easy to standardize. --- name - What to use. --- pos - Where to position the button. Should be of type UDim2. --- text - Text to show in the button. --- funcOnPress - Function to run when the button is pressed. --- parent - What to set the parent as. --- Return: --- button - The button gui. -function CreateStandardButton(name, pos, text, funcOnPress, parent, size) - local button = Instance.new "TextButton" - button.Name = name - button.Position = pos - - button.Size = UDim2.new(0, 120, 0, 40) - button.Text = text - - if size then - button.Size = size - end - - button.Style = Enum.ButtonStyle.RobloxButton - - button.TextColor3 = buttonTextColor - button.Font = buttonFont - button.FontSize = buttonFontSize - - button.MouseButton1Click:connect(funcOnPress) - button.Parent = parent - - return button -end - local cancelValues = { cancelAction = false, -- Used to cancel currently occuring actions. If set to true then terrain generation will stop. progressBar = nil, -- Will store the progress bar when needed. @@ -437,30 +401,31 @@ local cancelValues = { -- Load the progress bar to display when drawing a river. -- text - Text to display. -function LoadProgressBar(text) - if cancelValues.progressBar == nil then - cancelValues.isDrawing = true - - -- Start the progress bar. - cancelValues.progressBar, cancelValues.setAmountFunc, cancelValues.bindForCancel = - RbxGui.CreateLoadingFrame(text) - - cancelValues.progressBar.Position = - UDim2.new(0.5, -cancelValues.progressBar.Size.X.Offset / 2, 0, 15) - cancelValues.progressBar.Parent = g - - cancelValues.bindForCancel.Event:connect(function(_) - cancelValues.cancelActions = true -- Set the flag that everything should stop. - - coroutine.yield() - end) - else +local function LoadProgressBar(text) + if cancelValues.progressBar then print "Tried to start the progress bar when it was already running." + return end + + cancelValues.isDrawing = true + + -- Start the progress bar. + cancelValues.progressBar, cancelValues.setAmountFunc, cancelValues.bindForCancel = + RbxGui.CreateLoadingFrame(text) + + cancelValues.progressBar.Position = + UDim2.new(0.5, -cancelValues.progressBar.Size.X.Offset / 2, 0, 15) + cancelValues.progressBar.Parent = g + + cancelValues.bindForCancel.Event:connect(function(_) + cancelValues.cancelActions = true -- Set the flag that everything should stop. + + coroutine.yield() + end) end -- Unload the progress bar. -function UnloadProgressBar() +local function UnloadProgressBar() cancelValues.isDrawing = false cancelValues.cancelActions = false @@ -520,8 +485,8 @@ offsetZSliderPosition.Value = (terrainOptions.zpos + 252) / 4 + 1 --FUNCTION DEFINITIONS- ----------------------- ---makes a column of blocks from 1 up to height at location (x,z) in cluster c -function coordHeight(x, z, height) +-- makes a column of blocks from 1 up to height at location (x,z) in cluster c +local function coordHeight(x, z, height) SetCells( c, Region3int16.new( @@ -534,10 +499,10 @@ function coordHeight(x, z, height) ) end ---makes a heightmap for a layer of mountains (width x depth) ---with a width frequency wf and depthfrequency df (width should be divisible by wf, depth should be divisible by df) (for unsquished results, width/wf = depth/df) ---with a range of amplitudes between 0 and a -function mountLayer(width, depth, wf, df, a) +-- makes a heightmap for a layer of mountains (width x depth) +-- with a width frequency wf and depthfrequency df (width should be divisible by wf, depth should be divisible by df) (for unsquished results, width/wf = depth/df) +-- with a range of amplitudes between 0 and a +local function mountLayer(width, depth, wf, df, a) local heightmap = {} for i = 0, width - 1 do heightmap[i] = {} @@ -599,8 +564,8 @@ function mountLayer(width, depth, wf, df, a) return heightmap end ---makes a shell around block at coordinate x, z using heightmap -function makeShell(x, z, heightmap, shellheightmap) +-- makes a shell around block at coordinate x, z using heightmap +local function makeShell(x, z, heightmap, shellheightmap) local originalheight = heightmap[x][z] for i = x - 1, x + 1 do for k = z - 1, z + 1 do @@ -628,13 +593,13 @@ end -- Set the camera to look at the terrain from a distance so that all terrain will be in view. -- centerX, centerZ - Center coordinate of land. This doesn't take into account clipping. -- length, width - Land dimensions. -function SetCamera(centerX, centerZ, length, width, height) +local function SetCamera(centerX, centerZ, length, width, height) local currCamera = game.Workspace.CurrentCamera local cameraPos = Vector3.new(0, 400, 1600) local cameraFocus = Vector3.new(0, height * 4, 0) -- Nothing set so use the default. - if nil ~= centerX then + if centerX then local scale = 0 local lengthScale = 0 local widthScale = 0 @@ -657,11 +622,7 @@ function SetCamera(centerX, centerZ, length, width, height) widthScale = 0.7 end - if widthScale > lengthScale then - scale = widthScale - else - scale = lengthScale - end + scale = widthScale > lengthScale and widthScale or lengthScale local distance = Vector3.new(0, (200 * scale) + 200, (1100 * scale)) cameraPos = @@ -675,12 +636,11 @@ function SetCamera(centerX, centerZ, length, width, height) end -- Create terrain based on the current properties. -function GenerateTerrain() +local function GenerateTerrain() toolbarbutton:SetActive(false) generateOptions = terrainOptions:Clone() - -- Turn off the plugin Off() -- Create the progress bar that will track terrain creation completion. @@ -977,37 +937,45 @@ end local ConfirmationPopup +-- Unload the conformation popup if it exists. +-- Does nothing if the popup isn't set. +local function ClearConformation() + if ConfirmationPopupObject then + ConfirmationPopupObject:Clear() + ConfirmationPopupObject = nil + end +end + -- Function used by the generate button. Prompts the user first. -- Will not show if disabled or terrain is being processed. -function ConfirmGenerateTerrain() +local function ConfirmGenerateTerrain() -- Only do if something isn't already being processed. - if nil == cancelValues.progressBar then - if nil == ConfirmationPopupObject then - if not hideGenerateConformation then - ConfirmationPopupObject = ConfirmationPopup.Create( - "Generate Terrain?", - "Generate", - "Cancel", - function() - ClearConformation() - GenerateTerrain() - end, - ClearConformation, - function() - hideGenerateConformation = not hideGenerateConformation - return not hideGenerateConformation - end - ) - else - GenerateTerrain() - end - end + if cancelValues.progressBar or ConfirmationPopupObject then + return + elseif hideGenerateConformation then + GenerateTerrain() + return end + + ConfirmationPopupObject = ConfirmationPopup.Create( + "Generate Terrain?", + "Generate", + "Cancel", + function() + ClearConformation() + GenerateTerrain() + end, + ClearConformation, + function() + hideGenerateConformation = not hideGenerateConformation + return not hideGenerateConformation + end + ) end -- Clears all terrain. -- Clearing is immediate. -function ClearTerrain() +local function ClearTerrain() toolbarbutton:SetActive(false) Off() @@ -1024,30 +992,29 @@ end -- Function used by the clear button. Prompts the user first. -- Will not show if disabled or terrain is being processed. -function ConfirmClearTerrain() +local function ConfirmClearTerrain() -- Only do if something isn't already being processed. - if nil == cancelValues.progressBar then - if nil == ConfirmationPopupObject then - if not hideClearConformation then - ConfirmationPopupObject = ConfirmationPopup.Create( - "Clear Terrain?", - "Clear", - "Cancel", - function() - ClearConformation() - ClearTerrain() - end, - ClearConformation, - function() - hideClearConformation = not hideClearConformation - return not hideClearConformation - end - ) - else - ClearTerrain() - end - end + if cancelValues.progressBar or ConfirmationPopupObject then + return + elseif hideClearConformation then + ClearTerrain() + return end + + ConfirmationPopupObject = ConfirmationPopup.Create( + "Clear Terrain?", + "Clear", + "Cancel", + function() + ClearConformation() + ClearTerrain() + end, + ClearConformation, + function() + hideClearConformation = not hideClearConformation + return not hideClearConformation + end + ) end -- Used to create a highlighter. @@ -1072,34 +1039,39 @@ function ConfirmationPopup.Create( confirmFunction, declineFunction ) - local popup = {} - popup.confirmButton = nil -- Hold the button to confirm a choice. - popup.declineButton = nil -- Hold the button to decline a choice. - popup.confirmationFrame = nil -- Hold the conformation frame. - popup.confirmationText = nil -- Hold the text label to display the conformation message. - popup.confirmationHelpText = nil -- Hold the text label to display the conformation message help. + local popup = { + confirmButton = nil, -- Hold the button to confirm a choice. + declineButton = nil, -- Hold the button to decline a choice. + confirmationFrame = nil, -- Hold the conformation frame. + confirmationText = nil, -- Hold the text label to display the conformation message. + confirmationHelpText = nil, -- Hold the text label to display the conformation message help. + } - popup.confirmationFrame = Instance.new "Frame" - popup.confirmationFrame.Name = "ConfirmationFrame" - popup.confirmationFrame.Size = UDim2.new(0, 280, 0, 140) - popup.confirmationFrame.Position = UDim2.new( - 0.5, - -popup.confirmationFrame.Size.X.Offset / 2, - 0.5, - -popup.confirmationFrame.Size.Y.Offset / 2 - ) - popup.confirmationFrame.Style = Enum.FrameStyle.RobloxRound - popup.confirmationFrame.Parent = g + popup.confirmationFrame = New "Frame" { + Name = "ConfirmationFrame", + Size = UDim2.new(0, 280, 0, 140), + Position = UDim2.new( + 0.5, + -popup.confirmationFrame.Size.X.Offset / 2, + 0.5, + -popup.confirmationFrame.Size.Y.Offset / 2 + ), + Style = Enum.FrameStyle.RobloxRound, + Parent = g, + } - popup.confirmLabel = CreateStandardLabel( - "ConfirmLabel", - UDim2.new(0, 0, 0, 15), - UDim2.new(1, 0, 0, 24), - confirmText, - popup.confirmationFrame - ) - popup.confirmLabel.FontSize = Enum.FontSize.Size18 - popup.confirmLabel.TextXAlignment = Enum.TextXAlignment.Center + popup.confirmLabel = Hydrate( + CreateStandardLabel( + "ConfirmLabel", + UDim2.new(0, 0, 0, 15), + UDim2.new(1, 0, 0, 24), + confirmText, + popup.confirmationFrame + ) + ) { + FontSize = Enum.FontSize.Size18, + TextXAlignment = Enum.TextXAlignment.Center, + } -- Confirm popup.confirmButton = CreateStandardButton( @@ -1126,19 +1098,19 @@ end -- Clear the popup, free up assets. function ConfirmationPopup:Clear() - if nil ~= self.confirmButton then + if self.confirmButton then self.confirmButton.Parent = nil end - if nil ~= self.declineButton then + if self.declineButton then self.declineButton.Parent = nil end - if nil ~= self.confirmationFrame then + if self.confirmationFrame then self.confirmationFrame.Parent = nil end - if nil ~= self.confirmLabel then + if self.confirmLabel then self.confirmLabel.Parent = nil end @@ -1163,7 +1135,8 @@ local widthLabel = CreateStandardLabel( "", terrainFrame ) -local _, _ --[[widthSliderGui, widthSliderPosition]] = CreateStandardSlider( +-- local _, _ widthSliderGui, widthSliderPosition = +CreateStandardSlider( "WidthSliderGui", UDim2.new(0, 1, 0, 108), UDim2.new(0, 10, 0.5, -2), @@ -1184,7 +1157,8 @@ local lengthLabel = CreateStandardLabel( "", terrainFrame ) -local _, _ --[[lengthSliderGui, lengthSliderPosition]] = CreateStandardSlider( +-- local _, _ lengthSliderGui, lengthSliderPosition = +CreateStandardSlider( "LengthSliderGui", UDim2.new(0, 1, 0, 149), UDim2.new(0, 10, 0.5, -2), @@ -1305,17 +1279,8 @@ CreateStandardButton( UDim2.new(0, 150, 0, 40) ) --- Unload the conformation popup if it exists. --- Does nothing if the popup isn't set. -function ClearConformation() - if nil ~= ConfirmationPopupObject then - ConfirmationPopupObject:Clear() - ConfirmationPopupObject = nil - end -end - -- Run when the popup is activated. -On = function() +function On() if not c then return end @@ -1326,7 +1291,7 @@ On = function() end -- Run when the popup is deactivated. -Off = function() +function Off() toolbarbutton:SetActive(false) ClearConformation() on = false diff --git a/terrain plugins/01 - builder.luau b/terrain plugins/01 - builder.luau index 9e46c1c..cc1477b 100644 --- a/terrain plugins/01 - builder.luau +++ b/terrain plugins/01 - builder.luau @@ -1,10 +1,13 @@ -while game == nil do +while not game do wait() end local ChangeHistoryService = game:GetService "ChangeHistoryService" local CoreGui = game:GetService "CoreGui" +local CreateStandardLabel = require "../Modules/Terrain/CreateStandardLabel" +local New = require("../Modules/New").New + --------------- --PLUGIN SETUP- --------------- @@ -96,19 +99,21 @@ function MouseHighlighter.Create(mouseUse) end) -- Create the part that the highlighter will be attached to. - highlighter.selectionPart = Instance.new "Part" - highlighter.selectionPart.Name = "SelectionPart" - highlighter.selectionPart.Archivable = false - highlighter.selectionPart.Transparency = 1 - highlighter.selectionPart.Anchored = true - highlighter.selectionPart.Locked = true - highlighter.selectionPart.CanCollide = false - highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom + highlighter.selectionPart = New "Part" { + Name = "SelectionPart", + Archivable = false, + Transparency = 1, + Anchored = true, + Locked = true, + CanCollide = false, + FormFactor = Enum.FormFactor.Custom, + } + highlighter.selectionBox = New "SelectionBox" { + Archivable = false, + Color = mouseHighlightColor, + Adornee = highlighter.selectionPart, + } - highlighter.selectionBox = Instance.new "SelectionBox" - highlighter.selectionBox.Archivable = false - highlighter.selectionBox.Color = mouseHighlightColor - highlighter.selectionBox.Adornee = highlighter.selectionPart mouseH.TargetFilter = highlighter.selectionPart setmetatable(highlighter, MouseHighlighter) @@ -119,7 +124,7 @@ function MouseHighlighter.Create(mouseUse) -- Return: -- success - Value is true if there was a plane intersection, false if not. -- cellPos - Value is the terrain cell intersection point if there is one, vectorPos if there isn't. - PlaneIntersection = function(vectorPos) + function PlaneIntersection(vectorPos) local currCamera = game.Workspace.CurrentCamera local startPos = Vector3.new( currCamera.CoordinateFrame.p.X, @@ -147,7 +152,7 @@ function MouseHighlighter.Create(mouseUse) -- Update where the highlighter is displayed. -- position - Where to display the highlighter, in world space. - UpdatePosition = function(position) + function UpdatePosition(position) if not position then return end @@ -214,30 +219,6 @@ end local mouseHighlighter = MouseHighlighter.Create(mouse) mouseHighlighter:DisablePreview() --- Create a standard text label. Use this for all lables in the popup so it is easy to standardize. --- labelName - What to set the text label name as. --- pos - Where to position the label. Should be of type UDim2. --- size - How large to make the label. Should be of type UDim2. --- text - Text to display. --- parent - What to set the text parent as. --- Return: --- Value is the created label. -function CreateStandardLabel(labelName, pos, size, text, parent) - local label = Instance.new "TextLabel" - label.Name = labelName - label.Position = pos - label.Size = size - label.Text = text - label.TextColor3 = Color3.new(0.95, 0.95, 0.95) - label.Font = Enum.Font.ArialBold - label.FontSize = Enum.FontSize.Size14 - label.TextXAlignment = Enum.TextXAlignment.Left - label.BackgroundTransparency = 1 - label.Parent = parent - - return label -end - ------ --GUI- ------ @@ -372,7 +353,7 @@ mouse.Button1Down:connect(onClicked) mouseHighlighter.OnClicked = onClicked -- Run when the popup is activated. -On = function() +function On() if not c then return end @@ -383,7 +364,7 @@ On = function() end -- Run when the popup is deactivated. -Off = function() +function Off() toolbarbutton:SetActive(false) on = false diff --git a/terrain plugins/02 - remover.luau b/terrain plugins/02 - remover.luau index 90e3e31..ee4c356 100644 --- a/terrain plugins/02 - remover.luau +++ b/terrain plugins/02 - remover.luau @@ -1,10 +1,12 @@ -while game == nil do +while not game do wait() end local ChangeHistoryService = game:GetService "ChangeHistoryService" local CoreGui = game:GetService "CoreGui" +local CreateStandardLabel = require "../Modules/Terrain/CreateStandardLabel" + ----------------- --DEFAULT VALUES- ----------------- @@ -170,30 +172,6 @@ end -- Create the mouse movement highlighter. local mouseHighlighter = MouseHighlighter.Create(mouse) --- Create a standard text label. Use this for all lables in the popup so it is easy to standardize. --- labelName - What to set the text label name as. --- pos - Where to position the label. Should be of type UDim2. --- size - How large to make the label. Should be of type UDim2. --- text - Text to display. --- parent - What to set the text parent as. --- Return: --- Value is the created label. -function CreateStandardLabel(labelName, pos, size, text, parent) - local label = Instance.new "TextLabel" - label.Name = labelName - label.Position = pos - label.Size = size - label.Text = text - label.TextColor3 = Color3.new(0.95, 0.95, 0.95) - label.Font = Enum.Font.ArialBold - label.FontSize = Enum.FontSize.Size14 - label.TextXAlignment = Enum.TextXAlignment.Left - label.BackgroundTransparency = 1 - label.Parent = parent - - return label -end - ------ --GUI- ------ @@ -283,11 +261,10 @@ mouse.Button1Down:connect(onClicked) mouseHighlighter.OnClicked = onClicked -- Run when the popup is activated. -On = function() +function On() if not c then return - end - if plugin then + elseif plugin then plugin:Activate(true) end if toolbarbutton then @@ -300,7 +277,7 @@ On = function() end -- Run when the popup is deactivated. -Off = function() +function Off() on = false if toolbarbutton then diff --git a/terrain plugins/03 - elevation.luau b/terrain plugins/03 - elevation.luau index 4a593d5..5a9f1a4 100644 --- a/terrain plugins/03 - elevation.luau +++ b/terrain plugins/03 - elevation.luau @@ -1,10 +1,12 @@ -while game == nil do +while not game do wait() end local ChangeHistoryService = game:GetService "ChangeHistoryService" local CoreGui = game:GetService "CoreGui" +local CreateStandardLabel = require "../Modules/Terrain/CreateStandardLabel" + --------------- --PLUGIN SETUP- --------------- @@ -116,45 +118,9 @@ function MouseHighlighter.Create(mouseUse) -- Will hold a part the highlighter will be attached to. This will be moved where the mouse is. highlighter.selectionPart = nil - -- Function to call when the mouse has moved. Updates where to display the highlighter. - local function MouseMoved() - if on then - UpdatePosition(mouseH.Hit) - end - end - - -- Hook the mouse up to check for movement. - mouseH.Move:connect(function() - MouseMoved() - end) - - mouseH.Button1Down:connect(function() - highlighter.mouseDown = true - end) - mouseH.Button1Up:connect(function() - highlighter.mouseDown = false - end) - - -- Create the part that the highlighter will be attached to. - highlighter.selectionPart = Instance.new "Part" - highlighter.selectionPart.Name = "SelectionPart" - highlighter.selectionPart.Archivable = false - highlighter.selectionPart.Transparency = 1 - highlighter.selectionPart.Anchored = true - highlighter.selectionPart.Locked = true - highlighter.selectionPart.CanCollide = false - highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom - - highlighter.selectionBox = Instance.new "SelectionBox" - highlighter.selectionBox.Archivable = false - highlighter.selectionBox.Color = mouseHighlightColor - highlighter.selectionBox.Adornee = highlighter.selectionPart - mouseH.TargetFilter = highlighter.selectionPart - setmetatable(highlighter, MouseHighlighter) - -- Update where the highlighter is displayed. -- position - Where to display the highlighter, in world space. - function UpdatePosition(position) + local function UpdatePosition(position) if not position then return end @@ -204,6 +170,42 @@ function MouseHighlighter.Create(mouseUse) end end + -- Function to call when the mouse has moved. Updates where to display the highlighter. + local function MouseMoved() + if on then + UpdatePosition(mouseH.Hit) + end + end + + -- Hook the mouse up to check for movement. + mouseH.Move:connect(function() + MouseMoved() + end) + + mouseH.Button1Down:connect(function() + highlighter.mouseDown = true + end) + mouseH.Button1Up:connect(function() + highlighter.mouseDown = false + end) + + -- Create the part that the highlighter will be attached to. + highlighter.selectionPart = Instance.new "Part" + highlighter.selectionPart.Name = "SelectionPart" + highlighter.selectionPart.Archivable = false + highlighter.selectionPart.Transparency = 1 + highlighter.selectionPart.Anchored = true + highlighter.selectionPart.Locked = true + highlighter.selectionPart.CanCollide = false + highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom + + highlighter.selectionBox = Instance.new "SelectionBox" + highlighter.selectionBox.Archivable = false + highlighter.selectionBox.Color = mouseHighlightColor + highlighter.selectionBox.Adornee = highlighter.selectionPart + mouseH.TargetFilter = highlighter.selectionPart + setmetatable(highlighter, MouseHighlighter) + return highlighter end @@ -231,30 +233,6 @@ g.Parent = CoreGui -- UI gui load. Required for sliders. local RbxGui = LoadLibrary "RbxGui" --- Create a standard text label. Use this for all lables in the popup so it is easy to standardize. --- labelName - What to set the text label name as. --- pos - Where to position the label. Should be of type UDim2. --- size - How large to make the label. Should be of type UDim2. --- text - Text to display. --- parent - What to set the text parent as. --- Return: --- Value is the created label. -function CreateStandardLabel(labelName, pos, size, text, parent) - local label = Instance.new "TextLabel" - label.Name = labelName - label.Position = pos - label.Size = size - label.Text = text - label.TextColor3 = Color3.new(0.95, 0.95, 0.95) - label.Font = Enum.Font.ArialBold - label.FontSize = Enum.FontSize.Size14 - label.TextXAlignment = Enum.TextXAlignment.Left - label.BackgroundTransparency = 1 - label.Parent = parent - - return label -end - -- Create a standardized slider. -- name - Name to use for the slider. -- pos - Position to display the slider at. @@ -639,7 +617,7 @@ mouse.Button1Up:connect(function() end) -- Run when the popup is activated. -On = function() +function On() if not c then return end @@ -651,7 +629,7 @@ On = function() end -- Run when the popup is deactivated. -Off = function() +function Off() toolbarbutton:SetActive(false) on = false diff --git a/terrain plugins/04 - brush.luau b/terrain plugins/04 - brush.luau index 3f3036c..255105e 100644 --- a/terrain plugins/04 - brush.luau +++ b/terrain plugins/04 - brush.luau @@ -1,10 +1,12 @@ -while game == nil do +while not game do wait() end local ChangeHistoryService = game:GetService "ChangeHistoryService" local CoreGui = game:GetService "CoreGui" +-- local CreateStandardLabel = require "../Modules/Terrain/CreateStandardLabel" + --------------- --PLUGIN SETUP- --------------- @@ -133,9 +135,7 @@ end function brush(x, y, z) if depth == 0 then return - end - - if depth > 0 then + elseif depth > 0 then local findY = findLowPoint(x, y + depth, z) local yWithDepth = y + depth @@ -171,9 +171,7 @@ end function updatePreviewSelection(position) if not position then return - end - --if not mouse.Target then disablePreview() return end - if depth == 0 then + elseif depth == 0 then -- or not mouse.Target disablePreview() return end @@ -307,21 +305,21 @@ function doFillCells(position, mouseDrag, needsCellPos) end function mouseMoved() - if on then - if mousedown == true then - if mousemoving then - return - end - - mousemoving = true - local currMousePos = Vector2.new(mouse.X, mouse.Y) - local mouseDrag = currMousePos - lastMousePos - doFillCells(mouse.Hit, mouseDrag, true) - lastMousePos = currMousePos - mousemoving = false + if not on then + return + elseif mousedown == true then + if mousemoving then + return end - updatePreviewSelection(mouse.Hit) + + mousemoving = true + local currMousePos = Vector2.new(mouse.X, mouse.Y) + local mouseDrag = currMousePos - lastMousePos + doFillCells(mouse.Hit, mouseDrag, true) + lastMousePos = currMousePos + mousemoving = false end + updatePreviewSelection(mouse.Hit) end -- Do a line/plane intersection. The line starts at the camera. The plane is at y == 0, normal(0, 1, 0) @@ -403,11 +401,10 @@ end local brushDragBar -On = function() +function On() if not c then return - end - if this then + elseif this then this:Activate(true) end if toolbarbutton then @@ -422,7 +419,7 @@ On = function() on = true end -Off = function() +function Off() if toolbarbutton then toolbarbutton:SetActive(false) end @@ -474,30 +471,6 @@ function CreateStandardDropdown( return dropdown, updateSelection end --- Create a standard text label. Use this for all lables in the popup so it is easy to standardize. --- labelName - What to set the text label name as. --- pos - Where to position the label. Should be of type UDim2. --- size - How large to make the label. Should be of type UDim2. --- text - Text to display. --- parent - What to set the text parent as. --- Return: --- Value is the created label. --- local function CreateStandardLabel(labelName, pos, size, text, parent) --- local label = Instance.new "TextLabel" --- label.Name = labelName --- label.Position = pos --- label.Size = size --- label.Text = text --- label.TextColor3 = Color3.new(0.95, 0.95, 0.95) --- label.Font = Enum.Font.ArialBold --- label.FontSize = Enum.FontSize.Size14 --- label.TextXAlignment = Enum.TextXAlignment.Left --- label.BackgroundTransparency = 1 --- label.Parent = parent - --- return label --- end - -- Create a standardized slider. -- name - Name to use for the slider. -- pos - Position to display the slider at. @@ -546,6 +519,7 @@ local g = Instance.new "ScreenGui" g.Name = "TerrainBrushGui" g.Parent = CoreGui +local elevationFrame, elevationHelpFrame, elevationCloseEvent brushDragBar, elevationFrame, elevationHelpFrame, elevationCloseEvent = RbxGui.CreatePluginFrame( "Terrain Brush", diff --git a/terrain plugins/06 - craters.luau b/terrain plugins/06 - craters.luau index fbdb19a..20e1a74 100644 --- a/terrain plugins/06 - craters.luau +++ b/terrain plugins/06 - craters.luau @@ -1,4 +1,5 @@ -while game == nil do +--!strict +while not game do wait() end @@ -47,9 +48,13 @@ local craterDragBar, craterFrame, craterHelpFrame, craterCloseEvent = nil --FUNCTION DEFINITIONS- ----------------------- ---makes a crater at point (x, y, z) in cluster c ---cd is the depth factor, a percent of the depth of a perfect sphere -function makeCrater(x, y, z, cr, cd) +local function dist(x1, y1, x2, y2) + return math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2)) +end + +-- makes a crater at point (x, y, z) in cluster c +-- cd is the depth factor, a percent of the depth of a perfect sphere +local function makeCrater(x, y, z, cr, cd) local heightmap = {} for i = x - (cr + 1), x + (cr + 1) do heightmap[i] = {} @@ -112,10 +117,6 @@ function makeCrater(x, y, z, cr, cd) end end -function dist(x1, y1, x2, y2) - return math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2)) -end - local debounce = false mouse.Button1Down:connect(function() @@ -139,11 +140,10 @@ mouse.Button1Down:connect(function() ChangeHistoryService:SetWaypoint "Crater" end) -On = function() +function On() if not c then return - end - if this then + elseif this then this:Activate(true) end if toolbarbutton then @@ -155,7 +155,7 @@ On = function() on = true end -Off = function() +function Off() if toolbarbutton then toolbarbutton:SetActive(false) end diff --git a/terrain plugins/08 - roads.luau b/terrain plugins/08 - roads.luau index 44798a0..107878f 100644 --- a/terrain plugins/08 - roads.luau +++ b/terrain plugins/08 - roads.luau @@ -1,4 +1,5 @@ -while game == nil do +--!strict +while not game do wait() end @@ -272,11 +273,10 @@ mouse.Button1Down:connect(function() end end) -On = function() +function On() if not c then return - end - if this then + elseif this then this:Activate(true) end if toolbarbutton then @@ -290,7 +290,7 @@ On = function() on = true end -Off = function() +function Off() if toolbarbutton then toolbarbutton:SetActive(false) end @@ -311,6 +311,7 @@ local g = Instance.new "ScreenGui" g.Name = "RoadGui" g.Parent = game:GetService "CoreGui" +local roadHelpFrame roadDragBar, roadFrame, roadHelpFrame, roadCloseEvent = RbxGui.CreatePluginFrame( "Roads", diff --git a/terrain plugins/09 - materialpaint.luau b/terrain plugins/09 - materialpaint.luau index 0eac764..a8737a6 100644 --- a/terrain plugins/09 - materialpaint.luau +++ b/terrain plugins/09 - materialpaint.luau @@ -1,4 +1,4 @@ -while game == nil do +while not game do wait() end @@ -12,6 +12,7 @@ local loaded = false local on = false local On, Off +local mouseDown, mouseUp, mouseMove local this = PluginManager():CreatePlugin() local mouse = this:GetMouse() @@ -82,29 +83,173 @@ local RbxUtil = LoadLibrary "RbxUtility" --FUNCTION DEFINITIONS- ----------------------- function paintWaterfall(setCells) - if setCells then - for i = 1, #setCells do - SetWaterCell( - c, - setCells[i].xPos, - setCells[i].yPos, - setCells[i].zPos, - mediumWaterForce, - Enum.WaterDirection.NegY - ) - end - end -end - -function setWaterDirection(mouseCellPos, setCells) if not setCells then return end - if #setCells <= 0 then - return + for i = 1, #setCells do + SetWaterCell( + c, + setCells[i].xPos, + setCells[i].yPos, + setCells[i].zPos, + mediumWaterForce, + Enum.WaterDirection.NegY + ) + end +end + +-- Factored out this stuff because I didn't like the mutability of the setCells arrays. - Heliodex + +local function getSquareCell(x: number, y: number, z: number) + -- local tempCellPos = Vector3.new(x, y, z) + local oldMaterial, oldType, oldOrientation = GetCell(c, x, y, z) + if oldMaterial.Value <= 0 then + return nil + end + return { + xPos = x, + yPos = y, + zPos = z, + theType = oldType, + orientation = oldOrientation, + } +end + +local function getSquare(cellPos) + local setCells = {} + local finalX = cellPos.x + radius - 1 + local finalZ = cellPos.z + radius - 1 + local finalY = cellPos.y + radius - 1 + + for x = cellPos.x - radius + 1, finalX do + for z = cellPos.z - radius + 1, finalZ do + for y = cellPos.y - radius + 1, finalY do + table.insert(setCells, getSquareCell(x, y, z)) + end + end end - if directionIsDown(lastCell, mouseCellPos) then + return setCells +end + +local function getCircularCell( + x: number, + y: number, + z: number, + cellPos, + radiusSquared +) + local tempCellPos = Vector3.new(x, y, z) + local holdDist = tempCellPos - cellPos + local distSq = (holdDist):Dot(holdDist) + if distSq >= radiusSquared then + return nil + end + local oldMaterial, oldType, oldOrientation = GetCell(c, x, y, z) + if oldMaterial.Value <= 0 then + return nil + end + return { + xPos = x, + yPos = y, + zPos = z, + theType = oldType, + orientation = oldOrientation, + } +end + +local function getCircular(cellPos) + local setCells = {} + local finalX = cellPos.x + radius + local finalZ = cellPos.z + radius + local finalY = cellPos.y + radius + + for x = cellPos.x - radius, finalX do + for z = cellPos.z - radius, finalZ do + for y = cellPos.y - radius, finalY do + table.insert( + setCells, + getCircularCell(x, y, z, cellPos, radius * radius) + ) + end + end + end + + return setCells +end + +local function getAffectedCells(startPos) + if startPos and c then + if brushType == "Circular" then + return getCircular(startPos) + elseif brushType == "Square" then + return getSquare(startPos) + end + end + + return {} +end + +local function directionIsDown(fromCell, toCell) + if not toCell then + return false + end + + if toCell and fromCell then + local direction = (toCell - fromCell).unit + local absX, absY, absZ = + math.abs(direction.X), math.abs(direction.Y), math.abs(direction.Z) + if absY > absX and absY > absZ then + return true + end + end + + local viableCells = getAffectedCells(toCell) + if not viableCells or #viableCells < 2 then + return false + end + + local lowX, lowY, lowZ = + viableCells[1].xPos, viableCells[1].yPos, viableCells[1].zPos + local highX, highY, highZ = lowX, lowY, lowZ + + for i = 2, #viableCells do + if viableCells[i].xPos < lowX then + lowX = viableCells[i].xPos + end + if viableCells[i].xPos > highX then + highX = viableCells[i].xPos + end + + if viableCells[i].yPos < lowY then + lowY = viableCells[i].yPos + end + if viableCells[i].yPos > highY then + highY = viableCells[i].yPos + end + + if viableCells[i].zPos < lowZ then + lowZ = viableCells[i].zPos + end + if viableCells[i].zPos > highZ then + highZ = viableCells[i].zPos + end + end + + local xRange, yRange, zRange = + math.abs(highX - lowX), math.abs(highY - lowY), math.abs(highZ - lowZ) + + local xzPlaneArea = xRange * zRange + local xyPlaneArea = xRange * yRange + local yzPlaneArea = yRange * zRange + + return xyPlaneArea > xzPlaneArea or yzPlaneArea > xzPlaneArea +end + +local function setWaterDirection(mouseCellPos, setCells) + if not setCells or #setCells <= 0 then + return + elseif directionIsDown(lastCell, mouseCellPos) then paintWaterfall(setCells) return end @@ -209,197 +354,122 @@ function setWaterDirection(mouseCellPos, setCells) ) end end - else - for i = 1, #setCells do - if setCells[i].xPos == initX then + return + end + + for i = 1, #setCells do + if setCells[i].xPos == initX then + SetWaterCell( + c, + setCells[i].xPos, + setCells[i].yPos, + setCells[i].zPos, + mediumWaterForce, + down + ) + elseif setCells[i].xPos == endX then + SetWaterCell( + c, + setCells[i].xPos, + setCells[i].yPos, + setCells[i].zPos, + mediumWaterForce, + up + ) + else + if setCells[i].zPos < zMiddle then SetWaterCell( c, setCells[i].xPos, setCells[i].yPos, setCells[i].zPos, mediumWaterForce, - down + right ) - elseif setCells[i].xPos == endX then + elseif setCells[i].zPos > zMiddle then SetWaterCell( c, setCells[i].xPos, setCells[i].yPos, setCells[i].zPos, mediumWaterForce, - up + left ) else - if setCells[i].zPos < zMiddle then + if setCells[i].xPos < xMiddle then SetWaterCell( c, setCells[i].xPos, setCells[i].yPos, setCells[i].zPos, mediumWaterForce, - right + down ) - elseif setCells[i].zPos > zMiddle then + elseif setCells[i].xPos > xMiddle then SetWaterCell( c, setCells[i].xPos, setCells[i].yPos, setCells[i].zPos, mediumWaterForce, - left + up ) - else - if setCells[i].xPos < xMiddle then - SetWaterCell( - c, - setCells[i].xPos, - setCells[i].yPos, - setCells[i].zPos, - mediumWaterForce, - down - ) - elseif setCells[i].xPos > xMiddle then - SetWaterCell( - c, - setCells[i].xPos, - setCells[i].yPos, - setCells[i].zPos, - mediumWaterForce, - up - ) - end end end end end -end - -function getSquare(cellPos, setCells) - local finalX = cellPos.x + radius - 1 - local finalZ = cellPos.z + radius - 1 - local finalY = cellPos.y + radius - 1 - - for x = cellPos.x - radius + 1, finalX do - for z = cellPos.z - radius + 1, finalZ do - for y = cellPos.y - radius + 1, finalY do - -- local tempCellPos = Vector3.new(x, y, z) - local oldMaterial, oldType, oldOrientation = GetCell(c, x, y, z) - if oldMaterial.Value > 0 then - table.insert(setCells, { - xPos = x, - yPos = y, - zPos = z, - theType = oldType, - orientation = oldOrientation, - }) - end - end - end - end -end - -function getCircular(cellPos, setCells) - local radiusSquared = radius * radius - - local finalX = cellPos.x + radius - local finalZ = cellPos.z + radius - local finalY = cellPos.y + radius - - for x = cellPos.x - radius, finalX do - for z = cellPos.z - radius, finalZ do - for y = cellPos.y - radius, finalY do - local tempCellPos = Vector3.new(x, y, z) - local holdDist = tempCellPos - cellPos - local distSq = (holdDist):Dot(holdDist) - if distSq < radiusSquared then - local oldMaterial, oldType, oldOrientation = - GetCell(c, x, y, z) - if oldMaterial.Value > 0 then - table.insert(setCells, { - xPos = x, - yPos = y, - zPos = z, - theType = oldType, - orientation = oldOrientation, - }) - end - end - end - end - end -end - -function paintCircular(cellPos, setCells) - getCircular(cellPos, setCells) - - if currentMaterial ~= waterMaterial then - for i = 1, #setCells do - SetCell( - c, - setCells[i].xPos, - setCells[i].yPos, - setCells[i].zPos, - currentMaterial, - setCells[i].theType, - setCells[i].orientation - ) - end - end -end - -function paintSquare(cellPos, setCells) - getSquare(cellPos, setCells) - - if currentMaterial ~= waterMaterial then - for i = 1, #setCells do - SetCell( - c, - setCells[i].xPos, - setCells[i].yPos, - setCells[i].zPos, - currentMaterial, - setCells[i].theType, - setCells[i].orientation - ) - end - end -end - -function paint(startPos) - if startPos and c then - local cellPos = startPos - local setCells = {} - - if brushType == "Circular" then - paintCircular(cellPos, setCells) - elseif brushType == "Square" then - paintSquare(cellPos, setCells) - end - - if currentMaterial == waterMaterial then - setWaterDirection(cellPos, setCells) - end - - return setCells - end - return -end - -function getAffectedCells(startPos) - local setCells = {} - - if startPos and c then - if brushType == "Circular" then - getCircular(startPos, setCells) - elseif brushType == "Square" then - getSquare(startPos, setCells) - end - end return setCells end -function calculateRegion(mouseR) +local function paintWith( + fn: ( + Vector3 + ) -> { xPos: number, yPos: number, zPos: number }, + cellPos: Vector3 +) + local setCells = fn(cellPos) + + if currentMaterial == waterMaterial then + return + end + for i = 1, #setCells do + SetCell( + c, + setCells[i].xPos, + setCells[i].yPos, + setCells[i].zPos, + currentMaterial, + setCells[i].theType, + setCells[i].orientation + ) + end + + return setCells +end + +local function paint(startPos) + if not (startPos and c) then + return + end + + local cellPos = startPos + local setCells + + if brushType == "Circular" then + setCells = paintWith(getCircular, cellPos) + elseif brushType == "Square" then + setCells = paintWith(getSquare, cellPos) + end + + if currentMaterial == waterMaterial then + return setWaterDirection(cellPos, setCells) + end + + return setCells +end + +local function calculateRegion(mouseR) local cellPos = WorldToCellPreferSolid(c, mouseR.Hit.p) local lowVec = @@ -415,7 +485,7 @@ function calculateRegion(mouseR) ) end -function createSelection(mouseS, massSelection) +local function createSelection(mouseS, massSelection) currSelectionUpdate, currSelectionDestroy = RbxUtil.SelectTerrainRegion( calculateRegion(mouseS), BrickColor.new "Lime green", @@ -424,24 +494,19 @@ function createSelection(mouseS, massSelection) ) end -function updateSelection(mouseS) +local function updateSelection(mouseS) if not currSelectionUpdate then createSelection(mouseS, radius > 4) - else - currSelectionUpdate( - calculateRegion(mouseS), - BrickColor.new "Lime green" - ) - end -end - -function setPositionDirectionality() - if nil == lastCell then return end + currSelectionUpdate(calculateRegion(mouseS), BrickColor.new "Lime green") +end - -- no dragging occured, lets set our water to be stagnant or be a waterfall - if lastCell and not lastLastCell then +local function setPositionDirectionality() + if nil == lastCell then + return + elseif lastCell and not lastLastCell then + -- no dragging occured, lets set our water to be stagnant or be a waterfall local cellsToSet = paint(lastCell) if directionIsDown(nil, lastCell) then paintWaterfall(cellsToSet) @@ -476,23 +541,14 @@ function setPositionDirectionality() local direction if absX > absY and absX > absZ then - if overallDirection.X > 0 then - direction = Enum.WaterDirection.X - else - direction = Enum.WaterDirection.NegX - end + direction = overallDirection.X > 0 and Enum.WaterDirection.X + or Enum.WaterDirection.NegX elseif absY > absX and absY > absZ then - if overallDirection.Y > 0 then - direction = Enum.WaterDirection.Y - else - direction = Enum.WaterDirection.NegY - end + direction = overallDirection.Y > 0 and Enum.WaterDirection.Y + or Enum.WaterDirection.NegY elseif absZ > absX and absZ > absY then - if overallDirection.Z > 0 then - direction = Enum.WaterDirection.Z - else - direction = Enum.WaterDirection.NegZ - end + direction = overallDirection.Z > 0 and Enum.WaterDirection.Z + or Enum.WaterDirection.NegZ end if not direction then -- this should never be hit, but just in case @@ -512,24 +568,27 @@ function setPositionDirectionality() end function mouseDown(mouseD) - if on and mouseD.Target == game.Workspace.Terrain then - dragging = true - if mouseD and mouseD.Hit then - if mouseD.Target == game.Workspace.Terrain then - local newCell = WorldToCellPreferSolid(c, mouseD.Hit.p) - if newCell then - local setCells = paint(newCell) - if - currentMaterial == waterMaterial - and directionIsDown(lastCell, newCell) - then - paintWaterfall(setCells) - end - lastCell = newCell - end - end - end + if not (on and mouseD.Target == game.Workspace.Terrain) then + return end + + dragging = true + if + not (mouseD and mouseD.Hit and mouseD.Target == game.Workspace.Terrain) + then + return + end + + local newCell = WorldToCellPreferSolid(c, mouseD.Hit.p) + if not newCell then + return + elseif + currentMaterial == waterMaterial + and directionIsDown(lastCell, newCell) + then + paintWaterfall(paint(newCell)) + end + lastCell = newCell end function mouseUp(_) @@ -547,96 +606,29 @@ function mouseUp(_) end function mouseMove(mouseM) - if on then - if mouseM.Target == game.Workspace.Terrain then - if lastCell ~= WorldToCellPreferSolid(c, mouseM.Hit.p) then - updateSelection(mouseM) - local newCell = WorldToCellPreferSolid(c, mouseM.Hit.p) - - if dragging then - -- local painting = true - paint(newCell) - if lastCell and newCell then - if (lastCell - newCell).magnitude > 1 then - paintBetweenPoints(lastCell, newCell) - end - end - lastLastCell = lastCell - lastCell = newCell - -- painting = false - end - end - else - destroySelection() + if not on then + return + elseif mouseM.Target == game.Workspace.Terrain then + if lastCell == WorldToCellPreferSolid(c, mouseM.Hit.p) then + return end - end -end + updateSelection(mouseM) + local newCell = WorldToCellPreferSolid(c, mouseM.Hit.p) -function directionIsDown(fromCell, toCell) - if not toCell then - return false - end - - if toCell and fromCell then - local direction = (toCell - fromCell).unit - local absX, absY, absZ = - math.abs(direction.X), math.abs(direction.Y), math.abs(direction.Z) - if absY > absX and absY > absZ then - return true + if not dragging then + return end - end - - local viableCells = getAffectedCells(toCell) - if not viableCells then - return false - end - if #viableCells < 2 then - return false - end - - local lowX, lowY, lowZ = - viableCells[1].xPos, viableCells[1].yPos, viableCells[1].zPos - local highX, highY, highZ = lowX, lowY, lowZ - - for i = 2, #viableCells do - if viableCells[i].xPos < lowX then - lowX = viableCells[i].xPos - end - if viableCells[i].xPos > highX then - highX = viableCells[i].xPos - end - - if viableCells[i].yPos < lowY then - lowY = viableCells[i].yPos - end - if viableCells[i].yPos > highY then - highY = viableCells[i].yPos - end - - if viableCells[i].zPos < lowZ then - lowZ = viableCells[i].zPos - end - if viableCells[i].zPos > highZ then - highZ = viableCells[i].zPos + -- local painting = true + paint(newCell) + if lastCell and newCell and (lastCell - newCell).magnitude > 1 then + paintBetweenPoints(lastCell, newCell) end + lastLastCell = lastCell + lastCell = newCell + -- painting = false + else + destroySelection() end - - local xRange, yRange, zRange = - math.abs(highX - lowX), math.abs(highY - lowY), math.abs(highZ - lowZ) - - local xzPlaneArea = xRange * zRange - local xyPlaneArea = xRange * yRange - local yzPlaneArea = yRange * zRange - - if xyPlaneArea > xzPlaneArea then - return true - end - - if yzPlaneArea > xzPlaneArea then - return true - end - - return false end function destroySelection() @@ -649,31 +641,33 @@ function destroySelection() end end -function moveTowardsGoal(direction, currPos, goalPos, currCell) - if currPos ~= goalPos then - if currPos < goalPos then - if direction == "X" then - currCell = Vector3.new(currCell.X + 1, currCell.Y, currCell.Z) - elseif direction == "Y" then - currCell = Vector3.new(currCell.X, currCell.Y + 1, currCell.Z) - elseif direction == "Z" then - currCell = Vector3.new(currCell.X, currCell.Y, currCell.Z + 1) - end - elseif currPos > goalPos then - if direction == "X" then - currCell = Vector3.new(currCell.X - 1, currCell.Y, currCell.Z) - elseif direction == "Y" then - currCell = Vector3.new(currCell.X, currCell.Y - 1, currCell.Z) - elseif direction == "Z" then - currCell = Vector3.new(currCell.X, currCell.Y, currCell.Z - 1) - end +local function moveTowardsGoal(direction: string, currPos, goalPos, currCell) + if currPos == goalPos then + return currCell + end + + if currPos < goalPos then + if direction == "X" then + currCell = Vector3.new(currCell.X + 1, currCell.Y, currCell.Z) + elseif direction == "Y" then + currCell = Vector3.new(currCell.X, currCell.Y + 1, currCell.Z) + elseif direction == "Z" then + currCell = Vector3.new(currCell.X, currCell.Y, currCell.Z + 1) + end + elseif currPos > goalPos then + if direction == "X" then + currCell = Vector3.new(currCell.X - 1, currCell.Y, currCell.Z) + elseif direction == "Y" then + currCell = Vector3.new(currCell.X, currCell.Y - 1, currCell.Z) + elseif direction == "Z" then + currCell = Vector3.new(currCell.X, currCell.Y, currCell.Z - 1) end end return currCell end -function interpolateOneDim(direction, currPos, goalPos, currCell) +local function interpolateOneDim(direction, currPos, goalPos, currCell) if currPos ~= goalPos then currCell = moveTowardsGoal(direction, currPos, goalPos, currCell) paint(currCell) @@ -696,7 +690,7 @@ function paintBetweenPoints(lastCellP, newCell) end end -On = function() +function On() if not c then return end @@ -707,7 +701,7 @@ On = function() on = true end -Off = function() +function Off() toolbarbutton:SetActive(false) destroySelection() @@ -724,6 +718,7 @@ local g = Instance.new "ScreenGui" g.Name = "MaterialPainterGui" g.Parent = CoreGui +local containerFrame dragBar, containerFrame, helpFrame, closeEvent = RbxGui.CreatePluginFrame( "Material Brush", UDim2.new(0, 163, 0, 285), diff --git a/terrain plugins/10 - stamper.luau b/terrain plugins/10 - stamper.luau index dbbd5d0..4287f02 100644 --- a/terrain plugins/10 - stamper.luau +++ b/terrain plugins/10 - stamper.luau @@ -1,9 +1,12 @@ -while game == nil do +while not game do wait() end local ContentProvider = game:GetService "ContentProvider" local CoreGui = game:GetService "CoreGui" +local News = require "../Modules/New" +local New = News.New +local Hydrate = News.Hydrate --------------- --PLUGIN SETUP- @@ -113,11 +116,10 @@ function hideLoadingDialog() currStampGui.LoadingFrame.Visible = false end -local partSelected = function(name, id, terrainShape) - if not id then - return - end - if not name then +local stampCon + +local function partSelected(name, id, terrainShape) + if not (id and name) then return end @@ -146,21 +148,23 @@ local partSelected = function(name, id, terrainShape) and clusterTag:isA "Vector3Value" and clusterTag.Value.X == 17 then - local waterForceTag = Instance.new("StringValue", lastStampModel) - waterForceTag.Name = "WaterForceTag" - waterForceTag.Value = waterForceAndDirection[1] - - local waterForceDirectionTag = - Instance.new("StringValue", lastStampModel) - waterForceDirectionTag.Name = "WaterForceDirectionTag" - waterForceDirectionTag.Value = waterForceAndDirection[2] + New "StringValue" { + Name = "WaterForceTag", + Value = waterForceAndDirection[1], + Parent = lastStampModel, + } + New "StringValue" { + Name = "WaterForceDirectionTag", + Value = waterForceAndDirection[2], + Parent = lastStampModel, + } end end - setupStamper(lastStampModel, mouse) + setupStamper(lastStampModel) end -function updateWaterInfo() +local function updateWaterInfo() if stampControl then stampControl.Destroy() end @@ -177,70 +181,203 @@ function updateWaterInfo() local clusterTag = lastStampModel:FindFirstChild "ClusterMaterial" -- we are going to stamp water, send info to stamper about this if clusterTag and clusterTag.Value.X == 17 then - local waterForceTag = Instance.new("StringValue", lastStampModel) - waterForceTag.Name = "WaterForceTag" - waterForceTag.Value = waterForceAndDirection[1] - - local waterForceDirectionTag = - Instance.new("StringValue", lastStampModel) - waterForceDirectionTag.Name = "WaterForceDirectionTag" - waterForceDirectionTag.Value = waterForceAndDirection[2] + New "StringValue" { + Name = "WaterForceTag", + Value = waterForceAndDirection[1], + Parent = lastStampModel, + } + New "StringValue" { + Name = "WaterForceDirectionTag", + Value = waterForceAndDirection[2], + Parent = lastStampModel, + } end end - setupStamper(lastStampModel, mouse) + setupStamper(lastStampModel) end -local dialogClosed = function() - if lastStampModel then - if stampControl then - stampControl.Destroy() - end - setupStamper(lastStampModel, mouse) +local function dialogClosed() + if not lastStampModel then + return + elseif stampControl then + stampControl.Destroy() end + setupStamper(lastStampModel) end -function pickPart() +local function pickPart() if stampControl then stampControl.Destroy() end setPanelVisibility(true) end -function keyHandler(key) - if key == "f" then - handlePartShow() - end -end - -function partOn() - pickPart() -end - -function partOff() - setPanelVisibility(false) - if lastStampModel then - if stampControl then - stampControl.Destroy() +local function keyHandler(key) + if key ~= "f" then + return + elseif getPanelVisibility() then + -- handlePartShow + setPanelVisibility(false) + if lastStampModel then + if stampControl then + stampControl.Destroy() + end + setupStamper(lastStampModel) end - setupStamper(lastStampModel, mouse) - end -end - -function handlePartShow() - if getPanelVisibility() then - partOff() else - partOn() + pickPart() end end -On = function() - if not game.Workspace.Terrain then +function setupStamper(model) + if not model then + return + end + stampControl = getRbxStamper().SetupStamperDragger(model, mouse) + if not stampControl then + return + end + stampCon = stampControl.Stamped.Changed:connect(function() + if stampControl.Stamped.Value then + stampControl.ReloadModel() + end + end) +end + +function updateRecentParts(newName, newId, newTerrainShape) + if not newId then return end - if this then + for i = 1, #recentButtonStack do + if recentButtonStack[i].Id == newId then -- already have item, nothing to do + return + end + end + for i = #recentButtonStack - 1, 1, -1 do + recentButtonStack[i + 1].Id = recentButtonStack[i].Id + recentButtonStack[i + 1].Name = recentButtonStack[i].Name + recentButtonStack[i + 1].TerrainShape = + recentButtonStack[i].TerrainShape + + recentButtonStack[i + 1].Button.Image = + recentButtonStack[i].Button.Image + end + + recentButtonStack[1].Id = newId + recentButtonStack[1].Name = newName + recentButtonStack[1].TerrainShape = newTerrainShape + recentButtonStack[1].Button.Image = BaseUrl + .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=75&ht=75&aid=" + .. tostring(newId) +end + +------ +--GUI- +------ + +function createGui() + --Insert Panel + currStampGui, setPanelVisibility, getPanelVisibility, waterTypeChangedEvent = + getRbxGui().CreateSetPanel( + userSetIds, + partSelected, + dialogClosed, + UDim2.new(0.8, 0, 0.9, 0), + UDim2.new(0.1, 0, 0.05, 0), + true + ) + setPanelVisibility(false) + + currStampGui.Parent = CoreGui + + waterTypeChangedEvent.Event:connect(function(waterTable) + waterForceAndDirection = waterTable + updateWaterInfo() + end) + + -- Loading Gui + New "Frame" { + Name = "LoadingFrame", + Style = Enum.FrameStyle.RobloxRound, + Size = UDim2.new(0, 350, 0, 60), + Visible = false, + Position = UDim2.new(0.5, -175, 0.5, -30), + Parent = currStampGui, + New "TextLabel" { + Name = "LoadingText", + BackgroundTransparency = 1, + Size = UDim2.new(0, 155, 1, 0), + Font = Enum.Font.ArialBold, + FontSize = Enum.FontSize.Size36, + Text = "Loading...", + TextColor3 = Color3.new(1, 1, 1), + TextStrokeTransparency = 0, + }, + } + + -- Recents Stack Gui + recentsFrame = New "Frame" { + BackgroundTransparency = 0.5, + Name = "RecentsFrame", + BackgroundColor3 = Color3.new(0, 0, 0), + Size = UDim2.new(0, 50, 0, 150), + Visible = false, + Parent = currStampGui, + } + + local function recentButton() + return New "ImageButton" { + Style = Enum.ButtonStyle.RobloxButton, + } + end + + for i = 1, 3 do + recentButtonStack[i] = {} + recentButtonStack[i].Name = nil + recentButtonStack[i].Id = nil + recentButtonStack[i].TerrainShape = nil + end + + recentButtonStack[1].Button = Hydrate(recentButton()) { + Name = "RecentButtonOne", + Size = UDim2.new(0, 50, 0, 50), + Parent = recentsFrame, + } + recentButtonStack[2].Button = Hydrate(recentButton()) { + Name = "RecentButtonTwo", + Position = UDim2.new(0, 0, 0, 50), + Parent = recentsFrame, + } + recentButtonStack[3].Button = Hydrate(recentButton()) { + Name = "RecentButtonThree", + Position = UDim2.new(0, 0, 0, 100), + Parent = recentsFrame, + } + + local buttonClicked = false + + for i = 1, #recentButtonStack do + recentButtonStack[i].Button.MouseButton1Click:connect(function() + if buttonClicked then + return + end + buttonClicked = true + partSelected( + recentButtonStack[i].Name, + recentButtonStack[i].Id, + recentButtonStack[i].TerrainShape + ) + buttonClicked = false + end) + end +end + +function On() + if not game.Workspace.Terrain then + return + elseif this then this:Activate(true) mouse = this:GetMouse() end @@ -268,7 +405,7 @@ On = function() on = true end -Off = function() +function Off() if toolbarbutton then toolbarbutton:SetActive(false) end @@ -301,144 +438,6 @@ Off = function() on = false end -function setupStamper(model, mouse) - if model then - stampControl = getRbxStamper().SetupStamperDragger(model, mouse) - if stampControl then - stampCon = stampControl.Stamped.Changed:connect(function() - if stampControl.Stamped.Value then - stampControl.ReloadModel() - end - end) - end - end -end - -function updateRecentParts(newName, newId, newTerrainShape) - if newId then - for i = 1, #recentButtonStack do - if recentButtonStack[i].Id == newId then -- already have item, nothing to do - return - end - end - for i = #recentButtonStack - 1, 1, -1 do - recentButtonStack[i + 1].Id = recentButtonStack[i].Id - recentButtonStack[i + 1].Name = recentButtonStack[i].Name - recentButtonStack[i + 1].TerrainShape = - recentButtonStack[i].TerrainShape - - recentButtonStack[i + 1].Button.Image = - recentButtonStack[i].Button.Image - end - - recentButtonStack[1].Id = newId - recentButtonStack[1].Name = newName - recentButtonStack[1].TerrainShape = newTerrainShape - recentButtonStack[1].Button.Image = BaseUrl - .. "Game/Tools/ThumbnailAsset.ashx?fmt=png&wd=75&ht=75&aid=" - .. tostring(newId) - end -end - ------- ---GUI- ------- - -function createGui() - --Insert Panel - currStampGui, setPanelVisibility, getPanelVisibility, waterTypeChangedEvent = - getRbxGui().CreateSetPanel( - userSetIds, - partSelected, - dialogClosed, - UDim2.new(0.8, 0, 0.9, 0), - UDim2.new(0.1, 0, 0.05, 0), - true - ) - setPanelVisibility(false) - - currStampGui.Parent = CoreGui - - waterTypeChangedEvent.Event:connect(function(waterTable) - waterForceAndDirection = waterTable - updateWaterInfo() - end) - - -- Loading Gui - local loadingFrame = Instance.new "Frame" - loadingFrame.Name = "LoadingFrame" - loadingFrame.Style = Enum.FrameStyle.RobloxRound - loadingFrame.Size = UDim2.new(0, 350, 0, 60) - loadingFrame.Visible = false - loadingFrame.Position = UDim2.new(0.5, -175, 0.5, -30) - - local loadingText = Instance.new "TextLabel" - loadingText.Name = "LoadingText" - loadingText.BackgroundTransparency = 1 - loadingText.Size = UDim2.new(0, 155, 1, 0) - loadingText.Font = Enum.Font.ArialBold - loadingText.FontSize = Enum.FontSize.Size36 - loadingText.Text = "Loading..." - loadingText.TextColor3 = Color3.new(1, 1, 1) - loadingText.TextStrokeTransparency = 0 - loadingText.Parent = loadingFrame - - loadingFrame.Parent = currStampGui - - -- Recents Stack Gui - recentsFrame = Instance.new "Frame" - recentsFrame.BackgroundTransparency = 0.5 - recentsFrame.Name = "RecentsFrame" - recentsFrame.BackgroundColor3 = Color3.new(0, 0, 0) - recentsFrame.Size = UDim2.new(0, 50, 0, 150) - recentsFrame.Visible = false - recentsFrame.Parent = currStampGui - - local recentButtonOne = Instance.new "ImageButton" - recentButtonOne.Style = Enum.ButtonStyle.RobloxButton - recentButtonOne.Name = "RecentButtonOne" - recentButtonOne.Size = UDim2.new(0, 50, 0, 50) - recentButtonOne.Parent = recentsFrame - - local recentButtonTwo = recentButtonOne:clone() - recentButtonTwo.Name = "RecentButtonTwo" - recentButtonTwo.Position = UDim2.new(0, 0, 0, 50) - recentButtonTwo.Parent = recentsFrame - - local recentButtonThree = recentButtonOne:clone() - recentButtonThree.Name = "RecentButtonThree" - recentButtonThree.Position = UDim2.new(0, 0, 0, 100) - recentButtonThree.Parent = recentsFrame - - for i = 1, 3 do - recentButtonStack[i] = {} - recentButtonStack[i].Name = nil - recentButtonStack[i].Id = nil - recentButtonStack[i].TerrainShape = nil - end - - recentButtonStack[1].Button = recentButtonOne - recentButtonStack[2].Button = recentButtonTwo - recentButtonStack[3].Button = recentButtonThree - - local buttonClicked = false - - for i = 1, #recentButtonStack do - recentButtonStack[i].Button.MouseButton1Click:connect(function() - if buttonClicked then - return - end - buttonClicked = true - partSelected( - recentButtonStack[i].Name, - recentButtonStack[i].Id, - recentButtonStack[i].TerrainShape - ) - buttonClicked = false - end) - end -end - -------------------------- --SUCCESSFUL LOAD MESSAGE- -------------------------- diff --git a/terrain plugins/11 - floodfill.luau b/terrain plugins/11 - floodfill.luau index 703f7b9..12615c6 100644 --- a/terrain plugins/11 - floodfill.luau +++ b/terrain plugins/11 - floodfill.luau @@ -1,4 +1,4 @@ -while game == nil do +while not game do wait() end @@ -6,6 +6,12 @@ local ChangeHistoryService = game:GetService "ChangeHistoryService" local ContentProvider = game:GetService "ContentProvider" local CoreGui = game:GetService "CoreGui" +local CreateStandardButton = require "../Modules/Terrain/CreateStandardButton" +local CreateStandardLabel = require "../Modules/Terrain/CreateStandardLabel" +local News = require "../Modules/New" +local New = News.New +local Hydrate = News.Hydrate + --------------- --PLUGIN SETUP- --------------- @@ -13,6 +19,7 @@ local loaded = false local on = false local On, Off +local mouseDown, mouseUp local this = PluginManager():CreatePlugin() local mouse = this:GetMouse() @@ -139,9 +146,6 @@ function MouseHighlighter.Create(mouseUse) highlighter.selectionPart = nil -- Hook the mouse up to check for movement. - mouseH.Move:connect(function() - MouseMoved() - end) mouseH.Button1Down:connect(function() highlighter.mouseDown = true @@ -239,9 +243,7 @@ function MouseHighlighter.Create(mouseUse) local function UpdatePosition(position) if not position then return - end - - if not mouseH.Target then + elseif not mouseH.Target then stopTween() highlighter.selectionPart.Parent = nil return @@ -297,12 +299,12 @@ function MouseHighlighter.Create(mouseUse) end end - -- Function to call when the mouse has moved. Updates where to display the highlighter. - function MouseMoved() + -- Function to call when the mouse has moved. Updates where to display the highlighter. + mouseH.Move:connect(function() if on and not processing then UpdatePosition(mouseH.Hit) end - end + end) return highlighter end @@ -338,68 +340,6 @@ mouseHighlighter:DisablePreview() local ConfirmationPopup = {} ConfirmationPopup.__index = ConfirmationPopup --- Create a standard text label. Use this for all lables in the popup so it is easy to standardize. --- labelName - What to set the text label name as. --- pos - Where to position the label. Should be of type UDim2. --- size - How large to make the label. Should be of type UDim2. --- text - Text to display. --- parent - What to set the text parent as. --- Return: --- Value is the created label. -function CreateStandardLabel(labelName, pos, size, text, parent) - local label = Instance.new "TextLabel" - label.Name = labelName - label.Position = pos - label.Size = size - label.Text = text - label.TextColor3 = Color3.new(0.95, 0.95, 0.95) - label.Font = Enum.Font.ArialBold - label.FontSize = Enum.FontSize.Size14 - label.TextXAlignment = Enum.TextXAlignment.Left - label.BackgroundTransparency = 1 - label.Parent = parent - - return label -end - --- Keep common button properties here to make it easer to change them all at once. --- These are the default properties to use for a button. -local buttonTextColor = Color3.new(1, 1, 1) -local buttonFont = Enum.Font.ArialBold -local buttonFontSize = Enum.FontSize.Size18 - --- Create a standard dropdown. Use this for all dropdowns in the popup so it is easy to standardize. --- name - What to use. --- pos - Where to position the button. Should be of type UDim2. --- text - Text to show in the button. --- funcOnPress - Function to run when the button is pressed. --- parent - What to set the parent as. --- Return: --- button - The button gui. -function CreateStandardButton(name, pos, text, funcOnPress, parent, size) - local button = Instance.new "TextButton" - button.Name = name - button.Position = pos - - button.Size = UDim2.new(0, 120, 0, 40) - button.Text = text - - if size then - button.Size = size - end - - button.Style = Enum.ButtonStyle.RobloxButton - - button.TextColor3 = buttonTextColor - button.Font = buttonFont - button.FontSize = buttonFontSize - button.Parent = parent - - button.MouseButton1Click:connect(funcOnPress) - - return button -end - -- Create a confirmation popup. -- -- confirmText - What to display in the popup. @@ -417,46 +357,54 @@ function ConfirmationPopup.Create( confirmFunction, declineFunction ) - local popup = {} - popup.confirmButton = nil -- Hold the button to confirm a choice. - popup.declineButton = nil -- Hold the button to decline a choice. - popup.confirmationFrame = nil -- Hold the conformation frame. - popup.confirmationText = nil -- Hold the text label to display the conformation message. - popup.confirmationHelpText = nil -- Hold the text label to display the conformation message help. + local popup = { + confirmButton = nil, -- Hold the button to confirm a choice. + declineButton = nil, -- Hold the button to decline a choice. + confirmationFrame = nil, -- Hold the conformation frame. + confirmationText = nil, -- Hold the text label to display the conformation message. + confirmationHelpText = nil, -- Hold the text label to display the conformation message help. + } - popup.confirmationFrame = Instance.new "Frame" - popup.confirmationFrame.Name = "ConfirmationFrame" - popup.confirmationFrame.Size = UDim2.new(0, 280, 0, 160) - popup.confirmationFrame.Position = UDim2.new( - 0.5, - -popup.confirmationFrame.Size.X.Offset / 2, - 0.5, - -popup.confirmationFrame.Size.Y.Offset / 2 - ) - popup.confirmationFrame.Style = Enum.FrameStyle.RobloxRound - popup.confirmationFrame.Parent = screenGui + popup.confirmationFrame = New "Frame" { + Name = "ConfirmationFrame", + Size = UDim2.new(0, 280, 0, 160), + Position = UDim2.new( + 0.5, + -popup.confirmationFrame.Size.X.Offset / 2, + 0.5, + -popup.confirmationFrame.Size.Y.Offset / 2 + ), + Style = Enum.FrameStyle.RobloxRound, + Parent = screenGui, + } - popup.confirmLabel = CreateStandardLabel( - "ConfirmLabel", - UDim2.new(0, 0, 0, 15), - UDim2.new(1, 0, 0, 24), - confirmText, - popup.confirmationFrame - ) - popup.confirmLabel.FontSize = Enum.FontSize.Size18 - popup.confirmLabel.TextXAlignment = Enum.TextXAlignment.Center + popup.confirmLabel = Hydrate( + CreateStandardLabel( + "ConfirmLabel", + UDim2.new(0, 0, 0, 15), + UDim2.new(1, 0, 0, 24), + confirmText, + popup.confirmationFrame + ) + ) { + FontSize = Enum.FontSize.Size18, + TextXAlignment = Enum.TextXAlignment.Center, + } - popup.confirmationHelpText = CreateStandardLabel( - "ConfirmSmallLabel", - UDim2.new(0, 0, 0, 40), - UDim2.new(1, 0, 0, 28), - confirmSmallText, - popup.confirmationFrame - ) - popup.confirmationHelpText.FontSize = Enum.FontSize.Size14 - popup.confirmationHelpText.TextWrap = true - popup.confirmationHelpText.Font = Enum.Font.Arial - popup.confirmationHelpText.TextXAlignment = Enum.TextXAlignment.Center + popup.confirmationHelpText = Hydrate( + CreateStandardLabel( + "ConfirmSmallLabel", + UDim2.new(0, 0, 0, 40), + UDim2.new(1, 0, 0, 28), + confirmSmallText, + popup.confirmationFrame + ) + ) { + FontSize = Enum.FontSize.Size14, + TextWrap = true, + Font = Enum.Font.Arial, + TextXAlignment = Enum.TextXAlignment.Center, + } -- Confirm popup.confirmButton = CreateStandardButton( @@ -509,68 +457,11 @@ end --FUNCTION DEFINITIONS- ----------------------- -local floodFill = function(x, y, z) - LoadProgressBar "Processing" - breadthFill(x, y, z) - UnloadProgressBar() - ChangeHistoryService:SetWaypoint "FloodFill" -end - --- Function used when we try and flood fill. Prompts the user first. --- Will not show if disabled or terrain is being processed. -function ConfirmFloodFill(x, y, z) - -- Only do if something isn't already being processed. - if not processing then - processing = true - if nil == ConfirmationPopupObject then - ConfirmationPopupObject = ConfirmationPopup.Create( - "Flood Fill At Selected Location?", - "This operation may take some time.", - "Fill", - "Cancel", - function() - ConfirmationPopupObject:Clear() - ConfirmationPopupObject = nil - floodFill(x, y, z) - ConfirmationPopupObject = nil - end, - function() - ConfirmationPopupObject:Clear() - ConfirmationPopupObject = nil - processing = false - end - ) - end - end -end - -function mouseDown(mouseD) - if on and mouseD.Target == game.Workspace.Terrain then - startCell = mouseHighlighter:GetPosition() - end -end - -function mouseUp(_) - if processing then - return - end - - local upCell = mouseHighlighter:GetPosition() - if startCell == upCell then - ConfirmFloodFill(upCell.x, upCell.y, upCell.z) - end -end - -function getMaterial(x, y, z) - local material = GetCell(c, x, y, z) - return material -end - -- function startLoadingFrame() end -- Load the progress bar to display when drawing a river. -- text - Text to display. -function LoadProgressBar(text) +local function LoadProgressBar(text) processing = true -- Start the progress bar. @@ -597,31 +488,33 @@ function LoadProgressBar(text) -- spin = false end) - local spinnerFrame = Instance.new "Frame" - spinnerFrame.Name = "Spinner" - spinnerFrame.Size = UDim2.new(0, 80, 0, 80) - spinnerFrame.Position = UDim2.new(0.5, -40, 0.5, -55) - spinnerFrame.BackgroundTransparency = 1 - spinnerFrame.Parent = progressBar + local spinnerFrame = New "Frame" { + Name = "Spinner", + Size = UDim2.new(0, 80, 0, 80), + Position = UDim2.new(0.5, -40, 0.5, -55), + BackgroundTransparency = 1, + Parent = progressBar, + } local spinnerIcons = {} local spinnerNum = 1 while spinnerNum <= 8 do - local spinnerImage = Instance.new "ImageLabel" - spinnerImage.Name = "Spinner" .. spinnerNum - spinnerImage.Size = UDim2.new(0, 16, 0, 16) - spinnerImage.Position = UDim2.new( - 0.5 + 0.3 * math.cos(math.rad(45 * spinnerNum)), - -8, - 0.5 + 0.3 * math.sin(math.rad(45 * spinnerNum)), - -8 - ) - spinnerImage.BackgroundTransparency = 1 - spinnerImage.Image = "http://banland.xyz/asset?id=45880710" - spinnerImage.Parent = spinnerFrame + local spinnerImage = New "ImageLabel" { + Name = "Spinner" .. spinnerNum, + Size = UDim2.new(0, 16, 0, 16), + Position = UDim2.new( + 0.5 + 0.3 * math.cos(math.rad(45 * spinnerNum)), + -8, + 0.5 + 0.3 * math.sin(math.rad(45 * spinnerNum)), + -8 + ), + BackgroundTransparency = 1, + Image = "http://banland.xyz/asset?id=45880710", + Parent = spinnerFrame, + } spinnerIcons[spinnerNum] = spinnerImage - spinnerNum = spinnerNum + 1 + spinnerNum += 1 end --Make it spin @@ -648,7 +541,7 @@ function LoadProgressBar(text) end -- Unload the progress bar. -function UnloadProgressBar() +local function UnloadProgressBar() processing = false if progressBar then @@ -663,36 +556,68 @@ function UnloadProgressBar() end end -function breadthFill(x, y, z) - local yDepthChecks = doBreadthFill(x, y, z) - while yDepthChecks and #yDepthChecks > 0 do - local newYChecks = {} - for i = 1, #yDepthChecks do - local currYDepthChecks = doBreadthFill( - yDepthChecks[i].xPos, - yDepthChecks[i].yPos, - yDepthChecks[i].zPos - ) +local function floodFill(x, y, z) + LoadProgressBar "Processing" + breadthFill(x, y, z) + UnloadProgressBar() + ChangeHistoryService:SetWaypoint "FloodFill" +end - if not processing then - return - end +-- Function used when we try and flood fill. Prompts the user first. +-- Will not show if disabled or terrain is being processed. +local function ConfirmFloodFill(x, y, z) + -- Only do if something isn't already being processed. + if processing then + return + end - if currYDepthChecks and #currYDepthChecks > 0 then - for j = 1, #currYDepthChecks do - table.insert(newYChecks, { - xPos = currYDepthChecks[j].xPos, - yPos = currYDepthChecks[j].yPos, - zPos = currYDepthChecks[j].zPos, - }) - end - end + processing = true + if ConfirmationPopupObject then + return + end + + ConfirmationPopupObject = ConfirmationPopup.Create( + "Flood Fill At Selected Location?", + "This operation may take some time.", + "Fill", + "Cancel", + function() + ConfirmationPopupObject:Clear() + ConfirmationPopupObject = nil + floodFill(x, y, z) + ConfirmationPopupObject = nil + end, + function() + ConfirmationPopupObject:Clear() + ConfirmationPopupObject = nil + processing = false end - yDepthChecks = newYChecks + ) +end + +function mouseDown(mouseD) + if on and mouseD.Target == game.Workspace.Terrain then + startCell = mouseHighlighter:GetPosition() end end -function doBreadthFill(x, y, z) +function mouseUp(_) + if processing then + return + end + + local upCell = mouseHighlighter:GetPosition() + if startCell == upCell then + ConfirmFloodFill(upCell.x, upCell.y, upCell.z) + end +end + +local function getMaterial(x, y, z) + local material = GetCell(c, x, y, z) + return material +end + +local function doBreadthFill(x, y, z) if getMaterial(x, y, z) ~= emptyMaterial then return end @@ -735,7 +660,36 @@ function doBreadthFill(x, y, z) return yDepthChecks end -function cellInTerrain(x, y, z) +function breadthFill(x, y, z) + local yDepthChecks = doBreadthFill(x, y, z) + while yDepthChecks and #yDepthChecks > 0 do + local newYChecks = {} + for i = 1, #yDepthChecks do + local currYDepthChecks = doBreadthFill( + yDepthChecks[i].xPos, + yDepthChecks[i].yPos, + yDepthChecks[i].zPos + ) + + if + not (processing and currYDepthChecks and #currYDepthChecks > 0) + then + return + end + + for j = 1, #currYDepthChecks do + table.insert(newYChecks, { + xPos = currYDepthChecks[j].xPos, + yPos = currYDepthChecks[j].yPos, + zPos = currYDepthChecks[j].zPos, + }) + end + end + yDepthChecks = newYChecks + end +end + +local function cellInTerrain(x, y, z) if x < c.MaxExtents.Min.X or x >= c.MaxExtents.Max.X then return false end @@ -811,8 +765,7 @@ end On = function() if not c then return - end - if this then + elseif this then this:Activate(true) end if toolbarbutton then @@ -858,25 +811,27 @@ dragBar, containerFrame, helpFrame, closeEvent = RbxGui.CreatePluginFrame( ) dragBar.Visible = false -helpFrame.Size = UDim2.new(0, 200, 0, 190) - -local textHelp = Instance.new "TextLabel" -textHelp.Name = "TextHelp" -textHelp.Font = Enum.Font.ArialBold -textHelp.FontSize = Enum.FontSize.Size12 -textHelp.TextColor3 = Color3.new(1, 1, 1) -textHelp.Size = UDim2.new(1, -6, 1, -6) -textHelp.Position = UDim2.new(0, 3, 0, 3) -textHelp.TextXAlignment = Enum.TextXAlignment.Left -textHelp.TextYAlignment = Enum.TextYAlignment.Top -textHelp.BackgroundTransparency = 1 -textHelp.TextWrap = true -textHelp.Text = [[ +Hydrate(helpFrame) { + Size = UDim2.new(0, 200, 0, 190), + -- "This will be pdfs in 2024" - Heliodex + New "TextLabel" { + Name = "TextHelp", + Font = Enum.Font.ArialBold, + FontSize = Enum.FontSize.Size12, + TextColor3 = Color3.new(1, 1, 1), + Size = UDim2.new(1, -6, 1, -6), + Position = UDim2.new(0, 3, 0, 3), + TextXAlignment = Enum.TextXAlignment.Left, + TextYAlignment = Enum.TextYAlignment.Top, + BackgroundTransparency = 1, + TextWrap = true, + Text = [[ Quickly replace empty terrain cells with a selected material. Clicking the mouse will cause any empty terrain cells around the point clicked to be filled with the current material, and will also spread to surrounding empty cells (including any empty cells below, but not above). Simply click on a different material to fill with that material. The floating paint bucket and cube indicate where filling will start. -]] -textHelp.Parent = helpFrame +]], + }, +} closeEvent.Event:connect(function() Off()