Formatting improvements and modularisation of terrain plugins

This commit is contained in:
Lewin Kelly 2024-04-10 07:44:11 +01:00
parent ca31246727
commit 98561c76e0
12 changed files with 1087 additions and 1198 deletions

View File

@ -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

View File

@ -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

View File

@ -1,11 +1,17 @@
-- Local function definitions --!strict
while game == nil do while not game do
wait() wait()
end end
local ChangeHistoryService = game:GetService "ChangeHistoryService" local ChangeHistoryService = game:GetService "ChangeHistoryService"
local CoreGui = game:GetService "CoreGui" 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- --PLUGIN SETUP-
--------------- ---------------
@ -147,18 +153,20 @@ end)
terrainHelpFrame.Size = UDim2.new(0, 300, 0, 350) terrainHelpFrame.Size = UDim2.new(0, 300, 0, 350)
local terrainHelpText = Instance.new "TextLabel" local function helpText()
terrainHelpText.Name = "HelpText" return New "TextLabel" {
terrainHelpText.Font = Enum.Font.ArialBold Font = Enum.Font.ArialBold,
terrainHelpText.FontSize = Enum.FontSize.Size12 FontSize = Enum.FontSize.Size12,
terrainHelpText.TextColor3 = Color3.new(227 / 255, 227 / 255, 227 / 255) TextColor3 = Color3.new(227 / 255, 227 / 255, 227 / 255),
terrainHelpText.TextXAlignment = Enum.TextXAlignment.Left TextXAlignment = Enum.TextXAlignment.Left,
terrainHelpText.TextYAlignment = Enum.TextYAlignment.Top TextYAlignment = Enum.TextYAlignment.Top,
terrainHelpText.Position = UDim2.new(0, 4, 0, 4) BackgroundTransparency = 1,
terrainHelpText.Size = UDim2.new(1, -8, 0, 157) TextWrap = true,
terrainHelpText.BackgroundTransparency = 1 Parent = terrainHelpFrame,
terrainHelpText.TextWrap = true }
terrainHelpText.Text = [[Create terrain with hills, water. end
local helpText1 = [[Create terrain with hills, water.
X-Offset and Z-Offset: X-Offset and Z-Offset:
Center point of terrain that will be created. 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. 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: Amplitude:
Maximum height of hills. Maximum height of hills.
]] ]]
terrainHelpText.Parent = terrainHelpFrame local helpText2 = [[Frequency:
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:
How often hills are made. How often hills are made.
Base Height: Base Height:
@ -185,13 +187,7 @@ How high the base of terrain should be.
Water Height: Water Height:
How high water should be. Terrain will overwrite water. How high water should be. Terrain will overwrite water.
]] ]]
helpSecondText.Parent = terrainHelpText local helpText3 = [[
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 = [[
Generate Button: Generate Button:
Create the terrain. Create the terrain.
@ -199,7 +195,26 @@ Create the terrain.
Clear Button: Clear Button:
Remove all terrain. 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. -- Used to create a highlighter.
-- A highlighter is a open, rectuangular area displayed in 3D. -- A highlighter is a open, rectuangular area displayed in 3D.
@ -225,21 +240,22 @@ function Highlighter.Create(_, ID, color)
highlighter.z = 0 highlighter.z = 0
-- Create the part that the highlighter will be attached to. -- Create the part that the highlighter will be attached to.
highlighter.selectionPart = Instance.new "Part" highlighter.selectionPart = New "Part" {
highlighter.selectionPart.Name = "SelectionPart" Name = "SelectionPart",
highlighter.selectionPart.Archivable = false Archivable = false,
highlighter.selectionPart.Transparency = 1 Transparency = 1,
highlighter.selectionPart.Anchored = true Anchored = true,
highlighter.selectionPart.Locked = true Locked = true,
highlighter.selectionPart.CanCollide = false CanCollide = false,
highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom FormFactor = Enum.FormFactor.Custom,
}
highlighter.selectionBox = Instance.new "SelectionBox" highlighter.selectionBox = New "SelectionBox" {
highlighter.selectionBox.Name = "box" .. ID Name = "box" .. ID,
Archivable = false,
highlighter.selectionBox.Archivable = false Color = color,
highlighter.selectionBox.Color = color Adornee = highlighter.selectionPart,
highlighter.selectionBox.Adornee = highlighter.selectionPart }
setmetatable(highlighter, Highlighter) setmetatable(highlighter, Highlighter)
return highlighter return highlighter
@ -257,7 +273,7 @@ end
-- Update where the highlighter is displayed. -- Update where the highlighter is displayed.
-- cellPos - Where to display the highlighter, in cells. -- cellPos - Where to display the highlighter, in cells.
function Highlighter:UpdatePosition(cellPos) function Highlighter:UpdatePosition(cellPos: Vector3)
if not cellPos then if not cellPos then
self:DisablePreview() self:DisablePreview()
return return
@ -269,17 +285,20 @@ function Highlighter:UpdatePosition(cellPos)
self.y = cellPos.y self.y = cellPos.y
self.z = cellPos.z self.z = cellPos.z
local width, length, height =
self.width :: number, self.length :: number, self.height :: number
local lowVec = CellCenterToWorld( local lowVec = CellCenterToWorld(
c, c,
cellPos.x - self.width / 2, cellPos.x - width / 2,
cellPos.y - 1, cellPos.y - 1,
cellPos.z - self.length / 2 cellPos.z - length / 2
) )
local highVec = CellCenterToWorld( local highVec = CellCenterToWorld(
c, c,
cellPos.x + self.width / 2, cellPos.x + width / 2,
cellPos.y + self.height, cellPos.y + height,
cellPos.z + self.length / 2 cellPos.z + length / 2
) )
regionToSelect = Region3.new(lowVec, highVec) regionToSelect = Region3.new(lowVec, highVec)
@ -292,37 +311,17 @@ end
-- length - Length of the area (z direction) -- length - Length of the area (z direction)
-- width - Width of the area (x direction) -- width - Width of the area (x direction)
-- height - Height of the area (y 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.length = length
self.width = width self.width = width
self.height = height self.height = height
self:UpdatePosition(Vector3.new(self.x, self.y, self.z)) self:UpdatePosition(Vector3.new(self.x, self.y, self.z))
end 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. -- Create a standardized slider.
-- name - Name to use for the slider. -- name - Name to use for the slider.
-- pos - Position to display the slider at. -- pos - Position to display the slider at.
@ -333,7 +332,7 @@ end
-- Return: -- Return:
-- sliderGui - Slider gui object. -- sliderGui - Slider gui object.
-- sliderPosition - Object that can set the slider value. -- sliderPosition - Object that can set the slider value.
function CreateStandardSlider( local function CreateStandardSlider(
name, name,
pos, pos,
lengthBarPos, lengthBarPos,
@ -345,21 +344,24 @@ function CreateStandardSlider(
local sliderGui, sliderPosition = local sliderGui, sliderPosition =
RbxGui.CreateSlider(steps, 0, UDim2.new(0, 0, 0, 0)) RbxGui.CreateSlider(steps, 0, UDim2.new(0, 0, 0, 0))
sliderGui.Name = name Hydrate(sliderGui) {
sliderGui.Parent = parent Name = name,
sliderGui.Position = pos Parent = parent,
sliderGui.Size = UDim2.new(1, 0, 0, 20) Position = pos,
local lengthBar = sliderGui:FindFirstChild "Bar" Size = UDim2.new(1, 0, 0, 20),
lengthBar.Size = UDim2.new(1, -21, 0, 5) }
lengthBar.Position = lengthBarPos Hydrate(sliderGui.Bar) {
Size = UDim2.new(1, -21, 0, 5),
Position = lengthBarPos,
}
if nil ~= funcOnChange then if funcOnChange then
sliderPosition.Changed:connect(function() sliderPosition.Changed:connect(function()
funcOnChange(sliderPosition) funcOnChange(sliderPosition)
end) end)
end end
if nil ~= initValue then if initValue then
sliderPosition.Value = initValue sliderPosition.Value = initValue
end end
@ -390,44 +392,6 @@ end
-- return dropdown, updateSelection -- return dropdown, updateSelection
-- end -- 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 = { local cancelValues = {
cancelAction = false, -- Used to cancel currently occuring actions. If set to true then terrain generation will stop. 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. 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. -- Load the progress bar to display when drawing a river.
-- text - Text to display. -- text - Text to display.
function LoadProgressBar(text) local function LoadProgressBar(text)
if cancelValues.progressBar == nil then if cancelValues.progressBar 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
print "Tried to start the progress bar when it was already running." print "Tried to start the progress bar when it was already running."
return
end 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 end
-- Unload the progress bar. -- Unload the progress bar.
function UnloadProgressBar() local function UnloadProgressBar()
cancelValues.isDrawing = false cancelValues.isDrawing = false
cancelValues.cancelActions = false cancelValues.cancelActions = false
@ -520,8 +485,8 @@ offsetZSliderPosition.Value = (terrainOptions.zpos + 252) / 4 + 1
--FUNCTION DEFINITIONS- --FUNCTION DEFINITIONS-
----------------------- -----------------------
--makes a column of blocks from 1 up to height at location (x,z) in cluster c -- makes a column of blocks from 1 up to height at location (x,z) in cluster c
function coordHeight(x, z, height) local function coordHeight(x, z, height)
SetCells( SetCells(
c, c,
Region3int16.new( Region3int16.new(
@ -534,10 +499,10 @@ function coordHeight(x, z, height)
) )
end end
--makes a heightmap for a layer of mountains (width x depth) -- 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 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 -- with a range of amplitudes between 0 and a
function mountLayer(width, depth, wf, df, a) local function mountLayer(width, depth, wf, df, a)
local heightmap = {} local heightmap = {}
for i = 0, width - 1 do for i = 0, width - 1 do
heightmap[i] = {} heightmap[i] = {}
@ -599,8 +564,8 @@ function mountLayer(width, depth, wf, df, a)
return heightmap return heightmap
end end
--makes a shell around block at coordinate x, z using heightmap -- makes a shell around block at coordinate x, z using heightmap
function makeShell(x, z, heightmap, shellheightmap) local function makeShell(x, z, heightmap, shellheightmap)
local originalheight = heightmap[x][z] local originalheight = heightmap[x][z]
for i = x - 1, x + 1 do for i = x - 1, x + 1 do
for k = z - 1, z + 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. -- 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. -- centerX, centerZ - Center coordinate of land. This doesn't take into account clipping.
-- length, width - Land dimensions. -- 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 currCamera = game.Workspace.CurrentCamera
local cameraPos = Vector3.new(0, 400, 1600) local cameraPos = Vector3.new(0, 400, 1600)
local cameraFocus = Vector3.new(0, height * 4, 0) local cameraFocus = Vector3.new(0, height * 4, 0)
-- Nothing set so use the default. -- Nothing set so use the default.
if nil ~= centerX then if centerX then
local scale = 0 local scale = 0
local lengthScale = 0 local lengthScale = 0
local widthScale = 0 local widthScale = 0
@ -657,11 +622,7 @@ function SetCamera(centerX, centerZ, length, width, height)
widthScale = 0.7 widthScale = 0.7
end end
if widthScale > lengthScale then scale = widthScale > lengthScale and widthScale or lengthScale
scale = widthScale
else
scale = lengthScale
end
local distance = Vector3.new(0, (200 * scale) + 200, (1100 * scale)) local distance = Vector3.new(0, (200 * scale) + 200, (1100 * scale))
cameraPos = cameraPos =
@ -675,12 +636,11 @@ function SetCamera(centerX, centerZ, length, width, height)
end end
-- Create terrain based on the current properties. -- Create terrain based on the current properties.
function GenerateTerrain() local function GenerateTerrain()
toolbarbutton:SetActive(false) toolbarbutton:SetActive(false)
generateOptions = terrainOptions:Clone() generateOptions = terrainOptions:Clone()
-- Turn off the plugin
Off() Off()
-- Create the progress bar that will track terrain creation completion. -- Create the progress bar that will track terrain creation completion.
@ -977,37 +937,45 @@ end
local ConfirmationPopup 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. -- Function used by the generate button. Prompts the user first.
-- Will not show if disabled or terrain is being processed. -- Will not show if disabled or terrain is being processed.
function ConfirmGenerateTerrain() local function ConfirmGenerateTerrain()
-- Only do if something isn't already being processed. -- Only do if something isn't already being processed.
if nil == cancelValues.progressBar then if cancelValues.progressBar or ConfirmationPopupObject then
if nil == ConfirmationPopupObject then return
if not hideGenerateConformation then elseif hideGenerateConformation then
ConfirmationPopupObject = ConfirmationPopup.Create( GenerateTerrain()
"Generate Terrain?", return
"Generate",
"Cancel",
function()
ClearConformation()
GenerateTerrain()
end,
ClearConformation,
function()
hideGenerateConformation = not hideGenerateConformation
return not hideGenerateConformation
end
)
else
GenerateTerrain()
end
end
end end
ConfirmationPopupObject = ConfirmationPopup.Create(
"Generate Terrain?",
"Generate",
"Cancel",
function()
ClearConformation()
GenerateTerrain()
end,
ClearConformation,
function()
hideGenerateConformation = not hideGenerateConformation
return not hideGenerateConformation
end
)
end end
-- Clears all terrain. -- Clears all terrain.
-- Clearing is immediate. -- Clearing is immediate.
function ClearTerrain() local function ClearTerrain()
toolbarbutton:SetActive(false) toolbarbutton:SetActive(false)
Off() Off()
@ -1024,30 +992,29 @@ end
-- Function used by the clear button. Prompts the user first. -- Function used by the clear button. Prompts the user first.
-- Will not show if disabled or terrain is being processed. -- Will not show if disabled or terrain is being processed.
function ConfirmClearTerrain() local function ConfirmClearTerrain()
-- Only do if something isn't already being processed. -- Only do if something isn't already being processed.
if nil == cancelValues.progressBar then if cancelValues.progressBar or ConfirmationPopupObject then
if nil == ConfirmationPopupObject then return
if not hideClearConformation then elseif hideClearConformation then
ConfirmationPopupObject = ConfirmationPopup.Create( ClearTerrain()
"Clear Terrain?", return
"Clear",
"Cancel",
function()
ClearConformation()
ClearTerrain()
end,
ClearConformation,
function()
hideClearConformation = not hideClearConformation
return not hideClearConformation
end
)
else
ClearTerrain()
end
end
end end
ConfirmationPopupObject = ConfirmationPopup.Create(
"Clear Terrain?",
"Clear",
"Cancel",
function()
ClearConformation()
ClearTerrain()
end,
ClearConformation,
function()
hideClearConformation = not hideClearConformation
return not hideClearConformation
end
)
end end
-- Used to create a highlighter. -- Used to create a highlighter.
@ -1072,34 +1039,39 @@ function ConfirmationPopup.Create(
confirmFunction, confirmFunction,
declineFunction declineFunction
) )
local popup = {} local popup = {
popup.confirmButton = nil -- Hold the button to confirm a choice. confirmButton = nil, -- Hold the button to confirm a choice.
popup.declineButton = nil -- Hold the button to decline a choice. declineButton = nil, -- Hold the button to decline a choice.
popup.confirmationFrame = nil -- Hold the conformation frame. confirmationFrame = nil, -- Hold the conformation frame.
popup.confirmationText = nil -- Hold the text label to display the conformation message. confirmationText = nil, -- Hold the text label to display the conformation message.
popup.confirmationHelpText = nil -- Hold the text label to display the conformation message help. confirmationHelpText = nil, -- Hold the text label to display the conformation message help.
}
popup.confirmationFrame = Instance.new "Frame" popup.confirmationFrame = New "Frame" {
popup.confirmationFrame.Name = "ConfirmationFrame" Name = "ConfirmationFrame",
popup.confirmationFrame.Size = UDim2.new(0, 280, 0, 140) Size = UDim2.new(0, 280, 0, 140),
popup.confirmationFrame.Position = UDim2.new( Position = UDim2.new(
0.5, 0.5,
-popup.confirmationFrame.Size.X.Offset / 2, -popup.confirmationFrame.Size.X.Offset / 2,
0.5, 0.5,
-popup.confirmationFrame.Size.Y.Offset / 2 -popup.confirmationFrame.Size.Y.Offset / 2
) ),
popup.confirmationFrame.Style = Enum.FrameStyle.RobloxRound Style = Enum.FrameStyle.RobloxRound,
popup.confirmationFrame.Parent = g Parent = g,
}
popup.confirmLabel = CreateStandardLabel( popup.confirmLabel = Hydrate(
"ConfirmLabel", CreateStandardLabel(
UDim2.new(0, 0, 0, 15), "ConfirmLabel",
UDim2.new(1, 0, 0, 24), UDim2.new(0, 0, 0, 15),
confirmText, UDim2.new(1, 0, 0, 24),
popup.confirmationFrame confirmText,
) popup.confirmationFrame
popup.confirmLabel.FontSize = Enum.FontSize.Size18 )
popup.confirmLabel.TextXAlignment = Enum.TextXAlignment.Center ) {
FontSize = Enum.FontSize.Size18,
TextXAlignment = Enum.TextXAlignment.Center,
}
-- Confirm -- Confirm
popup.confirmButton = CreateStandardButton( popup.confirmButton = CreateStandardButton(
@ -1126,19 +1098,19 @@ end
-- Clear the popup, free up assets. -- Clear the popup, free up assets.
function ConfirmationPopup:Clear() function ConfirmationPopup:Clear()
if nil ~= self.confirmButton then if self.confirmButton then
self.confirmButton.Parent = nil self.confirmButton.Parent = nil
end end
if nil ~= self.declineButton then if self.declineButton then
self.declineButton.Parent = nil self.declineButton.Parent = nil
end end
if nil ~= self.confirmationFrame then if self.confirmationFrame then
self.confirmationFrame.Parent = nil self.confirmationFrame.Parent = nil
end end
if nil ~= self.confirmLabel then if self.confirmLabel then
self.confirmLabel.Parent = nil self.confirmLabel.Parent = nil
end end
@ -1163,7 +1135,8 @@ local widthLabel = CreateStandardLabel(
"", "",
terrainFrame terrainFrame
) )
local _, _ --[[widthSliderGui, widthSliderPosition]] = CreateStandardSlider( -- local _, _ widthSliderGui, widthSliderPosition =
CreateStandardSlider(
"WidthSliderGui", "WidthSliderGui",
UDim2.new(0, 1, 0, 108), UDim2.new(0, 1, 0, 108),
UDim2.new(0, 10, 0.5, -2), UDim2.new(0, 10, 0.5, -2),
@ -1184,7 +1157,8 @@ local lengthLabel = CreateStandardLabel(
"", "",
terrainFrame terrainFrame
) )
local _, _ --[[lengthSliderGui, lengthSliderPosition]] = CreateStandardSlider( -- local _, _ lengthSliderGui, lengthSliderPosition =
CreateStandardSlider(
"LengthSliderGui", "LengthSliderGui",
UDim2.new(0, 1, 0, 149), UDim2.new(0, 1, 0, 149),
UDim2.new(0, 10, 0.5, -2), UDim2.new(0, 10, 0.5, -2),
@ -1305,17 +1279,8 @@ CreateStandardButton(
UDim2.new(0, 150, 0, 40) 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. -- Run when the popup is activated.
On = function() function On()
if not c then if not c then
return return
end end
@ -1326,7 +1291,7 @@ On = function()
end end
-- Run when the popup is deactivated. -- Run when the popup is deactivated.
Off = function() function Off()
toolbarbutton:SetActive(false) toolbarbutton:SetActive(false)
ClearConformation() ClearConformation()
on = false on = false

View File

@ -1,10 +1,13 @@
while game == nil do while not game do
wait() wait()
end end
local ChangeHistoryService = game:GetService "ChangeHistoryService" local ChangeHistoryService = game:GetService "ChangeHistoryService"
local CoreGui = game:GetService "CoreGui" local CoreGui = game:GetService "CoreGui"
local CreateStandardLabel = require "../Modules/Terrain/CreateStandardLabel"
local New = require("../Modules/New").New
--------------- ---------------
--PLUGIN SETUP- --PLUGIN SETUP-
--------------- ---------------
@ -96,19 +99,21 @@ function MouseHighlighter.Create(mouseUse)
end) end)
-- Create the part that the highlighter will be attached to. -- Create the part that the highlighter will be attached to.
highlighter.selectionPart = Instance.new "Part" highlighter.selectionPart = New "Part" {
highlighter.selectionPart.Name = "SelectionPart" Name = "SelectionPart",
highlighter.selectionPart.Archivable = false Archivable = false,
highlighter.selectionPart.Transparency = 1 Transparency = 1,
highlighter.selectionPart.Anchored = true Anchored = true,
highlighter.selectionPart.Locked = true Locked = true,
highlighter.selectionPart.CanCollide = false CanCollide = false,
highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom 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 mouseH.TargetFilter = highlighter.selectionPart
setmetatable(highlighter, MouseHighlighter) setmetatable(highlighter, MouseHighlighter)
@ -119,7 +124,7 @@ function MouseHighlighter.Create(mouseUse)
-- Return: -- Return:
-- success - Value is true if there was a plane intersection, false if not. -- 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. -- 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 currCamera = game.Workspace.CurrentCamera
local startPos = Vector3.new( local startPos = Vector3.new(
currCamera.CoordinateFrame.p.X, currCamera.CoordinateFrame.p.X,
@ -147,7 +152,7 @@ function MouseHighlighter.Create(mouseUse)
-- Update where the highlighter is displayed. -- Update where the highlighter is displayed.
-- position - Where to display the highlighter, in world space. -- position - Where to display the highlighter, in world space.
UpdatePosition = function(position) function UpdatePosition(position)
if not position then if not position then
return return
end end
@ -214,30 +219,6 @@ end
local mouseHighlighter = MouseHighlighter.Create(mouse) local mouseHighlighter = MouseHighlighter.Create(mouse)
mouseHighlighter:DisablePreview() 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- --GUI-
------ ------
@ -372,7 +353,7 @@ mouse.Button1Down:connect(onClicked)
mouseHighlighter.OnClicked = onClicked mouseHighlighter.OnClicked = onClicked
-- Run when the popup is activated. -- Run when the popup is activated.
On = function() function On()
if not c then if not c then
return return
end end
@ -383,7 +364,7 @@ On = function()
end end
-- Run when the popup is deactivated. -- Run when the popup is deactivated.
Off = function() function Off()
toolbarbutton:SetActive(false) toolbarbutton:SetActive(false)
on = false on = false

View File

@ -1,10 +1,12 @@
while game == nil do while not game do
wait() wait()
end end
local ChangeHistoryService = game:GetService "ChangeHistoryService" local ChangeHistoryService = game:GetService "ChangeHistoryService"
local CoreGui = game:GetService "CoreGui" local CoreGui = game:GetService "CoreGui"
local CreateStandardLabel = require "../Modules/Terrain/CreateStandardLabel"
----------------- -----------------
--DEFAULT VALUES- --DEFAULT VALUES-
----------------- -----------------
@ -170,30 +172,6 @@ end
-- Create the mouse movement highlighter. -- Create the mouse movement highlighter.
local mouseHighlighter = MouseHighlighter.Create(mouse) 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- --GUI-
------ ------
@ -283,11 +261,10 @@ mouse.Button1Down:connect(onClicked)
mouseHighlighter.OnClicked = onClicked mouseHighlighter.OnClicked = onClicked
-- Run when the popup is activated. -- Run when the popup is activated.
On = function() function On()
if not c then if not c then
return return
end elseif plugin then
if plugin then
plugin:Activate(true) plugin:Activate(true)
end end
if toolbarbutton then if toolbarbutton then
@ -300,7 +277,7 @@ On = function()
end end
-- Run when the popup is deactivated. -- Run when the popup is deactivated.
Off = function() function Off()
on = false on = false
if toolbarbutton then if toolbarbutton then

View File

@ -1,10 +1,12 @@
while game == nil do while not game do
wait() wait()
end end
local ChangeHistoryService = game:GetService "ChangeHistoryService" local ChangeHistoryService = game:GetService "ChangeHistoryService"
local CoreGui = game:GetService "CoreGui" local CoreGui = game:GetService "CoreGui"
local CreateStandardLabel = require "../Modules/Terrain/CreateStandardLabel"
--------------- ---------------
--PLUGIN SETUP- --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. -- Will hold a part the highlighter will be attached to. This will be moved where the mouse is.
highlighter.selectionPart = nil 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. -- Update where the highlighter is displayed.
-- position - Where to display the highlighter, in world space. -- position - Where to display the highlighter, in world space.
function UpdatePosition(position) local function UpdatePosition(position)
if not position then if not position then
return return
end end
@ -204,6 +170,42 @@ function MouseHighlighter.Create(mouseUse)
end end
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 return highlighter
end end
@ -231,30 +233,6 @@ g.Parent = CoreGui
-- UI gui load. Required for sliders. -- UI gui load. Required for sliders.
local RbxGui = LoadLibrary "RbxGui" 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. -- Create a standardized slider.
-- name - Name to use for the slider. -- name - Name to use for the slider.
-- pos - Position to display the slider at. -- pos - Position to display the slider at.
@ -639,7 +617,7 @@ mouse.Button1Up:connect(function()
end) end)
-- Run when the popup is activated. -- Run when the popup is activated.
On = function() function On()
if not c then if not c then
return return
end end
@ -651,7 +629,7 @@ On = function()
end end
-- Run when the popup is deactivated. -- Run when the popup is deactivated.
Off = function() function Off()
toolbarbutton:SetActive(false) toolbarbutton:SetActive(false)
on = false on = false

View File

@ -1,10 +1,12 @@
while game == nil do while not game do
wait() wait()
end end
local ChangeHistoryService = game:GetService "ChangeHistoryService" local ChangeHistoryService = game:GetService "ChangeHistoryService"
local CoreGui = game:GetService "CoreGui" local CoreGui = game:GetService "CoreGui"
-- local CreateStandardLabel = require "../Modules/Terrain/CreateStandardLabel"
--------------- ---------------
--PLUGIN SETUP- --PLUGIN SETUP-
--------------- ---------------
@ -133,9 +135,7 @@ end
function brush(x, y, z) function brush(x, y, z)
if depth == 0 then if depth == 0 then
return return
end elseif depth > 0 then
if depth > 0 then
local findY = findLowPoint(x, y + depth, z) local findY = findLowPoint(x, y + depth, z)
local yWithDepth = y + depth local yWithDepth = y + depth
@ -171,9 +171,7 @@ end
function updatePreviewSelection(position) function updatePreviewSelection(position)
if not position then if not position then
return return
end elseif depth == 0 then -- or not mouse.Target
--if not mouse.Target then disablePreview() return end
if depth == 0 then
disablePreview() disablePreview()
return return
end end
@ -307,21 +305,21 @@ function doFillCells(position, mouseDrag, needsCellPos)
end end
function mouseMoved() function mouseMoved()
if on then if not on then
if mousedown == true then return
if mousemoving then elseif mousedown == true then
return if mousemoving then
end return
mousemoving = true
local currMousePos = Vector2.new(mouse.X, mouse.Y)
local mouseDrag = currMousePos - lastMousePos
doFillCells(mouse.Hit, mouseDrag, true)
lastMousePos = currMousePos
mousemoving = false
end 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 end
updatePreviewSelection(mouse.Hit)
end end
-- Do a line/plane intersection. The line starts at the camera. The plane is at y == 0, normal(0, 1, 0) -- 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 local brushDragBar
On = function() function On()
if not c then if not c then
return return
end elseif this then
if this then
this:Activate(true) this:Activate(true)
end end
if toolbarbutton then if toolbarbutton then
@ -422,7 +419,7 @@ On = function()
on = true on = true
end end
Off = function() function Off()
if toolbarbutton then if toolbarbutton then
toolbarbutton:SetActive(false) toolbarbutton:SetActive(false)
end end
@ -474,30 +471,6 @@ function CreateStandardDropdown(
return dropdown, updateSelection return dropdown, updateSelection
end 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. -- Create a standardized slider.
-- name - Name to use for the slider. -- name - Name to use for the slider.
-- pos - Position to display the slider at. -- pos - Position to display the slider at.
@ -546,6 +519,7 @@ local g = Instance.new "ScreenGui"
g.Name = "TerrainBrushGui" g.Name = "TerrainBrushGui"
g.Parent = CoreGui g.Parent = CoreGui
local elevationFrame, elevationHelpFrame, elevationCloseEvent
brushDragBar, elevationFrame, elevationHelpFrame, elevationCloseEvent = brushDragBar, elevationFrame, elevationHelpFrame, elevationCloseEvent =
RbxGui.CreatePluginFrame( RbxGui.CreatePluginFrame(
"Terrain Brush", "Terrain Brush",

View File

@ -1,4 +1,5 @@
while game == nil do --!strict
while not game do
wait() wait()
end end
@ -47,9 +48,13 @@ local craterDragBar, craterFrame, craterHelpFrame, craterCloseEvent = nil
--FUNCTION DEFINITIONS- --FUNCTION DEFINITIONS-
----------------------- -----------------------
--makes a crater at point (x, y, z) in cluster c local function dist(x1, y1, x2, y2)
--cd is the depth factor, a percent of the depth of a perfect sphere return math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2))
function makeCrater(x, y, z, cr, cd) 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 = {} local heightmap = {}
for i = x - (cr + 1), x + (cr + 1) do for i = x - (cr + 1), x + (cr + 1) do
heightmap[i] = {} heightmap[i] = {}
@ -112,10 +117,6 @@ function makeCrater(x, y, z, cr, cd)
end end
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 local debounce = false
mouse.Button1Down:connect(function() mouse.Button1Down:connect(function()
@ -139,11 +140,10 @@ mouse.Button1Down:connect(function()
ChangeHistoryService:SetWaypoint "Crater" ChangeHistoryService:SetWaypoint "Crater"
end) end)
On = function() function On()
if not c then if not c then
return return
end elseif this then
if this then
this:Activate(true) this:Activate(true)
end end
if toolbarbutton then if toolbarbutton then
@ -155,7 +155,7 @@ On = function()
on = true on = true
end end
Off = function() function Off()
if toolbarbutton then if toolbarbutton then
toolbarbutton:SetActive(false) toolbarbutton:SetActive(false)
end end

View File

@ -1,4 +1,5 @@
while game == nil do --!strict
while not game do
wait() wait()
end end
@ -272,11 +273,10 @@ mouse.Button1Down:connect(function()
end end
end) end)
On = function() function On()
if not c then if not c then
return return
end elseif this then
if this then
this:Activate(true) this:Activate(true)
end end
if toolbarbutton then if toolbarbutton then
@ -290,7 +290,7 @@ On = function()
on = true on = true
end end
Off = function() function Off()
if toolbarbutton then if toolbarbutton then
toolbarbutton:SetActive(false) toolbarbutton:SetActive(false)
end end
@ -311,6 +311,7 @@ local g = Instance.new "ScreenGui"
g.Name = "RoadGui" g.Name = "RoadGui"
g.Parent = game:GetService "CoreGui" g.Parent = game:GetService "CoreGui"
local roadHelpFrame
roadDragBar, roadFrame, roadHelpFrame, roadCloseEvent = roadDragBar, roadFrame, roadHelpFrame, roadCloseEvent =
RbxGui.CreatePluginFrame( RbxGui.CreatePluginFrame(
"Roads", "Roads",

View File

@ -1,4 +1,4 @@
while game == nil do while not game do
wait() wait()
end end
@ -12,6 +12,7 @@ local loaded = false
local on = false local on = false
local On, Off local On, Off
local mouseDown, mouseUp, mouseMove
local this = PluginManager():CreatePlugin() local this = PluginManager():CreatePlugin()
local mouse = this:GetMouse() local mouse = this:GetMouse()
@ -82,29 +83,173 @@ local RbxUtil = LoadLibrary "RbxUtility"
--FUNCTION DEFINITIONS- --FUNCTION DEFINITIONS-
----------------------- -----------------------
function paintWaterfall(setCells) 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 if not setCells then
return return
end end
if #setCells <= 0 then for i = 1, #setCells do
return 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 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) paintWaterfall(setCells)
return return
end end
@ -209,197 +354,122 @@ function setWaterDirection(mouseCellPos, setCells)
) )
end end
end end
else return
for i = 1, #setCells do end
if setCells[i].xPos == initX then
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( SetWaterCell(
c, c,
setCells[i].xPos, setCells[i].xPos,
setCells[i].yPos, setCells[i].yPos,
setCells[i].zPos, setCells[i].zPos,
mediumWaterForce, mediumWaterForce,
down right
) )
elseif setCells[i].xPos == endX then elseif setCells[i].zPos > zMiddle then
SetWaterCell( SetWaterCell(
c, c,
setCells[i].xPos, setCells[i].xPos,
setCells[i].yPos, setCells[i].yPos,
setCells[i].zPos, setCells[i].zPos,
mediumWaterForce, mediumWaterForce,
up left
) )
else else
if setCells[i].zPos < zMiddle then if setCells[i].xPos < xMiddle then
SetWaterCell( SetWaterCell(
c, c,
setCells[i].xPos, setCells[i].xPos,
setCells[i].yPos, setCells[i].yPos,
setCells[i].zPos, setCells[i].zPos,
mediumWaterForce, mediumWaterForce,
right down
) )
elseif setCells[i].zPos > zMiddle then elseif setCells[i].xPos > xMiddle then
SetWaterCell( SetWaterCell(
c, c,
setCells[i].xPos, setCells[i].xPos,
setCells[i].yPos, setCells[i].yPos,
setCells[i].zPos, setCells[i].zPos,
mediumWaterForce, 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 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 return setCells
end 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 cellPos = WorldToCellPreferSolid(c, mouseR.Hit.p)
local lowVec = local lowVec =
@ -415,7 +485,7 @@ function calculateRegion(mouseR)
) )
end end
function createSelection(mouseS, massSelection) local function createSelection(mouseS, massSelection)
currSelectionUpdate, currSelectionDestroy = RbxUtil.SelectTerrainRegion( currSelectionUpdate, currSelectionDestroy = RbxUtil.SelectTerrainRegion(
calculateRegion(mouseS), calculateRegion(mouseS),
BrickColor.new "Lime green", BrickColor.new "Lime green",
@ -424,24 +494,19 @@ function createSelection(mouseS, massSelection)
) )
end end
function updateSelection(mouseS) local function updateSelection(mouseS)
if not currSelectionUpdate then if not currSelectionUpdate then
createSelection(mouseS, radius > 4) createSelection(mouseS, radius > 4)
else
currSelectionUpdate(
calculateRegion(mouseS),
BrickColor.new "Lime green"
)
end
end
function setPositionDirectionality()
if nil == lastCell then
return return
end end
currSelectionUpdate(calculateRegion(mouseS), BrickColor.new "Lime green")
end
-- no dragging occured, lets set our water to be stagnant or be a waterfall local function setPositionDirectionality()
if lastCell and not lastLastCell then 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) local cellsToSet = paint(lastCell)
if directionIsDown(nil, lastCell) then if directionIsDown(nil, lastCell) then
paintWaterfall(cellsToSet) paintWaterfall(cellsToSet)
@ -476,23 +541,14 @@ function setPositionDirectionality()
local direction local direction
if absX > absY and absX > absZ then if absX > absY and absX > absZ then
if overallDirection.X > 0 then direction = overallDirection.X > 0 and Enum.WaterDirection.X
direction = Enum.WaterDirection.X or Enum.WaterDirection.NegX
else
direction = Enum.WaterDirection.NegX
end
elseif absY > absX and absY > absZ then elseif absY > absX and absY > absZ then
if overallDirection.Y > 0 then direction = overallDirection.Y > 0 and Enum.WaterDirection.Y
direction = Enum.WaterDirection.Y or Enum.WaterDirection.NegY
else
direction = Enum.WaterDirection.NegY
end
elseif absZ > absX and absZ > absY then elseif absZ > absX and absZ > absY then
if overallDirection.Z > 0 then direction = overallDirection.Z > 0 and Enum.WaterDirection.Z
direction = Enum.WaterDirection.Z or Enum.WaterDirection.NegZ
else
direction = Enum.WaterDirection.NegZ
end
end end
if not direction then -- this should never be hit, but just in case if not direction then -- this should never be hit, but just in case
@ -512,24 +568,27 @@ function setPositionDirectionality()
end end
function mouseDown(mouseD) function mouseDown(mouseD)
if on and mouseD.Target == game.Workspace.Terrain then if not (on and mouseD.Target == game.Workspace.Terrain) then
dragging = true return
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
end 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 end
function mouseUp(_) function mouseUp(_)
@ -547,96 +606,29 @@ function mouseUp(_)
end end
function mouseMove(mouseM) function mouseMove(mouseM)
if on then if not on then
if mouseM.Target == game.Workspace.Terrain then return
if lastCell ~= WorldToCellPreferSolid(c, mouseM.Hit.p) then elseif mouseM.Target == game.Workspace.Terrain then
updateSelection(mouseM) if lastCell == WorldToCellPreferSolid(c, mouseM.Hit.p) then
local newCell = WorldToCellPreferSolid(c, mouseM.Hit.p) return
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()
end end
end updateSelection(mouseM)
end local newCell = WorldToCellPreferSolid(c, mouseM.Hit.p)
function directionIsDown(fromCell, toCell) if not dragging then
if not toCell then return
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
end -- local painting = true
paint(newCell)
local viableCells = getAffectedCells(toCell) if lastCell and newCell and (lastCell - newCell).magnitude > 1 then
if not viableCells then paintBetweenPoints(lastCell, newCell)
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
end end
lastLastCell = lastCell
lastCell = newCell
-- painting = false
else
destroySelection()
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
if xyPlaneArea > xzPlaneArea then
return true
end
if yzPlaneArea > xzPlaneArea then
return true
end
return false
end end
function destroySelection() function destroySelection()
@ -649,31 +641,33 @@ function destroySelection()
end end
end end
function moveTowardsGoal(direction, currPos, goalPos, currCell) local function moveTowardsGoal(direction: string, currPos, goalPos, currCell)
if currPos ~= goalPos then if currPos == goalPos then
if currPos < goalPos then return currCell
if direction == "X" then end
currCell = Vector3.new(currCell.X + 1, currCell.Y, currCell.Z)
elseif direction == "Y" then if currPos < goalPos then
currCell = Vector3.new(currCell.X, currCell.Y + 1, currCell.Z) if direction == "X" then
elseif direction == "Z" then currCell = Vector3.new(currCell.X + 1, currCell.Y, currCell.Z)
currCell = Vector3.new(currCell.X, currCell.Y, currCell.Z + 1) elseif direction == "Y" then
end currCell = Vector3.new(currCell.X, currCell.Y + 1, currCell.Z)
elseif currPos > goalPos then elseif direction == "Z" then
if direction == "X" then currCell = Vector3.new(currCell.X, currCell.Y, currCell.Z + 1)
currCell = Vector3.new(currCell.X - 1, currCell.Y, currCell.Z) end
elseif direction == "Y" then elseif currPos > goalPos then
currCell = Vector3.new(currCell.X, currCell.Y - 1, currCell.Z) if direction == "X" then
elseif direction == "Z" then currCell = Vector3.new(currCell.X - 1, currCell.Y, currCell.Z)
currCell = Vector3.new(currCell.X, currCell.Y, currCell.Z - 1) elseif direction == "Y" then
end 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
end end
return currCell return currCell
end end
function interpolateOneDim(direction, currPos, goalPos, currCell) local function interpolateOneDim(direction, currPos, goalPos, currCell)
if currPos ~= goalPos then if currPos ~= goalPos then
currCell = moveTowardsGoal(direction, currPos, goalPos, currCell) currCell = moveTowardsGoal(direction, currPos, goalPos, currCell)
paint(currCell) paint(currCell)
@ -696,7 +690,7 @@ function paintBetweenPoints(lastCellP, newCell)
end end
end end
On = function() function On()
if not c then if not c then
return return
end end
@ -707,7 +701,7 @@ On = function()
on = true on = true
end end
Off = function() function Off()
toolbarbutton:SetActive(false) toolbarbutton:SetActive(false)
destroySelection() destroySelection()
@ -724,6 +718,7 @@ local g = Instance.new "ScreenGui"
g.Name = "MaterialPainterGui" g.Name = "MaterialPainterGui"
g.Parent = CoreGui g.Parent = CoreGui
local containerFrame
dragBar, containerFrame, helpFrame, closeEvent = RbxGui.CreatePluginFrame( dragBar, containerFrame, helpFrame, closeEvent = RbxGui.CreatePluginFrame(
"Material Brush", "Material Brush",
UDim2.new(0, 163, 0, 285), UDim2.new(0, 163, 0, 285),

View File

@ -1,9 +1,12 @@
while game == nil do while not game do
wait() wait()
end end
local ContentProvider = game:GetService "ContentProvider" local ContentProvider = game:GetService "ContentProvider"
local CoreGui = game:GetService "CoreGui" local CoreGui = game:GetService "CoreGui"
local News = require "../Modules/New"
local New = News.New
local Hydrate = News.Hydrate
--------------- ---------------
--PLUGIN SETUP- --PLUGIN SETUP-
@ -113,11 +116,10 @@ function hideLoadingDialog()
currStampGui.LoadingFrame.Visible = false currStampGui.LoadingFrame.Visible = false
end end
local partSelected = function(name, id, terrainShape) local stampCon
if not id then
return local function partSelected(name, id, terrainShape)
end if not (id and name) then
if not name then
return return
end end
@ -146,21 +148,23 @@ local partSelected = function(name, id, terrainShape)
and clusterTag:isA "Vector3Value" and clusterTag:isA "Vector3Value"
and clusterTag.Value.X == 17 and clusterTag.Value.X == 17
then then
local waterForceTag = Instance.new("StringValue", lastStampModel) New "StringValue" {
waterForceTag.Name = "WaterForceTag" Name = "WaterForceTag",
waterForceTag.Value = waterForceAndDirection[1] Value = waterForceAndDirection[1],
Parent = lastStampModel,
local waterForceDirectionTag = }
Instance.new("StringValue", lastStampModel) New "StringValue" {
waterForceDirectionTag.Name = "WaterForceDirectionTag" Name = "WaterForceDirectionTag",
waterForceDirectionTag.Value = waterForceAndDirection[2] Value = waterForceAndDirection[2],
Parent = lastStampModel,
}
end end
end end
setupStamper(lastStampModel, mouse) setupStamper(lastStampModel)
end end
function updateWaterInfo() local function updateWaterInfo()
if stampControl then if stampControl then
stampControl.Destroy() stampControl.Destroy()
end end
@ -177,70 +181,203 @@ function updateWaterInfo()
local clusterTag = lastStampModel:FindFirstChild "ClusterMaterial" local clusterTag = lastStampModel:FindFirstChild "ClusterMaterial"
-- we are going to stamp water, send info to stamper about this -- we are going to stamp water, send info to stamper about this
if clusterTag and clusterTag.Value.X == 17 then if clusterTag and clusterTag.Value.X == 17 then
local waterForceTag = Instance.new("StringValue", lastStampModel) New "StringValue" {
waterForceTag.Name = "WaterForceTag" Name = "WaterForceTag",
waterForceTag.Value = waterForceAndDirection[1] Value = waterForceAndDirection[1],
Parent = lastStampModel,
local waterForceDirectionTag = }
Instance.new("StringValue", lastStampModel) New "StringValue" {
waterForceDirectionTag.Name = "WaterForceDirectionTag" Name = "WaterForceDirectionTag",
waterForceDirectionTag.Value = waterForceAndDirection[2] Value = waterForceAndDirection[2],
Parent = lastStampModel,
}
end end
end end
setupStamper(lastStampModel, mouse) setupStamper(lastStampModel)
end end
local dialogClosed = function() local function dialogClosed()
if lastStampModel then if not lastStampModel then
if stampControl then return
stampControl.Destroy() elseif stampControl then
end stampControl.Destroy()
setupStamper(lastStampModel, mouse)
end end
setupStamper(lastStampModel)
end end
function pickPart() local function pickPart()
if stampControl then if stampControl then
stampControl.Destroy() stampControl.Destroy()
end end
setPanelVisibility(true) setPanelVisibility(true)
end end
function keyHandler(key) local function keyHandler(key)
if key == "f" then if key ~= "f" then
handlePartShow() return
end elseif getPanelVisibility() then
end -- handlePartShow
setPanelVisibility(false)
function partOn() if lastStampModel then
pickPart() if stampControl then
end stampControl.Destroy()
end
function partOff() setupStamper(lastStampModel)
setPanelVisibility(false)
if lastStampModel then
if stampControl then
stampControl.Destroy()
end end
setupStamper(lastStampModel, mouse)
end
end
function handlePartShow()
if getPanelVisibility() then
partOff()
else else
partOn() pickPart()
end end
end end
On = function() function setupStamper(model)
if not game.Workspace.Terrain then 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 return
end 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) this:Activate(true)
mouse = this:GetMouse() mouse = this:GetMouse()
end end
@ -268,7 +405,7 @@ On = function()
on = true on = true
end end
Off = function() function Off()
if toolbarbutton then if toolbarbutton then
toolbarbutton:SetActive(false) toolbarbutton:SetActive(false)
end end
@ -301,144 +438,6 @@ Off = function()
on = false on = false
end 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- --SUCCESSFUL LOAD MESSAGE-
-------------------------- --------------------------

View File

@ -1,4 +1,4 @@
while game == nil do while not game do
wait() wait()
end end
@ -6,6 +6,12 @@ local ChangeHistoryService = game:GetService "ChangeHistoryService"
local ContentProvider = game:GetService "ContentProvider" local ContentProvider = game:GetService "ContentProvider"
local CoreGui = game:GetService "CoreGui" 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- --PLUGIN SETUP-
--------------- ---------------
@ -13,6 +19,7 @@ local loaded = false
local on = false local on = false
local On, Off local On, Off
local mouseDown, mouseUp
local this = PluginManager():CreatePlugin() local this = PluginManager():CreatePlugin()
local mouse = this:GetMouse() local mouse = this:GetMouse()
@ -139,9 +146,6 @@ function MouseHighlighter.Create(mouseUse)
highlighter.selectionPart = nil highlighter.selectionPart = nil
-- Hook the mouse up to check for movement. -- Hook the mouse up to check for movement.
mouseH.Move:connect(function()
MouseMoved()
end)
mouseH.Button1Down:connect(function() mouseH.Button1Down:connect(function()
highlighter.mouseDown = true highlighter.mouseDown = true
@ -239,9 +243,7 @@ function MouseHighlighter.Create(mouseUse)
local function UpdatePosition(position) local function UpdatePosition(position)
if not position then if not position then
return return
end elseif not mouseH.Target then
if not mouseH.Target then
stopTween() stopTween()
highlighter.selectionPart.Parent = nil highlighter.selectionPart.Parent = nil
return return
@ -297,12 +299,12 @@ function MouseHighlighter.Create(mouseUse)
end end
end end
-- Function to call when the mouse has moved. Updates where to display the highlighter. -- Function to call when the mouse has moved. Updates where to display the highlighter.
function MouseMoved() mouseH.Move:connect(function()
if on and not processing then if on and not processing then
UpdatePosition(mouseH.Hit) UpdatePosition(mouseH.Hit)
end end
end end)
return highlighter return highlighter
end end
@ -338,68 +340,6 @@ mouseHighlighter:DisablePreview()
local ConfirmationPopup = {} local ConfirmationPopup = {}
ConfirmationPopup.__index = 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. -- Create a confirmation popup.
-- --
-- confirmText - What to display in the popup. -- confirmText - What to display in the popup.
@ -417,46 +357,54 @@ function ConfirmationPopup.Create(
confirmFunction, confirmFunction,
declineFunction declineFunction
) )
local popup = {} local popup = {
popup.confirmButton = nil -- Hold the button to confirm a choice. confirmButton = nil, -- Hold the button to confirm a choice.
popup.declineButton = nil -- Hold the button to decline a choice. declineButton = nil, -- Hold the button to decline a choice.
popup.confirmationFrame = nil -- Hold the conformation frame. confirmationFrame = nil, -- Hold the conformation frame.
popup.confirmationText = nil -- Hold the text label to display the conformation message. confirmationText = nil, -- Hold the text label to display the conformation message.
popup.confirmationHelpText = nil -- Hold the text label to display the conformation message help. confirmationHelpText = nil, -- Hold the text label to display the conformation message help.
}
popup.confirmationFrame = Instance.new "Frame" popup.confirmationFrame = New "Frame" {
popup.confirmationFrame.Name = "ConfirmationFrame" Name = "ConfirmationFrame",
popup.confirmationFrame.Size = UDim2.new(0, 280, 0, 160) Size = UDim2.new(0, 280, 0, 160),
popup.confirmationFrame.Position = UDim2.new( Position = UDim2.new(
0.5, 0.5,
-popup.confirmationFrame.Size.X.Offset / 2, -popup.confirmationFrame.Size.X.Offset / 2,
0.5, 0.5,
-popup.confirmationFrame.Size.Y.Offset / 2 -popup.confirmationFrame.Size.Y.Offset / 2
) ),
popup.confirmationFrame.Style = Enum.FrameStyle.RobloxRound Style = Enum.FrameStyle.RobloxRound,
popup.confirmationFrame.Parent = screenGui Parent = screenGui,
}
popup.confirmLabel = CreateStandardLabel( popup.confirmLabel = Hydrate(
"ConfirmLabel", CreateStandardLabel(
UDim2.new(0, 0, 0, 15), "ConfirmLabel",
UDim2.new(1, 0, 0, 24), UDim2.new(0, 0, 0, 15),
confirmText, UDim2.new(1, 0, 0, 24),
popup.confirmationFrame confirmText,
) popup.confirmationFrame
popup.confirmLabel.FontSize = Enum.FontSize.Size18 )
popup.confirmLabel.TextXAlignment = Enum.TextXAlignment.Center ) {
FontSize = Enum.FontSize.Size18,
TextXAlignment = Enum.TextXAlignment.Center,
}
popup.confirmationHelpText = CreateStandardLabel( popup.confirmationHelpText = Hydrate(
"ConfirmSmallLabel", CreateStandardLabel(
UDim2.new(0, 0, 0, 40), "ConfirmSmallLabel",
UDim2.new(1, 0, 0, 28), UDim2.new(0, 0, 0, 40),
confirmSmallText, UDim2.new(1, 0, 0, 28),
popup.confirmationFrame confirmSmallText,
) popup.confirmationFrame
popup.confirmationHelpText.FontSize = Enum.FontSize.Size14 )
popup.confirmationHelpText.TextWrap = true ) {
popup.confirmationHelpText.Font = Enum.Font.Arial FontSize = Enum.FontSize.Size14,
popup.confirmationHelpText.TextXAlignment = Enum.TextXAlignment.Center TextWrap = true,
Font = Enum.Font.Arial,
TextXAlignment = Enum.TextXAlignment.Center,
}
-- Confirm -- Confirm
popup.confirmButton = CreateStandardButton( popup.confirmButton = CreateStandardButton(
@ -509,68 +457,11 @@ end
--FUNCTION DEFINITIONS- --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 -- function startLoadingFrame() end
-- Load the progress bar to display when drawing a river. -- Load the progress bar to display when drawing a river.
-- text - Text to display. -- text - Text to display.
function LoadProgressBar(text) local function LoadProgressBar(text)
processing = true processing = true
-- Start the progress bar. -- Start the progress bar.
@ -597,31 +488,33 @@ function LoadProgressBar(text)
-- spin = false -- spin = false
end) end)
local spinnerFrame = Instance.new "Frame" local spinnerFrame = New "Frame" {
spinnerFrame.Name = "Spinner" Name = "Spinner",
spinnerFrame.Size = UDim2.new(0, 80, 0, 80) Size = UDim2.new(0, 80, 0, 80),
spinnerFrame.Position = UDim2.new(0.5, -40, 0.5, -55) Position = UDim2.new(0.5, -40, 0.5, -55),
spinnerFrame.BackgroundTransparency = 1 BackgroundTransparency = 1,
spinnerFrame.Parent = progressBar Parent = progressBar,
}
local spinnerIcons = {} local spinnerIcons = {}
local spinnerNum = 1 local spinnerNum = 1
while spinnerNum <= 8 do while spinnerNum <= 8 do
local spinnerImage = Instance.new "ImageLabel" local spinnerImage = New "ImageLabel" {
spinnerImage.Name = "Spinner" .. spinnerNum Name = "Spinner" .. spinnerNum,
spinnerImage.Size = UDim2.new(0, 16, 0, 16) Size = UDim2.new(0, 16, 0, 16),
spinnerImage.Position = UDim2.new( Position = UDim2.new(
0.5 + 0.3 * math.cos(math.rad(45 * spinnerNum)), 0.5 + 0.3 * math.cos(math.rad(45 * spinnerNum)),
-8, -8,
0.5 + 0.3 * math.sin(math.rad(45 * spinnerNum)), 0.5 + 0.3 * math.sin(math.rad(45 * spinnerNum)),
-8 -8
) ),
spinnerImage.BackgroundTransparency = 1 BackgroundTransparency = 1,
spinnerImage.Image = "http://banland.xyz/asset?id=45880710" Image = "http://banland.xyz/asset?id=45880710",
spinnerImage.Parent = spinnerFrame Parent = spinnerFrame,
}
spinnerIcons[spinnerNum] = spinnerImage spinnerIcons[spinnerNum] = spinnerImage
spinnerNum = spinnerNum + 1 spinnerNum += 1
end end
--Make it spin --Make it spin
@ -648,7 +541,7 @@ function LoadProgressBar(text)
end end
-- Unload the progress bar. -- Unload the progress bar.
function UnloadProgressBar() local function UnloadProgressBar()
processing = false processing = false
if progressBar then if progressBar then
@ -663,36 +556,68 @@ function UnloadProgressBar()
end end
end end
function breadthFill(x, y, z) local function floodFill(x, y, z)
local yDepthChecks = doBreadthFill(x, y, z) LoadProgressBar "Processing"
while yDepthChecks and #yDepthChecks > 0 do breadthFill(x, y, z)
local newYChecks = {} UnloadProgressBar()
for i = 1, #yDepthChecks do ChangeHistoryService:SetWaypoint "FloodFill"
local currYDepthChecks = doBreadthFill( end
yDepthChecks[i].xPos,
yDepthChecks[i].yPos,
yDepthChecks[i].zPos
)
if not processing then -- Function used when we try and flood fill. Prompts the user first.
return -- Will not show if disabled or terrain is being processed.
end 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 processing = true
for j = 1, #currYDepthChecks do if ConfirmationPopupObject then
table.insert(newYChecks, { return
xPos = currYDepthChecks[j].xPos, end
yPos = currYDepthChecks[j].yPos,
zPos = currYDepthChecks[j].zPos, ConfirmationPopupObject = ConfirmationPopup.Create(
}) "Flood Fill At Selected Location?",
end "This operation may take some time.",
end "Fill",
"Cancel",
function()
ConfirmationPopupObject:Clear()
ConfirmationPopupObject = nil
floodFill(x, y, z)
ConfirmationPopupObject = nil
end,
function()
ConfirmationPopupObject:Clear()
ConfirmationPopupObject = nil
processing = false
end end
yDepthChecks = newYChecks )
end
function mouseDown(mouseD)
if on and mouseD.Target == game.Workspace.Terrain then
startCell = mouseHighlighter:GetPosition()
end end
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 if getMaterial(x, y, z) ~= emptyMaterial then
return return
end end
@ -735,7 +660,36 @@ function doBreadthFill(x, y, z)
return yDepthChecks return yDepthChecks
end 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 if x < c.MaxExtents.Min.X or x >= c.MaxExtents.Max.X then
return false return false
end end
@ -811,8 +765,7 @@ end
On = function() On = function()
if not c then if not c then
return return
end elseif this then
if this then
this:Activate(true) this:Activate(true)
end end
if toolbarbutton then if toolbarbutton then
@ -858,25 +811,27 @@ dragBar, containerFrame, helpFrame, closeEvent = RbxGui.CreatePluginFrame(
) )
dragBar.Visible = false dragBar.Visible = false
helpFrame.Size = UDim2.new(0, 200, 0, 190) Hydrate(helpFrame) {
Size = UDim2.new(0, 200, 0, 190),
local textHelp = Instance.new "TextLabel" -- "This will be pdfs in 2024" - Heliodex
textHelp.Name = "TextHelp" New "TextLabel" {
textHelp.Font = Enum.Font.ArialBold Name = "TextHelp",
textHelp.FontSize = Enum.FontSize.Size12 Font = Enum.Font.ArialBold,
textHelp.TextColor3 = Color3.new(1, 1, 1) FontSize = Enum.FontSize.Size12,
textHelp.Size = UDim2.new(1, -6, 1, -6) TextColor3 = Color3.new(1, 1, 1),
textHelp.Position = UDim2.new(0, 3, 0, 3) Size = UDim2.new(1, -6, 1, -6),
textHelp.TextXAlignment = Enum.TextXAlignment.Left Position = UDim2.new(0, 3, 0, 3),
textHelp.TextYAlignment = Enum.TextYAlignment.Top TextXAlignment = Enum.TextXAlignment.Left,
textHelp.BackgroundTransparency = 1 TextYAlignment = Enum.TextYAlignment.Top,
textHelp.TextWrap = true BackgroundTransparency = 1,
textHelp.Text = [[ 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). 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. 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() closeEvent.Event:connect(function()
Off() Off()