Compile terrain plugins from Luau

This commit is contained in:
Lewin Kelly 2024-03-22 02:40:39 +00:00
parent 6f48e7f584
commit 759056c0da
11 changed files with 6277 additions and 6 deletions

View File

@ -18,11 +18,15 @@ local libraryCores = {
"Red/init.luau",
}
local otherCores = {}
local plugins = {}
-- Allow for running from the base or corescripts directories
local cwd = process.cwd
local fromCoresDir = not not string.match(cwd, "corescripts/$")
local normalCoresDir = if fromCoresDir then "./luau" else "./corescripts/luau"
local normalPluginsDir = if fromCoresDir
then "./terrain plugins"
else "./corescripts/terrain plugins"
for _, core in ipairs(fs.readDir(normalCoresDir)) do
table.insert(
@ -31,6 +35,15 @@ for _, core in ipairs(fs.readDir(normalCoresDir)) do
core
)
end
for _, core in ipairs(fs.readDir(normalPluginsDir)) do
print(core)
table.insert(plugins, core)
end
local coresDir = if fromCoresDir then "." else "./corescripts"
local pluginsDir = if fromCoresDir
then "../Client deployer"
else "./Client deployer"
local function processCores(
scripts: { string },
@ -43,20 +56,20 @@ local function processCores(
then `../{config}.json5`
else `{config}.json5`
local coresDir = if fromCoresDir then "." else "./corescripts"
for i, core in ipairs(scripts) do
local newCore = if libraries
then `{10000000 + i}.lua`
else string.gsub(core, "%.luau$", ".lua")
print(`{coresDir}/{startDir}/{core}`, `{endDir}/{newCore}`)
task.spawn(function()
local cmd = process.spawn("darklua", {
"process",
"-c",
configFile,
`{coresDir}/{startDir}/{core}`,
`{coresDir}/{endDir}/{newCore}`,
`{endDir}/{newCore}`,
})
print(
@ -69,13 +82,28 @@ local function processCores(
end
task.spawn(function()
processCores(libraryCores, "Libraries", "processed", "dense", true)
processCores(
libraryCores,
"Libraries",
`{coresDir}/processed`,
"dense",
true
)
end)
task.spawn(function()
processCores(normalCores, "luau", "processed", "dense")
processCores(
plugins,
"terrain plugins",
`{pluginsDir}/staging/BuiltInPlugins/terrain`,
"lines"
)
end)
task.spawn(function()
processCores(otherCores, "luau", "processed", "lines")
processCores(normalCores, "luau", `{coresDir}/processed`, "dense")
end)
task.spawn(function()
processCores(otherCores, "luau", `{coresDir}/processed`, "lines")
end)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,395 @@
while game == nil do
wait(1 / 30)
end
---------------
--PLUGIN SETUP-
---------------
local loaded = false
-- True if the plugin is on, false if not.
local on = false
local On, Off
local plugin = PluginManager():CreatePlugin()
local mouse = plugin:GetMouse()
mouse.Button1Down:connect(function()
onClicked(mouse)
end)
local toolbar = plugin:CreateToolbar "Terrain"
local toolbarbutton = toolbar:CreateButton("Builder", "Builder", "builder.png")
toolbarbutton.Click:connect(function()
if on then
Off()
elseif loaded then
On()
end
end)
game:WaitForChild "Workspace"
game.Workspace:WaitForChild "Terrain"
local c = game.Workspace.Terrain
local SetCell = c.SetCell
local GetCell = c.GetCell
local WorldToCellPreferSolid = c.WorldToCellPreferSolid
local CellCenterToWorld = c.CellCenterToWorld
local AutoWedge = c.AutowedgeCells
local WorldToCellPreferEmpty = c.WorldToCellPreferEmpty
local GetWaterCell = c.GetWaterCell
local SetWaterCell = c.SetWaterCell
-----------------
--DEFAULT VALUES-
-----------------
-- Stores selection properties.
local selectionProps = {}
selectionProps.isWater = nil -- True if what will be built is water.
selectionProps.waterForce = nil -- Water force.
selectionProps.waterDirection = nil -- Water direction.
selectionProps.terrainMaterial = 0 -- Terrain material to use
-- What color to use for the mouse highlighter.
local mouseHighlightColor = BrickColor.new "Lime green"
-- Used to create a highlighter that follows the mouse.
-- It is a class mouse highlighter. To use, call MouseHighlighter.Create(mouse) where mouse is the mouse to track.
local MouseHighlighter = {}
MouseHighlighter.__index = MouseHighlighter
local PlaneIntersection, UpdatePosition
-- Create a mouse movement highlighter.
-- plugin - Plugin to get the mouse from.
function MouseHighlighter.Create(mouseUse)
local highlighter = {}
local mouseH = mouseUse
highlighter.OnClicked = nil
highlighter.mouseDown = false
-- Store the last point used to draw.
highlighter.lastUsedPoint = nil
-- 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)
-- Do a line/plane intersection. The line starts at the camera. The plane is at y == 0, normal(0, 1, 0)
--
-- vectorPos - End point of the line.
--
-- 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)
local currCamera = game.Workspace.CurrentCamera
local startPos = Vector3.new(
currCamera.CoordinateFrame.p.X,
currCamera.CoordinateFrame.p.Y,
currCamera.CoordinateFrame.p.Z
)
local endPos = Vector3.new(vectorPos.X, vectorPos.Y, vectorPos.Z)
local normal = Vector3.new(0, 1, 0)
local p3 = Vector3.new(0, 0, 0)
local startEndDot = normal:Dot(endPos - startPos)
local cellPos = vectorPos
local success = false
if startEndDot ~= 0 then
local t = normal:Dot(p3 - startPos) / startEndDot
if t >= 0 and t <= 1 then
local intersection = ((endPos - startPos) * t) + startPos
cellPos = c:WorldToCell(intersection)
success = true
end
end
return success, cellPos
end
-- Update where the highlighter is displayed.
-- position - Where to display the highlighter, in world space.
UpdatePosition = function(position)
if not position then
return
end
-- NOTE:
-- Change this gui to be the one you want to use.
highlighter.selectionBox.Parent = game:GetService "CoreGui"
local vectorPos = Vector3.new(position.x, position.y, position.z)
local cellPos = WorldToCellPreferEmpty(c, vectorPos)
local solidCell = WorldToCellPreferSolid(c, vectorPos)
local success = false
-- If nothing was hit, do the plane intersection.
if 0 == GetCell(c, solidCell.X, solidCell.Y, solidCell.Z).Value then
success, cellPos = PlaneIntersection(vectorPos)
if not success then
cellPos = solidCell
end
else
highlighter.lastUsedPoint = cellPos
end
local regionToSelect
local lowVec = CellCenterToWorld(c, cellPos.x, cellPos.y - 1, cellPos.z)
local highVec =
CellCenterToWorld(c, cellPos.x, cellPos.y + 1, cellPos.z)
regionToSelect = Region3.new(lowVec, highVec)
highlighter.selectionPart.Size = regionToSelect.Size
- Vector3.new(-4, 4, -4)
highlighter.selectionPart.CFrame = regionToSelect.CFrame
if nil ~= highlighter.OnClicked and highlighter.mouseDown then
if nil == highlighter.lastUsedPoint then
highlighter.lastUsedPoint = WorldToCellPreferEmpty(
c,
Vector3.new(mouseH.Hit.x, mouseH.Hit.y, mouseH.Hit.z)
)
else
cellPos = WorldToCellPreferEmpty(
c,
Vector3.new(mouseH.Hit.x, mouseH.Hit.y, mouseH.Hit.z)
)
end
end
end
return highlighter
end
-- Hide the highlighter.
function MouseHighlighter:DisablePreview()
self.selectionBox.Parent = nil
end
-- Show the highlighter.
function MouseHighlighter:EnablePreview()
self.selectionBox.Parent = game:GetService "CoreGui" -- This will make it not show up in workspace.
end
-- Create the mouse movement highlighter.
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-
------
--screengui
local g = Instance.new "ScreenGui"
g.Name = "BuilderGui"
g.Parent = game:GetService "CoreGui"
-- UI gui load. Required for sliders.
local RbxGui = LoadLibrary "RbxGui"
-- Store properties here.
local properties = { autoWedgeEnabled = false }
-- Gui frame for the plugin.
local builderPropertiesDragBar, builderFrame, builderHelpFrame, builderCloseEvent =
RbxGui.CreatePluginFrame(
"Builder",
UDim2.new(0, 123, 0, 40),
UDim2.new(0, 0, 0, 0),
false,
g
)
builderPropertiesDragBar.Visible = false
builderCloseEvent.Event:connect(function()
Off()
end)
builderHelpFrame.Size = UDim2.new(0, 160, 0, 85)
local builderHelpText = Instance.new "TextLabel"
builderHelpText.Name = "HelpText"
builderHelpText.Font = Enum.Font.ArialBold
builderHelpText.FontSize = Enum.FontSize.Size12
builderHelpText.TextColor3 = Color3.new(227 / 255, 227 / 255, 227 / 255)
builderHelpText.TextXAlignment = Enum.TextXAlignment.Left
builderHelpText.TextYAlignment = Enum.TextYAlignment.Top
builderHelpText.Position = UDim2.new(0, 4, 0, 4)
builderHelpText.Size = UDim2.new(1, -8, 0, 177)
builderHelpText.BackgroundTransparency = 1
builderHelpText.TextWrap = true
builderHelpText.Text = [[
Clicking terrain adds a single block into the selection box shown. The terrain material and type will be the same as the cell that was clicked on.]]
builderHelpText.Parent = builderHelpFrame
CreateStandardLabel(
"AddText",
UDim2.new(0, 8, 0, 10),
UDim2.new(0, 67, 0, 14),
"Click to add terrain.",
builderFrame
)
-- Function to connect to the mouse button 1 down event. This is what will run when the user clicks.
-- Adding and autowedging done here.
-- mouse - Mouse data.
function onClicked(mouseC)
if on then
local cellPos = WorldToCellPreferEmpty(
c,
Vector3.new(mouseC.Hit.x, mouseC.Hit.y, mouseC.Hit.z)
)
local x = cellPos.x
local y = cellPos.y
local z = cellPos.z
local solidCellPos = WorldToCellPreferSolid(
c,
Vector3.new(mouseC.Hit.x, mouseC.Hit.y, mouseC.Hit.z)
)
local celMat =
GetCell(c, solidCellPos.x, solidCellPos.y, solidCellPos.z)
local success = false
if celMat.Value > 0 then
selectionProps.terrainMaterial = celMat.Value
selectionProps.isWater, selectionProps.waterForce, selectionProps.waterDirection =
GetWaterCell(c, solidCellPos.X, solidCellPos.Y, solidCellPos.Z)
else
if 0 == selectionProps.terrainMaterial then
-- It was nothing, give it a default type and the plane intersection.
selectionProps.isWater = false
selectionProps.terrainMaterial = 1
end
success, cellPos = PlaneIntersection(
Vector3.new(mouseC.Hit.x, mouseC.Hit.y, mouseC.Hit.z)
)
if not success then
cellPos = solidCellPos
end
x = cellPos.x
y = cellPos.y
z = cellPos.z
end
if selectionProps.isWater and 17 == selectionProps.terrainMaterial then
SetWaterCell(
c,
x,
y,
z,
selectionProps.waterForce,
selectionProps.waterDirection
)
else
SetCell(c, x, y, z, selectionProps.terrainMaterial, 0, 0)
end
if properties.autoWedgeEnabled then
AutoWedge(
c,
Region3int16.new(
Vector3int16.new(x - 1, y - 1, z - 1),
Vector3int16.new(x + 1, y + 1, z + 1)
)
)
end
-- Mark undo point.
game:GetService("ChangeHistoryService"):SetWaypoint "Builder"
UpdatePosition(mouseC.Hit)
end
end
mouseHighlighter.OnClicked = onClicked
-- Run when the popup is activated.
On = function()
if not c then
return
end
plugin:Activate(true)
toolbarbutton:SetActive(true)
builderPropertiesDragBar.Visible = true
on = true
end
-- Run when the popup is deactivated.
Off = function()
toolbarbutton:SetActive(false)
on = false
-- Hide the popup gui.
builderPropertiesDragBar.Visible = false
mouseHighlighter:DisablePreview()
end
plugin.Deactivation:connect(function()
Off()
end)
loaded = true

View File

@ -0,0 +1,320 @@
while game == nil do
wait(1 / 30)
end
-----------------
--DEFAULT VALUES-
-----------------
local loaded = false
-- True if the plugin is on, false if not.
local on = false
local On, Off
---------------
--PLUGIN SETUP-
---------------
local plugin = PluginManager():CreatePlugin()
local mouse = plugin:GetMouse()
mouse.Button1Down:connect(function()
onClicked(mouse)
end)
local toolbar = plugin:CreateToolbar "Terrain"
local toolbarbutton =
toolbar:CreateButton("Remover", "Remover", "destroyer.png")
toolbarbutton.Click:connect(function()
if on then
Off()
elseif loaded then
On()
end
end)
game:WaitForChild "Workspace"
game.Workspace:WaitForChild "Terrain"
local c = game.Workspace.Terrain
local SetCell = c.SetCell
local GetCell = c.GetCell
local WorldToCellPreferSolid = c.WorldToCellPreferSolid
local CellCenterToWorld = c.CellCenterToWorld
local AutoWedge = c.AutowedgeCells
-- What color to use for the mouse highlighter.
local mouseHighlightColor = BrickColor.new "Really red"
-- Used to create a highlighter that follows the mouse.
-- It is a class mouse highlighter. To use, call MouseHighlighter.Create(mouse) where mouse is the mouse to track.
local MouseHighlighter = {}
MouseHighlighter.__index = MouseHighlighter
-- Create a mouse movement highlighter.
-- plugin - Plugin to get the mouse from.
function MouseHighlighter.Create(mouseUse)
local highlighter = {}
local mouse2 = mouseUse
highlighter.OnClicked = nil
highlighter.mouseDown = false
-- Store the last point used to draw.
highlighter.lastUsedPoint = nil
-- 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(mouse2.Hit)
end
end
-- Hook the mouse up to check for movement.
mouse2.Move:connect(function()
MouseMoved()
end)
mouse2.Button1Down:connect(function()
highlighter.mouseDown = true
end)
mouse2.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
mouse2.TargetFilter = highlighter.selectionPart
setmetatable(highlighter, MouseHighlighter)
-- Update where the highlighter is displayed.
-- position - Where to display the highlighter, in world space.
function UpdatePosition(position)
if not position then
return
end
if not mouse2.Target then
highlighter.selectionBox.Parent = nil
return
end
-- NOTE:
-- Change this gui to be the one you want to use.
highlighter.selectionBox.Parent = game:GetService "CoreGui"
local vectorPos = Vector3.new(position.x, position.y, position.z)
local cellPos = WorldToCellPreferSolid(c, vectorPos)
local regionToSelect
local lowVec = CellCenterToWorld(c, cellPos.x, cellPos.y - 1, cellPos.z)
local highVec =
CellCenterToWorld(c, cellPos.x, cellPos.y + 1, cellPos.z)
regionToSelect = Region3.new(lowVec, highVec)
highlighter.selectionPart.Size = regionToSelect.Size
- Vector3.new(-4, 4, -4)
highlighter.selectionPart.CFrame = regionToSelect.CFrame
if nil ~= highlighter.OnClicked and highlighter.mouseDown then
if nil == highlighter.lastUsedPoint then
highlighter.lastUsedPoint = WorldToCellPreferSolid(
c,
Vector3.new(mouse2.Hit.x, mouse2.Hit.y, mouse2.Hit.z)
)
else
cellPos = WorldToCellPreferSolid(
c,
Vector3.new(mouse2.Hit.x, mouse2.Hit.y, mouse2.Hit.z)
)
holdChange = cellPos - highlighter.lastUsedPoint
-- Require terrain.
if 0 == GetCell(c, cellPos.X, cellPos.Y, cellPos.Z).Value then
return
else
highlighter.lastUsedPoint = cellPos
end
end
end
end
return highlighter
end
-- Hide the highlighter.
function MouseHighlighter:DisablePreview()
self.selectionBox.Parent = nil
end
-- Show the highlighter.
function MouseHighlighter:EnablePreview()
self.selectionBox.Parent = game:GetService "CoreGui" -- This will make it not show up in workspace.
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-
------
--screengui
local g = Instance.new "ScreenGui"
g.Name = "RemoverGui"
g.Parent = game:GetService "CoreGui"
-- UI gui load. Required for sliders.
local RbxGui = LoadLibrary "RbxGui"
-- Store properties here.
local properties = { autoWedgeEnabled = false }
-- Gui frame for the plugin.
local removerPropertiesDragBar, removerFrame, removerHelpFrame, removerCloseEvent =
RbxGui.CreatePluginFrame(
"Remover",
UDim2.new(0, 143, 0, 40),
UDim2.new(0, 0, 0, 0),
false,
g
)
removerPropertiesDragBar.Visible = false
removerCloseEvent.Event:connect(function()
Off()
end)
removerHelpFrame.Size = UDim2.new(0, 160, 0, 60)
local removerHelpText = Instance.new "TextLabel"
removerHelpText.Name = "HelpText"
removerHelpText.Font = Enum.Font.ArialBold
removerHelpText.FontSize = Enum.FontSize.Size12
removerHelpText.TextColor3 = Color3.new(227 / 255, 227 / 255, 227 / 255)
removerHelpText.TextXAlignment = Enum.TextXAlignment.Left
removerHelpText.TextYAlignment = Enum.TextYAlignment.Top
removerHelpText.Position = UDim2.new(0, 4, 0, 4)
removerHelpText.Size = UDim2.new(1, -8, 0, 177)
removerHelpText.BackgroundTransparency = 1
removerHelpText.TextWrap = true
removerHelpText.Text = [[
Clicking terrain removes a single block at the location clicked (shown with red highlight).]]
removerHelpText.Parent = removerHelpFrame
CreateStandardLabel(
"removeText",
UDim2.new(0, 8, 0, 10),
UDim2.new(0, 67, 0, 14),
"Click to remove terrain",
removerFrame
)
-- Function to connect to the mouse button 1 down event. This is what will run when the user clicks.
-- mouse - Mouse data.
function onClicked(mouse2)
if on then
local cellPos = WorldToCellPreferSolid(
c,
Vector3.new(mouse2.Hit.x, mouse2.Hit.y, mouse2.Hit.z)
)
local x = cellPos.x
local y = cellPos.y
local z = cellPos.z
SetCell(c, x, y, z, 0, 0, 0)
-- Mark undo point.
game:GetService("ChangeHistoryService"):SetWaypoint "Remover"
UpdatePosition(mouse2.Hit)
if properties.autoWedgeEnabled then
AutoWedge(
c,
Region3int16.new(
Vector3int16.new(x - 1, y - 1, z - 1),
Vector3int16.new(x + 1, y + 1, z + 1)
)
)
end
end
end
mouseHighlighter.OnClicked = onClicked
-- Run when the popup is activated.
On = function()
if not c then
return
end
if plugin then
plugin:Activate(true)
end
if toolbarbutton then
toolbarbutton:SetActive(true)
end
if removerPropertiesDragBar then
removerPropertiesDragBar.Visible = true
end
on = true
end
-- Run when the popup is deactivated.
Off = function()
on = false
if toolbarbutton then
toolbarbutton:SetActive(false)
end
-- Hide the popup gui.
if removerPropertiesDragBar then
removerPropertiesDragBar.Visible = false
end
if mouseHighlighter then
mouseHighlighter:DisablePreview()
end
end
plugin.Deactivation:connect(function()
Off()
end)
loaded = true

View File

@ -0,0 +1,658 @@
while game == nil do
wait(1 / 30)
end
---------------
--PLUGIN SETUP-
---------------
local loaded = false
-- True if the plugin is on, false if not.
local on = false
local On, Off
local plugin = PluginManager():CreatePlugin()
local mouse = plugin:GetMouse()
mouse.Button1Down:connect(function()
onClicked(mouse)
end)
local toolbar = plugin:CreateToolbar "Terrain"
local toolbarbutton = toolbar:CreateButton(
"Elevation Adjuster",
"Elevation Adjuster",
"elevation.png"
)
toolbarbutton.Click:connect(function()
if on then
Off()
elseif loaded then
On()
end
end)
game:WaitForChild "Workspace"
game.Workspace:WaitForChild "Terrain"
local c = game.Workspace.Terrain
local SetCell = c.SetCell
local SetWaterCell = c.SetWaterCell
local GetCell = c.GetCell
local GetWaterCell = c.GetWaterCell
local WorldToCellPreferSolid = c.WorldToCellPreferSolid
local WorldToCellPreferEmpty = c.WorldToCellPreferEmpty
local CellCenterToWorld = c.CellCenterToWorld
local AutoWedge = c.AutowedgeCell
-----------------
--DEFAULT VALUES-
-----------------
-- The ID number that represents water.
local waterMaterialID = 17
-- Elevation related options. Contains everything needed for making the elevation.
local elevationOptions = {
r = 0, -- Radius of the elevation area. The larger it is, the wider the top of the elevation will be.
s = 1, -- Slope of the elevation area. The larger it is, the steeper the slope.
defaultTerrainMaterial = 1, -- The material that the elevation should be made of.
waterForce = nil, -- What force the material has if it is water.
waterDirection = nil, -- What direction the material has if it is water.
autowedge = true, -- Whether smoothing should be applied. True if it should, false if not.
}
-- What color to use for the mouse highlighter.
local mouseHighlightColor = BrickColor.new "Lime green"
-- Used to create a highlighter that follows the mouse.
-- It is a class mouse highlighter. To use, call MouseHighlighter.Create(mouse) where mouse is the mouse to track.
local MouseHighlighter = {}
MouseHighlighter.__index = MouseHighlighter
-- Create a mouse movement highlighter.
-- plugin - Plugin to get the mouse from.
function MouseHighlighter.Create(mouseUse)
local highlighter = {}
local mouseH = mouseUse
highlighter.OnClicked = nil
highlighter.mouseDown = false
-- Store the last point used to draw.
highlighter.lastUsedPoint = nil
-- 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)
-- Do a line/plane intersection. The line starts at the camera. The plane is at y == 0, normal(0, 1, 0)
--
-- vectorPos - End point of the line.
--
-- 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.
local function PlaneIntersection(vectorPos)
local currCamera = game.Workspace.CurrentCamera
local startPos = Vector3.new(
currCamera.CoordinateFrame.p.X,
currCamera.CoordinateFrame.p.Y,
currCamera.CoordinateFrame.p.Z
)
local endPos = Vector3.new(vectorPos.X, vectorPos.Y, vectorPos.Z)
local normal = Vector3.new(0, 1, 0)
local p3 = Vector3.new(0, 0, 0)
local startEndDot = normal:Dot(endPos - startPos)
local cellPos = vectorPos
local success = false
if startEndDot ~= 0 then
local t = normal:Dot(p3 - startPos) / startEndDot
if t >= 0 and t <= 1 then
local intersection = ((endPos - startPos) * t) + startPos
cellPos = c:WorldToCell(intersection)
success = true
end
end
return success, cellPos
end
-- Update where the highlighter is displayed.
-- position - Where to display the highlighter, in world space.
function UpdatePosition(position)
if not position then
return
end
-- NOTE:
-- Change this gui to be the one you want to use.
highlighter.selectionBox.Parent = game:GetService "CoreGui"
local vectorPos = Vector3.new(position.x, position.y, position.z)
local cellPos = WorldToCellPreferEmpty(c, vectorPos)
local solidCell = WorldToCellPreferSolid(c, vectorPos)
local success = false
-- If nothing was hit, do the plane intersection.
if 0 == GetCell(c, solidCell.X, solidCell.Y, solidCell.Z).Value then
success, cellPos = PlaneIntersection(vectorPos)
if not success then
cellPos = solidCell
end
else
highlighter.lastUsedPoint = cellPos
end
local regionToSelect
local lowVec = CellCenterToWorld(c, cellPos.x, cellPos.y - 1, cellPos.z)
local highVec =
CellCenterToWorld(c, cellPos.x, cellPos.y + 1, cellPos.z)
regionToSelect = Region3.new(lowVec, highVec)
highlighter.selectionPart.Size = regionToSelect.Size
- Vector3.new(-4, 4, -4)
highlighter.selectionPart.CFrame = regionToSelect.CFrame
if nil ~= highlighter.OnClicked and highlighter.mouseDown then
if nil == highlighter.lastUsedPoint then
highlighter.lastUsedPoint = WorldToCellPreferEmpty(
c,
Vector3.new(mouseH.Hit.x, mouseH.Hit.y, mouseH.Hit.z)
)
else
cellPos = WorldToCellPreferEmpty(
c,
Vector3.new(mouseH.Hit.x, mouseH.Hit.y, mouseH.Hit.z)
)
end
end
end
return highlighter
end
-- Hide the highlighter.
function MouseHighlighter:DisablePreview()
self.selectionBox.Parent = nil
end
-- Show the highlighter.
function MouseHighlighter:EnablePreview()
self.selectionBox.Parent = game:GetService "CoreGui" -- This will make it not show up in workspace.
end
-- Create the mouse movement highlighter.
local mouseHighlighter = MouseHighlighter.Create(mouse)
------
--GUI-
------
--screengui
local g = Instance.new "ScreenGui"
g.Name = "ElevationGui"
g.Parent = game:GetService "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.
-- steps - How many steps there are in the slider.
-- funcOnChange - Function to call when the slider changes.
-- initValue - Initial value to set the slider to. If nil the slider stays at the default.
-- parent - What to set as the parent.
-- Return:
-- sliderGui - Slider gui object.
-- sliderPosition - Object that can set the slider value.
function CreateStandardSlider(
name,
pos,
lengthBarPos,
steps,
funcOnChange,
initValue,
parent
)
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, -20, 0, 5)
lengthBar.Position = lengthBarPos
if nil ~= funcOnChange then
sliderPosition.Changed:connect(function()
funcOnChange(sliderPosition)
end)
end
if nil ~= initValue then
sliderPosition.Value = initValue
end
return sliderGui, sliderPosition
end
-- Gui frame for the plugin.
local elevationPropertiesDragBar, elevationFrame, elevationHelpFrame, elevationCloseEvent =
RbxGui.CreatePluginFrame(
"Elevation Adjuster",
UDim2.new(0, 185, 0, 100),
UDim2.new(0, 0, 0, 0),
false,
g
)
elevationPropertiesDragBar.Visible = false
elevationCloseEvent.Event:connect(function()
Off()
end)
elevationHelpFrame.Size = UDim2.new(0, 300, 0, 130)
local elevationHelpText = Instance.new "TextLabel"
elevationHelpText.Name = "HelpText"
elevationHelpText.Font = Enum.Font.ArialBold
elevationHelpText.FontSize = Enum.FontSize.Size12
elevationHelpText.TextColor3 = Color3.new(227 / 255, 227 / 255, 227 / 255)
elevationHelpText.TextXAlignment = Enum.TextXAlignment.Left
elevationHelpText.TextYAlignment = Enum.TextYAlignment.Top
elevationHelpText.Position = UDim2.new(0, 4, 0, 4)
elevationHelpText.Size = UDim2.new(1, -8, 0, 177)
elevationHelpText.BackgroundTransparency = 1
elevationHelpText.TextWrap = true
elevationHelpText.Text = [[
Use to drag terrain up or down. Hold the left mouse button down and drag the mouse up or down to create a mountain or valley.
Radius:
The larger it is, the wider the top of the elevation will be.
Slope:
The larger it is, the steeper the slope.]]
elevationHelpText.Parent = elevationHelpFrame
-- Slider for controlling radius.
local radiusLabel = CreateStandardLabel(
"RadiusLabel",
UDim2.new(0, 8, 0, 10),
UDim2.new(0, 67, 0, 14),
"",
elevationFrame
)
local _, radiusSliderPosition = CreateStandardSlider(
"radiusSliderGui",
UDim2.new(0, 1, 0, 26),
UDim2.new(0, 10, 0.5, -2),
11,
function(radiusSliderPosition2)
elevationOptions.r = radiusSliderPosition2.Value -- 1
radiusLabel.Text = "Radius: " .. elevationOptions.r
end,
nil,
elevationFrame
)
radiusSliderPosition.Value = 1
-- Slider for controlling the z offset to generate terrain at.
local slopeLabel = CreateStandardLabel(
"SlopeLabel",
UDim2.new(0, 8, 0, 51),
UDim2.new(0, 67, 0, 14),
"",
elevationFrame
)
local _, slopeSliderPosition = CreateStandardSlider(
"slopeSliderGui",
UDim2.new(0, 1, 0, 67),
UDim2.new(0, 10, 0.5, -2),
16,
function()
elevationOptions.s = slopeSliderPosition.Value / 10 + 0.4
slopeLabel.Text = "Slope: " .. elevationOptions.s
end,
nil,
elevationFrame
)
slopeSliderPosition.Value = 1
-----------------------
--FUNCTION DEFINITIONS-
-----------------------
--find height at coordinate x, z
function findHeight(x, z)
local h = 0
local material, _, _ = GetCell(c, x, h + 1, z)
while material.Value > 0 do
h = h + 1
material, _, _ = GetCell(c, x, h + 1, z)
end
return h
end
--makes a shell around block at coordinate x, z using heightmap
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
if shellheightmap[i][k] < originalheight then
for h = originalheight, shellheightmap[i][k] - 2, -1 do
if h > 0 then
if
waterMaterialID
== elevationOptions.defaultTerrainMaterial
then
SetWaterCell(
c,
i,
h,
k,
elevationOptions.waterForce,
elevationOptions.waterDirection
)
else
SetCell(
c,
i,
h,
k,
elevationOptions.defaultTerrainMaterial,
0,
0
)
end
end
end
shellheightmap[i][k] = originalheight
end
end
end
return shellheightmap
end
local height
--elevates terrain at point (x, y, z) in cluster c
--within radius r1 from x, z the elevation should become y + d
--from radius r1 to r2 the elevation should be a gradient
function elevate(x, y, z, r1, r2, d, range)
for i = x - (range + 2), x + (range + 2) do
if oldheightmap[i] == nil then
oldheightmap[i] = {}
end
for k = z - (range + 2), z + (range + 2) do
if oldheightmap[i][k] == nil then
oldheightmap[i][k] = findHeight(i, k)
end
--figure out the height to make coordinate (i, k)
local distance = dist(i, k, x, z)
if distance < r1 then
height = y + d
elseif distance < r2 then
height = math.floor(
(y + d) * (1 - (distance - r1) / (r2 - r1))
+ oldheightmap[i][k] * (distance - r1) / (r2 - r1)
)
else
height = oldheightmap[i][k]
end
if height == 0 then
height = -1
end
--heightmap[i][k] should be the current height of coordinate (i, k)
if heightmap[i] == nil then
heightmap[i] = {}
end
if heightmap[i][k] == nil then
heightmap[i][k] = oldheightmap[i][k]
end
--the height is either greater than or less than the current height
if height > heightmap[i][k] then
for h = heightmap[i][k] - 2, height do
SetCell(
c,
i,
h,
k,
elevationOptions.defaultTerrainMaterial,
0,
0
)
end
heightmap[i][k] = height
elseif height < heightmap[i][k] then
for h = heightmap[i][k], height + 1, -1 do
SetCell(c, i, h, k, 0, 0, 0)
end
heightmap[i][k] = height
end
end
end
--copy heightmap into shellheightmap
local shellheightmap = {}
for i = x - (range + 2), x + (range + 2) do
if shellheightmap[i] == nil then
shellheightmap[i] = {}
end
for k = z - (range + 2), z + (range + 2) do
shellheightmap[i][k] = heightmap[i][k]
end
end
--shell everything
for i = x - range, x + range do
for k = z - range, z + range do
if shellheightmap[i][k] ~= oldheightmap[i][k] then
shellheightmap = makeShell(i, k, heightmap, shellheightmap)
end
end
end
for i = x - (range + 2), x + (range + 2) do
for k = z - (range + 2), z + (range + 2) do
heightmap[i][k] = shellheightmap[i][k]
end
end
for k = z - (range + 1), z + (range + 1) do
for i = x - (range + 1), x + (range + 1) do
local height2 = heightmap[i][k]
if height2 == nil then
height2 = -1
end
-- Autowedge if enabled.
if elevationOptions.autowedge then
for h = height2, 1, -1 do
if not AutoWedge(c, i, h, k) then
break
end
end
end
end
end
end
function dist(x1, y1, x2, y2)
return math.sqrt(math.pow(x2 - x1, 2) + math.pow(y2 - y1, 2))
end
-- function dist3d(x1, y1, z1, x2, y2, z2)
-- return math.sqrt(math.pow(dist(x1, z1, x2, z2), 2) + math.pow(math.abs(y2 - y1) * 100 / d, 2))
-- end
-- Run when the mouse gets clicked. If the click is on terrain, then it will be used as the starting point of the elevation area.
function onClicked(mouse2)
if on then
oldheightmap = {}
heightmap = {}
local cellPos = WorldToCellPreferEmpty(
c,
Vector3.new(mouse2.Hit.x, mouse2.Hit.y, mouse2.Hit.z)
)
local solidCell = WorldToCellPreferSolid(
c,
Vector3.new(mouse2.Hit.x, mouse2.Hit.y, mouse2.Hit.z)
)
local success = false
-- If nothing was hit, do the plane intersection.
if 0 == GetCell(c, solidCell.X, solidCell.Y, solidCell.Z).Value then
--print('Plane Intersection happens')
success, cellPos = PlaneIntersection(
Vector3.new(mouse2.Hit.x, mouse2.Hit.y, mouse2.Hit.z)
)
if not success then
cellPos = solidCell
end
end
local x = cellPos.X
local y = cellPos.Y
local z = cellPos.Z
local celMat = GetCell(c, x, y, z)
if celMat.Value > 0 then
elevationOptions.defaultTerrainMaterial = celMat.Value
_, elevationOptions.waterForce, elevationOptions.waterDirection =
GetWaterCell(c, cellPos.X, cellPos.Y, cellPos.Z)
else
if 0 == elevationOptions.defaultTerrainMaterial then
-- It was nothing, give it a default type and the plane intersection.
elevationOptions.isWater = false
elevationOptions.defaultTerrainMaterial = 1
end
end
-- Hide the selection area while dragging.
mouseHighlighter:DisablePreview()
local mousedown = true
local originalY = mouse2.Y
local prevY = originalY
local d = 0
local range = 0
while mousedown == true do
if math.abs(mouse2.Y - prevY) >= 5 then
prevY = mouse2.Y
local r2 = elevationOptions.r
+ math.floor(
50
* 1
/ elevationOptions.s
* math.abs(originalY - prevY)
/ mouse2.ViewSizeY
)
if r2 > range then
range = r2
end
d = math.floor(50 * (originalY - prevY) / mouse2.ViewSizeY)
elevate(x, y, z, elevationOptions.r, r2, d, range)
end
wait(0)
end
game:GetService("ChangeHistoryService"):SetWaypoint "Elevation"
end
end
mouseHighlighter.OnClicked = onClicked
mouse = plugin:GetMouse()
mouse.Button1Up:connect(function()
mousedown = false
mouseHighlighter:EnablePreview()
end)
-- Run when the popup is activated.
On = function()
if not c then
return
end
plugin:Activate(true)
toolbarbutton:SetActive(true)
elevationPropertiesDragBar.Visible = true
on = true
end
-- Run when the popup is deactivated.
Off = function()
toolbarbutton:SetActive(false)
on = false
-- Hide the popup gui.
elevationPropertiesDragBar.Visible = false
mouseHighlighter:DisablePreview()
end
plugin.Deactivation:connect(function()
Off()
end)
loaded = true

View File

@ -0,0 +1,701 @@
while game == nil do
wait(1 / 30)
end
---------------
--PLUGIN SETUP-
---------------
local loaded = false
local on = false
local On, Off
local this = PluginManager():CreatePlugin()
local toolbar = this:CreateToolbar "Terrain"
local toolbarbutton = toolbar:CreateButton("Brush", "Brush", "brush.png")
toolbarbutton.Click:connect(function()
if on then
Off()
elseif loaded then
On()
end
end)
game:WaitForChild "Workspace"
game.Workspace:WaitForChild "Terrain"
-- Local function definitions
local c = game.Workspace.Terrain
local GetCell = c.GetCell
local SetCells = c.SetCells
local AutowedgeCells = c.AutowedgeCells
local WorldToCellPreferSolid = c.WorldToCellPreferSolid
local CellCenterToWorld = c.CellCenterToWorld
local buildTerrainMode = "Add"
local removeTerrainMode = "Remove"
-----------------
--DEFAULT VALUES-
-----------------
local radius = 5
local depth = 0
local mousedown = false
local mousemoving = false
local brushheight
local material = 1
local lastMousePos = Vector2.new(-1, -1)
local lastCellFillTime = 0
local maxYExtents = math.min(c.MaxExtents.Max.Y, 512)
-- Which mode (build/remove) it is.
local mode = buildTerrainMode
-- Height and depth to use for the different modes.
local buildTerrainHeight = 5
local removeTerrainDepth = -5
local mouse = this:GetMouse()
mouse.Button1Down:connect(function()
buttonDown()
end)
mouse.Button1Up:connect(function()
mousedown = false
brushheight = nil
enablePreview()
updatePreviewSelection(mouse.Hit)
game:GetService("ChangeHistoryService"):SetWaypoint "Brush"
end)
mouse.Move:connect(function()
mouseMoved()
end)
local selectionPart = Instance.new "Part"
selectionPart.Name = "SelectionPart"
selectionPart.Archivable = false
selectionPart.Transparency = 1
selectionPart.Anchored = true
selectionPart.Locked = true
selectionPart.CanCollide = false
selectionPart.FormFactor = Enum.FormFactor.Custom
local selectionBox = Instance.new "SelectionBox"
selectionBox.Archivable = false
selectionBox.Color = BrickColor.new "Lime green"
selectionBox.Adornee = selectionPart
mouse.TargetFilter = selectionPart
-----------------------
--FUNCTION DEFINITIONS-
-----------------------
-- searches the y depth of a particular cell position to find the lowest y that is empty
function findLowestEmptyCell(x, y, z)
local cellMat = GetCell(c, x, y, z)
local lowestY = y
while cellMat == Enum.CellMaterial.Empty do
lowestY = y
if y > 0 then
y = y - 1
cellMat = GetCell(c, x, y, z)
else
lowestY = 0
cellMat = nil
end
end
return lowestY
end
-- finds the lowest cell that is not filled in the radius that is currently specified
function findLowPoint(x, y, z)
local lowestPoint = maxYExtents + 1
for i = -radius, radius do
local zPos = z + i
for _ = -radius, radius do
local xPos = x + i
local cellMat = GetCell(c, xPos, y, zPos)
if cellMat == Enum.CellMaterial.Empty then
local emptyDepth = findLowestEmptyCell(xPos, y, zPos)
if emptyDepth < lowestPoint then
lowestPoint = emptyDepth
end
end
end
end
return lowestPoint
end
--brushes terrain at point (x, y, z) in cluster c
function brush(x, y, z)
if depth == 0 then
return
end
if depth > 0 then
local findY = findLowPoint(x, y + depth, z)
local yWithDepth = y + depth
local lowY
if findY < yWithDepth then
lowY = findY
else
lowY = yWithDepth
end
local lowVec = Vector3int16.new(x - radius, lowY, z - radius)
local highVec = Vector3int16.new(x + radius, yWithDepth, z + radius)
local regionToFill = Region3int16.new(lowVec, highVec)
SetCells(c, regionToFill, material, 0, 0)
AutowedgeCells(c, regionToFill)
else
local lowVec = Vector3int16.new(x - radius, y + depth + 1, z - radius)
local highVec = Vector3int16.new(x + radius, maxYExtents, z + radius)
local regionToEmpty = Region3int16.new(lowVec, highVec)
SetCells(c, regionToEmpty, Enum.CellMaterial.Empty, 0, 0)
end
end
function disablePreview()
selectionBox.Parent = nil
end
function enablePreview()
selectionBox.Parent = game.Workspace
end
function updatePreviewSelection(position)
if not position then
return
end
--if not mouse.Target then disablePreview() return end
if depth == 0 then
disablePreview()
return
end
local vectorPos = Vector3.new(position.x, position.y, position.z)
local cellPos = WorldToCellPreferSolid(c, vectorPos)
local solidCell = WorldToCellPreferSolid(c, vectorPos)
-- If nothing was hit, do the plane intersection.
if 0 == GetCell(c, solidCell.X, solidCell.Y, solidCell.Z).Value then
local success = false
success, cellPos = PlaneIntersection(vectorPos)
if not success then
if mouse.Target then
cellPos = solidCell
else
return
end
end
end
local regionToSelect
if depth > 0 then
local yWithDepth
if brushheight then
yWithDepth = brushheight + depth
else
yWithDepth = cellPos.y + depth
end
local lowY
if brushheight then
lowY = brushheight + 1
else
local findY = findLowPoint(cellPos.x, yWithDepth, cellPos.z)
if findY < yWithDepth then
lowY = findY
else
lowY = yWithDepth
end
end
local lowVec = CellCenterToWorld(
c,
cellPos.x - radius,
lowY - 1,
cellPos.z - radius
)
local highVec = CellCenterToWorld(
c,
cellPos.x + radius,
yWithDepth + 1,
cellPos.z + radius
)
selectionBox.Color = BrickColor.new "Lime green"
regionToSelect = Region3.new(lowVec, highVec)
else
local yPos = cellPos.y + depth
if brushheight then
yPos = brushheight + depth
end
local lowVec =
CellCenterToWorld(c, cellPos.x - radius, yPos, cellPos.z - radius)
local highVec = CellCenterToWorld(
c,
cellPos.x + radius,
maxYExtents,
cellPos.z + radius
)
selectionBox.Color = BrickColor.new "Really red"
regionToSelect = Region3.new(lowVec, highVec)
end
selectionPart.Size = regionToSelect.Size - Vector3.new(-4, 4, -4)
selectionPart.CFrame = regionToSelect.CFrame
enablePreview()
end
function doFillCells(position, mouseDrag, needsCellPos)
if mouseDrag then
local timeBetweenFills = tick() - lastCellFillTime
local totalDragPixels = math.abs(mouseDrag.x) + math.abs(mouseDrag.y)
local editDistance = (
game.Workspace.CurrentCamera.CoordinateFrame.p
- Vector3.new(position.x, position.y, position.z)
).magnitude
if timeBetweenFills <= 0.05 then
if editDistance * totalDragPixels < 450 then
lastCellFillTime = tick()
return
end
end
end
local x = position.x
local y = position.y
local z = position.z
if needsCellPos then
local cellPos = WorldToCellPreferSolid(c, Vector3.new(x, y, z))
local solidCell = WorldToCellPreferSolid(c, Vector3.new(x, y, z))
-- If nothing was hit, do the plane intersection.
if 0 == GetCell(c, solidCell.X, solidCell.Y, solidCell.Z).Value then
local success = false
success, cellPos = PlaneIntersection(Vector3.new(x, y, z))
if not success then
if mouse.Target then
cellPos = solidCell
else
return
end
end
end
x = cellPos.x
y = cellPos.y
z = cellPos.z
end
if brushheight == nil then
brushheight = y
end
brush(x, brushheight, z)
lastCellFillTime = tick()
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
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)
--
-- vectorPos - End point of the line.
--
-- Return:
-- success - Value is true if there was a plane intersection, false if not.
-- intersection - Value is the terrain cell intersection point if there is one, vectorPos if there isn't.
function PlaneIntersection(vectorPos)
local currCamera = game.Workspace.CurrentCamera
local startPos = Vector3.new(
currCamera.CoordinateFrame.p.X,
currCamera.CoordinateFrame.p.Y,
currCamera.CoordinateFrame.p.Z
)
local endPos = Vector3.new(vectorPos.X, vectorPos.Y, vectorPos.Z)
local normal = Vector3.new(0, 1, 0)
local p3 = Vector3.new(0, 0, 0)
local startEndDot = normal:Dot(endPos - startPos)
local cellPos = vectorPos
local success = false
if startEndDot ~= 0 then
local t = normal:Dot(p3 - startPos) / startEndDot
if t >= 0 and t <= 1 then
local intersection = ((endPos - startPos) * t) + startPos
cellPos = c:WorldToCell(intersection)
success = true
end
end
return success, cellPos
end
function buttonDown()
if on then
local firstCellPos = WorldToCellPreferSolid(
c,
Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z)
)
local solidCell = WorldToCellPreferSolid(
c,
Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z)
)
-- If nothing was hit, do the plane intersection.
if 0 == GetCell(c, solidCell.X, solidCell.Y, solidCell.Z).Value then
local success = false
success, firstCellPos = PlaneIntersection(
Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z)
)
if not success then
if mouse.Target then
firstCellPos = solidCell
else
return
end
end
end
local celMat =
GetCell(c, firstCellPos.x, firstCellPos.y, firstCellPos.z)
if celMat.Value > 0 then
material = celMat.Value
else
if 0 == material then
-- It was nothing, give it a default type and the plane intersection.
material = 1
end
end
brushheight = firstCellPos.y
lastMousePos = Vector2.new(mouse.X, mouse.Y)
doFillCells(firstCellPos)
mousedown = true
end
end
local brushDragBar
On = function()
if not c then
return
end
if this then
this:Activate(true)
end
if toolbarbutton then
toolbarbutton:SetActive(true)
end
if enablePreview then
enablePreview()
end
if brushDragBar then
brushDragBar.Visible = true
end
on = true
end
Off = function()
if toolbarbutton then
toolbarbutton:SetActive(false)
end
if disablePreview then
disablePreview()
end
if brushDragBar then
brushDragBar.Visible = false
end
on = false
end
------
--GUI-
------
--load library for with sliders
local RbxGui = LoadLibrary "RbxGui"
-- Create a standard dropdown. Use this for all dropdowns in the popup so it is easy to standardize.
-- name - What to set the text label name as.
-- pos - Where to position the label. Should be of type UDim2.
-- values - A table of the values that will be in the dropbox, in the order they are to be shown.
-- initValue - Initial value the dropdown should be set to.
-- funcOnChange - Function to run when a dropdown selection is made.
-- parent - What to set the parent as.
-- Return:
-- dropdown - The dropdown gui.
-- updateSelection - Object to use to change the current dropdown.
function CreateStandardDropdown(
name,
pos,
values,
initValue,
funcOnChange,
parent
)
-- Create a dropdown selection for the modes to fill in a river
local dropdown, updateSelection =
RbxGui.CreateDropDownMenu(values, funcOnChange)
dropdown.Name = name
dropdown.Position = pos
dropdown.Active = true
dropdown.Size = UDim2.new(0, 150, 0, 32)
dropdown.Parent = parent
updateSelection(initValue)
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.
-- steps - How many steps there are in the slider.
-- funcOnChange - Function to call when the slider changes.
-- initValue - Initial value to set the slider to. If nil the slider stays at the default.
-- parent - What to set as the parent.
-- Return:
-- sliderGui - Slider gui object.
-- sliderPosition - Object that can set the slider value.
-- function CreateStandardSlider(
-- name,
-- pos,
-- lengthBarPos,
-- steps,
-- funcOnChange,
-- initValue,
-- parent
-- )
-- 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(0, 160, 0, 20)
-- local lengthBar = sliderGui:FindFirstChild "Bar"
-- lengthBar.Size = UDim2.new(1, -20, 0, 5)
-- lengthBar.Position = lengthBarPos
-- if nil ~= funcOnChange then
-- sliderPosition.Changed:connect(function()
-- funcOnChange(sliderPosition)
-- end)
-- end
-- if nil ~= initValue then
-- sliderPosition.Value = initValue
-- end
-- return sliderGui, sliderPosition
-- end
--screengui
local g = Instance.new "ScreenGui"
g.Name = "TerrainBrushGui"
g.Parent = game:GetService "CoreGui"
brushDragBar, elevationFrame, elevationHelpFrame, elevationCloseEvent =
RbxGui.CreatePluginFrame(
"Terrain Brush",
UDim2.new(0, 151, 0, 160),
UDim2.new(0, 0, 0, 0),
false,
g
)
brushDragBar.Visible = false
elevationCloseEvent.Event:connect(function()
Off()
end)
elevationHelpFrame.Size = UDim2.new(0, 200, 0, 210)
local helpText = Instance.new "TextLabel"
helpText.Font = Enum.Font.ArialBold
helpText.FontSize = Enum.FontSize.Size12
helpText.TextColor3 = Color3.new(1, 1, 1)
helpText.BackgroundTransparency = 1
helpText.TextWrap = true
helpText.Size = UDim2.new(1, -10, 1, -10)
helpText.Position = UDim2.new(0, 5, 0, 5)
helpText.TextXAlignment = Enum.TextXAlignment.Left
helpText.Text =
[[Drag the mouse by holding the left mouse button to either create or destroy terrain defined by the selection box.
Radius:
Half of the width of the selection box to be used.
Height:
How tall to make terrain from the mouse location. If this value is negative, the brush will remove terrain instead of creating terrain (indicated by the red selection box).
]]
helpText.Parent = elevationHelpFrame
--current radius display label
local radl = Instance.new "TextLabel"
radl.Position = UDim2.new(0, 0, 0, 70)
radl.Size = UDim2.new(1, 0, 0, 14)
radl.Text = ""
radl.BackgroundColor3 = Color3.new(0.4, 0.4, 0.4)
radl.TextColor3 = Color3.new(0.95, 0.95, 0.95)
radl.Font = Enum.Font.ArialBold
radl.FontSize = Enum.FontSize.Size14
radl.TextXAlignment = Enum.TextXAlignment.Left
radl.BorderColor3 = Color3.new(0, 0, 0)
radl.BackgroundTransparency = 1
radl.Parent = elevationFrame
--radius slider
local radSliderGui, radSliderPosition =
RbxGui.CreateSlider(5, 0, UDim2.new(0, 10, 0, 92))
radSliderGui.Parent = elevationFrame
local radBar = radSliderGui:FindFirstChild "Bar"
radBar.Size = UDim2.new(1, -20, 0, 5)
radSliderPosition.Changed:connect(function()
radius = radSliderPosition.Value + 1
radl.Text = " Radius: " .. radius
end)
radSliderPosition.Value = radius - 1
--current depth factor display label
local dfl = Instance.new "TextLabel"
dfl.Position = UDim2.new(0, 0, 0, 110)
dfl.Size = UDim2.new(1, 0, 0, 14)
dfl.Text = ""
dfl.BackgroundColor3 = Color3.new(0.4, 0.4, 0.4)
dfl.TextColor3 = Color3.new(0.95, 0.95, 0.95)
dfl.Font = Enum.Font.ArialBold
dfl.FontSize = Enum.FontSize.Size14
dfl.BorderColor3 = Color3.new(0, 0, 0)
dfl.TextXAlignment = Enum.TextXAlignment.Left
dfl.BackgroundTransparency = 1
dfl.Parent = elevationFrame
--depth factor slider
local addSliderGui, addSliderPosition =
RbxGui.CreateSlider(31, 0, UDim2.new(0, 10, 0, 132))
addSliderGui.Parent = elevationFrame
local dfBar = addSliderGui:FindFirstChild "Bar"
dfBar.Size = UDim2.new(1, -20, 0, 5)
addSliderPosition.Changed:connect(function()
depth = addSliderPosition.Value
dfl.Text = " Height: " .. depth
end)
addSliderPosition.Value = buildTerrainHeight
--depth factor slider
local removeSliderGui, removeSliderPosition =
RbxGui.CreateSlider(31, 0, UDim2.new(0, 10, 0, 132))
removeSliderGui.Parent = elevationFrame
dfBar = removeSliderGui:FindFirstChild "Bar"
dfBar.Size = UDim2.new(1, -20, 0, 5)
removeSliderPosition.Changed:connect(function()
depth = -removeSliderPosition.Value
dfl.Text = " Height: " .. depth
end)
removeSliderPosition.Value = removeTerrainDepth
-- Set which mode is to be used. Show/hide as needed.
--
-- mode - Which build mode to run.
function SetMode(setmode)
if setmode == buildTerrainMode then
addSliderGui.Visible = true
local hold = addSliderPosition.Value
addSliderPosition.Value = 0
addSliderPosition.Value = hold
removeSliderGui.Visible = false
elseif setmode == removeTerrainMode then
addSliderGui.Visible = false
removeSliderGui.Visible = true
local hold = removeSliderPosition.Value
removeSliderPosition.Value = 0
removeSliderPosition.Value = hold
end
end
-- Create/Remove mode
-- Create the build mode gui.
-- local buildModeLabel = CreateStandardLabel(
-- "BuildModeLabel",
-- UDim2.new(0, 8, 0, 10),
-- UDim2.new(0, 67, 0, 14),
-- "Build Mode:",
-- elevationFrame
-- )
local _, buildModeSet = CreateStandardDropdown(
"BuildModeDropdown",
UDim2.new(0, 1, 0, 26),
{ buildTerrainMode, removeTerrainMode },
buildMode,
function(value)
if "Add" == value then
SetMode(buildTerrainMode)
elseif "Remove" == value then
SetMode(removeTerrainMode)
end
end,
elevationFrame
)
--[[
buildModeSet(buildTerrainMode)
buildModeSet(removeTerrainMode)
--]]
buildModeSet(mode)
SetMode(mode)
this.Deactivation:connect(function()
Off()
end)
--------------------------
--SUCCESSFUL LOAD MESSAGE-
--------------------------
loaded = true

View File

@ -0,0 +1,269 @@
while game == nil do
wait(1 / 30)
end
---------------
--PLUGIN SETUP-
---------------
local loaded = false
local on = false
local On, Off
local this = PluginManager():CreatePlugin()
local mouse = this:GetMouse()
mouse.Button1Down:connect(function()
onClicked(mouse)
end)
local toolbar = this:CreateToolbar "Terrain"
local toolbarbutton = toolbar:CreateButton("Crater", "Crater", "craters.png")
toolbarbutton.Click:connect(function()
if on then
Off()
elseif loaded then
On()
end
end)
game:WaitForChild "Workspace"
game.Workspace:WaitForChild "Terrain"
-- Local function definitions
local c = game.Workspace.Terrain
local SetCell = c.SetCell
local GetCell = c.GetCell
local WorldToCellPreferSolid = c.WorldToCellPreferSolid
local AutoWedge = c.AutowedgeCell
-----------------
--DEFAULT VALUES-
-----------------
local r = 20
local d = 20
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 heightmap = {}
for i = x - (cr + 1), x + (cr + 1) do
heightmap[i] = {}
end
for j = 0, cr + 1 do
local cellschanged = false
for i = x - (cr + 1), x + (cr + 1) do
for k = z - (cr + 1), z + (cr + 1) do
local distance = math.sqrt(
math.pow(dist(x, z, i, k), 2)
+ math.pow(y - (y - j * (100 / cd)), 2)
)
if distance < r then
SetCell(c, i, y + j, k, 0, 0, 0)
SetCell(c, i, y - j, k, 0, 0, 0)
cellschanged = true
elseif heightmap[i] and heightmap[i][k] == nil then
local material, _, _ = GetCell(c, i, y - j, k)
if material.Value > 0 then
heightmap[i][k] = y - j
end
end
end
end
if cellschanged == false then
break
end
wait(0)
end
for ri = 0, cr do
wait(0)
local i = x - ri
for k = z - cr, z + cr do
local height = heightmap[i][k]
if height == nil then
height = -1
end
for h = height, 0, -1 do
if not AutoWedge(c, i, h, k) then
break
end
end
end
i = x + ri
for k = z - cr, z + cr do
local height = heightmap[i][k]
if height == nil then
height = -1
end
for h = height, 0, -1 do
if not AutoWedge(c, i, h, k) then
break
end
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
function onClicked(mouseC)
if on and not debounce then
debounce = true
local cellPos = WorldToCellPreferSolid(
c,
Vector3.new(mouseC.Hit.x, mouseC.Hit.y, mouseC.Hit.z)
)
local x = cellPos.x
local y = cellPos.y
local z = cellPos.z
makeCrater(x, y, z, r, d)
debounce = false
game:GetService("ChangeHistoryService"):SetWaypoint "Crater"
end
end
On = function()
if not c then
return
end
if this then
this:Activate(true)
end
if toolbarbutton then
toolbarbutton:SetActive(true)
end
if craterDragBar then
craterDragBar.Visible = true
end
on = true
end
Off = function()
if toolbarbutton then
toolbarbutton:SetActive(false)
end
if craterDragBar then
craterDragBar.Visible = false
end
on = false
end
------
--GUI-
------
--load library for with sliders
local RbxGui = LoadLibrary "RbxGui"
--screengui
local g = Instance.new "ScreenGui"
g.Name = "CraterGui"
g.Parent = game:GetService "CoreGui"
craterDragBar, craterFrame, craterHelpFrame, craterCloseEvent =
RbxGui.CreatePluginFrame(
"Crater",
UDim2.new(0, 141, 0, 100),
UDim2.new(0, 0, 0, 0),
false,
g
)
craterDragBar.Visible = false
craterCloseEvent.Event:connect(function()
Off()
end)
craterHelpFrame.Size = UDim2.new(0, 200, 0, 170)
local helpText = Instance.new "TextLabel"
helpText.Font = Enum.Font.ArialBold
helpText.FontSize = Enum.FontSize.Size12
helpText.TextColor3 = Color3.new(1, 1, 1)
helpText.BackgroundTransparency = 1
helpText.TextWrap = true
helpText.Size = UDim2.new(1, -10, 1, -10)
helpText.Position = UDim2.new(0, 5, 0, 5)
helpText.TextXAlignment = Enum.TextXAlignment.Left
helpText.Text =
[[Creates craters in existing terrain. Click on a point in terrain to make a crater.
Radius:
Half of the width of the crater to be created.
Depth:
A percentage value, representing a perfect spherical crater. 0% is no crater, 100% will make a crater the same depth as the radius.
]]
helpText.Parent = craterHelpFrame
--current radius display label
local radl = Instance.new "TextLabel"
radl.Position = UDim2.new(0, 0, 0, 10)
radl.Size = UDim2.new(1, 0, 0, 14)
radl.Text = ""
radl.BackgroundColor3 = Color3.new(0.4, 0.4, 0.4)
radl.TextColor3 = Color3.new(0.95, 0.95, 0.95)
radl.Font = Enum.Font.ArialBold
radl.FontSize = Enum.FontSize.Size14
radl.BorderColor3 = Color3.new(0, 0, 0)
radl.TextXAlignment = Enum.TextXAlignment.Left
radl.BackgroundTransparency = 1
radl.Parent = craterFrame
--radius slider
local radSliderGui, radSliderPosition =
RbxGui.CreateSlider(128, 0, UDim2.new(0, 10, 0, 32))
radSliderGui.Parent = craterFrame
local radBar = radSliderGui:FindFirstChild "Bar"
radBar.Size = UDim2.new(1, -20, 0, 5)
radSliderPosition.Changed:connect(function()
r = radSliderPosition.Value
radl.Text = " Radius: " .. r
end)
radSliderPosition.Value = r
--current depth factor display label
local dfl = Instance.new "TextLabel"
dfl.Position = UDim2.new(0, 0, 0, 50)
dfl.Size = UDim2.new(1, 0, 0, 14)
dfl.Text = ""
dfl.BackgroundColor3 = Color3.new(0.4, 0.4, 0.4)
dfl.TextColor3 = Color3.new(0.95, 0.95, 0.95)
dfl.Font = Enum.Font.ArialBold
dfl.FontSize = Enum.FontSize.Size14
dfl.BorderColor3 = Color3.new(0, 0, 0)
dfl.TextXAlignment = Enum.TextXAlignment.Left
dfl.BackgroundTransparency = 1
dfl.Parent = craterFrame
--depth factor slider
local dfSliderGui, dfSliderPosition =
RbxGui.CreateSlider(100, 0, UDim2.new(0, 10, 0, 72))
dfSliderGui.Parent = craterFrame
local dfBar = dfSliderGui:FindFirstChild "Bar"
dfBar.Size = UDim2.new(1, -20, 0, 5)
dfSliderPosition.Changed:connect(function()
d = dfSliderPosition.Value
dfl.Text = " Depth: " .. d .. "%"
end)
dfSliderPosition.Value = d
this.Deactivation:connect(function()
Off()
end)
--------------------------
--SUCCESSFUL LOAD MESSAGE-
--------------------------
loaded = true

View File

@ -0,0 +1,361 @@
while game == nil do
wait(1 / 30)
end
---------------
--PLUGIN SETUP-
---------------
local loaded = false
local on = false
local On, Off
local this = PluginManager():CreatePlugin()
local mouse = this:GetMouse()
mouse.Button1Down:connect(function()
onClicked(mouse)
end)
local toolbar = this:CreateToolbar "Terrain"
local toolbarbutton = toolbar:CreateButton("Roads", "Roads", "roads.png")
toolbarbutton.Click:connect(function()
if on then
Off()
elseif loaded then
On()
end
end)
game:WaitForChild "Workspace"
game.Workspace:WaitForChild "Terrain"
-- Local function definitions
local c = game.Workspace.Terrain
local GetCell = c.GetCell
local WorldToCellPreferSolid = c.WorldToCellPreferSolid
local WorldToCellPreferEmpty = c.WorldToCellPreferEmpty
local SetCells = c.SetCells
-----------------
--DEFAULT VALUES-
-----------------
local x1 = 200
local y1 = 200
local x2 = 300
local y2 = 300
local h = 20
local mode = 0
local DefaultTerrainMaterial = 1
local roadDragBar, roadFrame, roadCloseEvent
-----------------------
--FUNCTION DEFINITIONS-
-----------------------
--makes a column of blocks from 0 up to height at location (x,z) in cluster c
function coordHeight(x, z, height)
SetCells(
c,
Region3int16.new(
Vector3int16.new(x, 1, z),
Vector3int16.new(x, height, z)
),
DefaultTerrainMaterial,
0,
0
)
end
function coordCheck(x, z, height)
for hh = height, 0, -1 do
local material, _, _ = GetCell(c, x, hh, z)
if material.Value > 0 then
return true
elseif height == 0 then
return false
end
end
return
end
-- local function dist(dx1, dy1, dx2, dy2)
-- return math.sqrt(math.pow(dx2 - dx1, 2) + math.pow(dy2 - dy1, 2))
-- end
-- local function dist3d(dx1, dy1, dz1, dx2, dy2, dz2)
-- return math.sqrt(
-- math.pow(dist(dx1, dy1, dx2, dy2), 2) + math.pow(dz2 - dz1, 2)
-- )
-- end
--create a path between coordinates (x1,z1) and (x2,z2) at height h in cluster c
--a path is a road with height of 3 instead of 1, and it builds a bridge if there is no existing land under it
--if you want path to come from x direction, make it start at the place
--if you want it to come from z direction, make it end at the place
--if p is true, turns on pillars, otherwise pillars are off
function makePath(px1, pz1, px2, pz2, ph, pp)
local incx, n
if px2 < px1 then
incx = -1
n = 1
else
incx = 1
n = -1
end
for x = px1, px2 + n, incx do
SetCells(
c,
Region3int16.new(
Vector3int16.new(x, h + 1, pz1 - 1),
Vector3int16.new(x, ph + 3, pz1 + 1)
),
0,
0,
0
)
SetCells(
c,
Region3int16.new(
Vector3int16.new(x, ph, pz1 - 1),
Vector3int16.new(x, ph, pz1 + 1)
),
DefaultTerrainMaterial,
0,
0
)
end
if pp then
for x = px1, px2 + n, 16 * incx do
if coordCheck(x, pz1, ph - 1) then
coordHeight(x, pz1, ph - 1)
end
if coordCheck(x, pz1 - 1, ph - 1) then
coordHeight(x, pz1 - 1, ph - 1)
end
if coordCheck(x, pz1 + 1, ph - 1) then
coordHeight(x, pz1 + 1, ph - 1)
end
end
end
local incz, m
if pz2 < pz1 then
incz = -1
m = 1
n = 2
else
incz = 1
m = -1
n = -2
end
for z = pz1 + m, pz2 + n, incz do
SetCells(
c,
Region3int16.new(
Vector3int16.new(px2 - 1, ph + 1, z),
Vector3int16.new(px2 + 1, ph + 3, z)
),
0,
0,
0
)
SetCells(
c,
Region3int16.new(
Vector3int16.new(x2 - 1, ph, z),
Vector3int16.new(px2 + 1, ph, z)
),
DefaultTerrainMaterial,
0,
0
)
end
if pp then
for z = pz1 + m, pz2 + n, 16 * incz do
if coordCheck(px2, z, ph - 1) then
coordHeight(px2, z, ph - 1)
end
if coordCheck(px2 - 1, z, ph - 1) then
coordHeight(px2 - 1, z, ph - 1)
end
if coordCheck(px2 + 1, z, ph - 1) then
coordHeight(px2 + 1, z, ph - 1)
end
end
end
game:GetService("ChangeHistoryService"):SetWaypoint "Roads"
end
-- Do a line/plane intersection. The line starts at the camera. The plane is at y == 0, normal(0, 1, 0)
--
-- vectorPos - End point of the line.
--
-- 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.
function PlaneIntersection(vectorPos)
local currCamera = game.Workspace.CurrentCamera
local startPos = Vector3.new(
currCamera.CoordinateFrame.p.X,
currCamera.CoordinateFrame.p.Y,
currCamera.CoordinateFrame.p.Z
)
local endPos = Vector3.new(vectorPos.X, vectorPos.Y, vectorPos.Z)
local normal = Vector3.new(0, 1, 0)
local p3 = Vector3.new(0, 0, 0)
local startEndDot = normal:Dot(endPos - startPos)
local cellPos = vectorPos
local success = false
if startEndDot ~= 0 then
local t = normal:Dot(p3 - startPos) / startEndDot
if t >= 0 and t <= 1 then
local intersection = ((endPos - startPos) * t) + startPos
cellPos = c:WorldToCell(intersection)
success = true
end
end
return success, cellPos
end
function onClicked(mouseC)
if on then
local cellPos = WorldToCellPreferEmpty(
c,
Vector3.new(mouseC.Hit.x, mouseC.Hit.y, mouseC.Hit.z)
)
local solidCell = WorldToCellPreferSolid(
c,
Vector3.new(mouseC.Hit.x, mouseC.Hit.y, mouseC.Hit.z)
)
local success = false
-- If nothing was hit, do the plane intersection.
if 0 == GetCell(c, solidCell.X, solidCell.Y, solidCell.Z).Value then
--print('Plane Intersection happens')
success, cellPos = PlaneIntersection(
Vector3.new(mouseC.Hit.x, mouseC.Hit.y, mouseC.Hit.z)
)
if not success then
cellPos = solidCell
end
end
local x = cellPos.x
local y = cellPos.y
local z = cellPos.z
if mode == 0 then
x1 = x
y1 = z
h = y
mode = 1
-- first click determines default material
local celMat = GetCell(c, x, y, z)
if celMat.Value > 0 then
DefaultTerrainMaterial = celMat.Value
else
if 0 == DefaultTerrainMaterial then
DefaultTerrainMaterial = 1
end
end
elseif mode == 1 then
x2 = x
y2 = z
makePath(x1, y1, x2, y2, h, true)
mode = 0
end
end
end
On = function()
if not c then
return
end
if this then
this:Activate(true)
end
if toolbarbutton then
toolbarbutton:SetActive(true)
end
if roadDragBar then
roadDragBar.Visible = true
end
mode = 0
on = true
end
Off = function()
if toolbarbutton then
toolbarbutton:SetActive(false)
end
if roadDragBar then
roadDragBar.Visible = false
end
on = false
end
------
--GUI-
------
local RbxGui = LoadLibrary "RbxGui"
--screengui
local g = Instance.new "ScreenGui"
g.Name = "RoadGui"
g.Parent = game:GetService "CoreGui"
roadDragBar, roadFrame, roadHelpFrame, roadCloseEvent =
RbxGui.CreatePluginFrame(
"Roads",
UDim2.new(0, 141, 0, 80),
UDim2.new(0, 0, 0, 0),
false,
g
)
roadDragBar.Visible = false
roadCloseEvent.Event:connect(function()
Off()
end)
local roadTextHelper = Instance.new "TextLabel"
roadTextHelper.Text =
"Click once to set the starting point and again to set the endpoint of the road."
roadTextHelper.Font = Enum.Font.ArialBold
roadTextHelper.FontSize = Enum.FontSize.Size14
roadTextHelper.TextColor3 = Color3.new(1, 1, 1)
roadTextHelper.BackgroundTransparency = 1
roadTextHelper.Size = UDim2.new(1, -8, 1, -8)
roadTextHelper.Position = UDim2.new(0, 4, 0, 4)
roadTextHelper.TextXAlignment = Enum.TextXAlignment.Left
roadTextHelper.TextYAlignment = Enum.TextYAlignment.Top
roadTextHelper.TextWrap = true
roadTextHelper.Parent = roadFrame
roadHelpFrame.Size = UDim2.new(0, 200, 0, 150)
local helpText = Instance.new "TextLabel"
helpText.Font = Enum.Font.ArialBold
helpText.FontSize = Enum.FontSize.Size12
helpText.TextColor3 = Color3.new(1, 1, 1)
helpText.BackgroundTransparency = 1
helpText.TextWrap = true
helpText.Size = UDim2.new(1, -10, 1, -10)
helpText.Position = UDim2.new(0, 5, 0, 5)
helpText.TextXAlignment = Enum.TextXAlignment.Left
helpText.Text =
[[Creates roads in existing terrain. Roads are created by setting a beginning point (first click on terrain) and an ending point (second click on terrain). The material of terrain is determined by the first click point. After the second click the tool resets and will create a new segment of road, not neccessarily connected to the first road segment created.]]
helpText.Parent = roadHelpFrame
this.Deactivation:connect(function()
Off()
end)
--------------------------
--SUCCESSFUL LOAD MESSAGE-
--------------------------
loaded = true

View File

@ -0,0 +1,851 @@
while game == nil do
wait(1 / 30)
end
---------------
--PLUGIN SETUP-
---------------
local loaded = false
local on = false
local On, Off
local this = PluginManager():CreatePlugin()
local mouse = this:GetMouse()
mouse.Button1Down:connect(function()
mouseDown(mouse)
end)
mouse.Button1Up:connect(function()
mouseUp(mouse)
end)
mouse.Move:connect(function()
mouseMove(mouse)
end)
local toolbar = this:CreateToolbar "Terrain"
local toolbarbutton = toolbar:CreateButton(
"Material Brush",
"Material Brush",
"materialBrush.png"
)
toolbarbutton.Click:connect(function()
if on then
Off()
elseif loaded then
On()
end
end)
game:WaitForChild "Workspace"
game.Workspace:WaitForChild "Terrain"
-----------------------------
--LOCAL FUNCTION DEFINITIONS-
-----------------------------
local c = game.Workspace.Terrain
local SetCell = c.SetCell
local SetWaterCell = c.SetWaterCell
-- local GetWaterCell = c.GetWaterCell
local GetCell = c.GetCell
local WorldToCellPreferSolid = c.WorldToCellPreferSolid
local CellCenterToWorld = c.CellCenterToWorld
local waterMaterial = 17
local brushTypes = { "Circular", "Square" }
-- local waterForceDirections = { "NegX", "X", "NegY", "Y", "NegZ", "Z" }
-- local waterForces = { "None", "Small", "Medium", "Strong", "Max" }
local mediumWaterForce = Enum.WaterForce.Medium
-----------------
--DEFAULT VALUES-
-----------------
local terrainSelectorGui, terrainSelected, radiusLabel, dragBar, closeEvent, helpFrame, currSelectionUpdate, currSelectionDestroy, lastCell, lastLastCell --, waterPanel
local dragging = false
-- local painting = false
--- exposed values to user via gui
local currentMaterial = Enum.CellMaterial.Grass
local radius = 3
local brushType = "Square"
-- local currWaterForceDirection = "NegX"
-- local currWaterForce = "None"
-- lua library load
local RbxGui = LoadLibrary "RbxGui"
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
end
if directionIsDown(lastCell, mouseCellPos) then
paintWaterfall(setCells)
return
end
local initX = setCells[1].xPos
local initZ = setCells[1].zPos
local endX = setCells[#setCells].xPos
local endZ = setCells[#setCells].zPos
local zWidth = math.abs(endZ - initZ)
local zMiddle = math.ceil(zWidth / 2 + initZ)
local xMiddle = math.ceil(zWidth / 2 + initX)
local down = endX - initX
local up, left, right = nil
if down < 0 then
down = Enum.WaterDirection.NegX
up = Enum.WaterDirection.X
left = Enum.WaterDirection.Z
right = Enum.WaterDirection.NegZ
else
down = Enum.WaterDirection.X
up = Enum.WaterDirection.NegX
left = Enum.WaterDirection.NegZ
right = Enum.WaterDirection.Z
end
if #setCells == 1 then
if not mouseCellPos or not lastCell then
return
end
local overallDirection = (mouseCellPos - lastCell).unit
if
math.abs(overallDirection.x) > math.abs(overallDirection.y)
and math.abs(overallDirection.x) > math.abs(overallDirection.z)
then
if overallDirection.x > 0 then
SetWaterCell(
c,
setCells[1].xPos,
setCells[1].yPos,
setCells[1].zPos,
mediumWaterForce,
Enum.WaterDirection.X
)
else
SetWaterCell(
c,
setCells[1].xPos,
setCells[1].yPos,
setCells[1].zPos,
mediumWaterForce,
Enum.WaterDirection.NegX
)
end
elseif
math.abs(overallDirection.z) > math.abs(overallDirection.y)
and math.abs(overallDirection.z) > math.abs(overallDirection.x)
then
if overallDirection.z > 0 then
SetWaterCell(
c,
setCells[1].xPos,
setCells[1].yPos,
setCells[1].zPos,
mediumWaterForce,
Enum.WaterDirection.Z
)
else
SetWaterCell(
c,
setCells[1].xPos,
setCells[1].yPos,
setCells[1].zPos,
mediumWaterForce,
Enum.WaterDirection.NegZ
)
end
elseif
math.abs(overallDirection.y) > math.abs(overallDirection.z)
and math.abs(overallDirection.y) > math.abs(overallDirection.x)
then
if overallDirection.y > 0 then
SetWaterCell(
c,
setCells[1].xPos,
setCells[1].yPos,
setCells[1].zPos,
mediumWaterForce,
Enum.WaterDirection.Y
)
else
SetWaterCell(
c,
setCells[1].xPos,
setCells[1].yPos,
setCells[1].zPos,
mediumWaterForce,
Enum.WaterDirection.NegY
)
end
end
else
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,
right
)
elseif setCells[i].zPos > zMiddle then
SetWaterCell(
c,
setCells[i].xPos,
setCells[i].yPos,
setCells[i].zPos,
mediumWaterForce,
left
)
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 cellPos = WorldToCellPreferSolid(c, mouseR.Hit.p)
local lowVec =
Vector3.new(cellPos.x - radius, cellPos.y - radius, cellPos.z - radius)
local highVec =
Vector3.new(cellPos.x + radius, cellPos.y + radius, cellPos.z + radius)
lowVec = CellCenterToWorld(c, lowVec.x, lowVec.y, lowVec.z)
highVec = CellCenterToWorld(c, highVec.x, highVec.y, highVec.z)
return Region3.new(
lowVec + Vector3.new(2, 2, 2),
highVec - Vector3.new(2, 2, 2)
)
end
function createSelection(mouseS, massSelection)
currSelectionUpdate, currSelectionDestroy = RbxUtil.SelectTerrainRegion(
calculateRegion(mouseS),
BrickColor.new "Lime green",
massSelection,
game.CoreGui
)
end
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
-- no dragging occured, lets set our water to be stagnant or be a waterfall
if lastCell and not lastLastCell then
local cellsToSet = paint(lastCell)
if directionIsDown(nil, lastCell) then
paintWaterfall(cellsToSet)
else
for i = 1, #cellsToSet do
SetWaterCell(
c,
cellsToSet[i].xPos,
cellsToSet[i].yPos,
cellsToSet[i].zPos,
Enum.WaterForce.None,
Enum.WaterDirection.NegX
)
end
end
return
end
if directionIsDown(lastLastCell, lastCell) then
local cellsToSet = paint(lastCell)
paintWaterfall(cellsToSet)
return
end
local overallDirection = (lastCell - lastLastCell).unit
local cellsToSet = paint(lastCell)
local absX, absY, absZ =
math.abs(overallDirection.X),
math.abs(overallDirection.Y),
math.abs(overallDirection.Z)
local direction
if absX > absY and absX > absZ then
if overallDirection.X > 0 then
direction = Enum.WaterDirection.X
else
direction = Enum.WaterDirection.NegX
end
elseif absY > absX and absY > absZ then
if overallDirection.Y > 0 then
direction = Enum.WaterDirection.Y
else
direction = Enum.WaterDirection.NegY
end
elseif absZ > absX and absZ > absY then
if overallDirection.Z > 0 then
direction = Enum.WaterDirection.Z
else
direction = Enum.WaterDirection.NegZ
end
end
if not direction then -- this should never be hit, but just in case
direction = Enum.WaterDirection.NegX
end
for i = 1, #cellsToSet do
SetWaterCell(
c,
cellsToSet[i].xPos,
cellsToSet[i].yPos,
cellsToSet[i].zPos,
mediumWaterForce,
direction
)
end
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
end
end
function mouseUp(_)
dragging = false
-- we need to fix directionality on last cell set (if water)
if currentMaterial == waterMaterial then
setPositionDirectionality()
end
game:GetService("ChangeHistoryService"):SetWaypoint "MaterialPaint"
lastLastCell = nil
lastCell = nil
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()
end
end
end
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 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
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
function destroySelection()
if currSelectionUpdate then
currSelectionUpdate = nil
end
if currSelectionDestroy then
currSelectionDestroy()
currSelectionDestroy = nil
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
end
end
return currCell
end
function interpolateOneDim(direction, currPos, goalPos, currCell)
if currPos ~= goalPos then
currCell = moveTowardsGoal(direction, currPos, goalPos, currCell)
paint(currCell)
end
return currCell
end
function paintBetweenPoints(lastCellP, newCell)
local currCell = lastCellP
while
currCell.X ~= newCell.X
or currCell.Z ~= newCell.Z
or currCell.Y ~= newCell.Y
do
currCell = interpolateOneDim("X", currCell.X, newCell.X, currCell)
currCell = interpolateOneDim("Z", currCell.Z, newCell.Z, currCell)
currCell = interpolateOneDim("Y", currCell.Y, newCell.Y, currCell)
end
end
On = function()
if not c then
return
end
this:Activate(true)
toolbarbutton:SetActive(true)
dragBar.Visible = true
on = true
end
Off = function()
toolbarbutton:SetActive(false)
destroySelection()
dragBar.Visible = false
on = false
end
------
--GUI-
------
--screengui
local g = Instance.new "ScreenGui"
g.Name = "MaterialPainterGui"
g.Parent = game:GetService "CoreGui"
dragBar, containerFrame, helpFrame, closeEvent = RbxGui.CreatePluginFrame(
"Material Brush",
UDim2.new(0, 163, 0, 285),
UDim2.new(0, 0, 0, 0),
false,
g
)
dragBar.Visible = false
-- End dragging if it goes over the gui frame.
containerFrame.MouseEnter:connect(function()
dragging = false
end)
dragBar.MouseEnter:connect(function()
dragging = false
end)
helpFrame.Size = UDim2.new(0, 200, 0, 250)
helpFrame.ZIndex = 3
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.Position = UDim2.new(0, 4, 0, 4)
textHelp.BackgroundTransparency = 1
textHelp.TextWrap = true
textHelp.ZIndex = 4
textHelp.Text =
[[Changes existing terrain blocks to the specified material. Simply hold the mouse down and drag to 'paint' the terrain (only cells inside the selection box will be affected).
Size:
The size of the brush we paint terrain with.
Brush Type:
The shape we paint terrain with inside of our selection box.
Material Selection:
The terrain material we will paint.]]
textHelp.Parent = helpFrame
closeEvent.Event:connect(function()
Off()
end)
terrainSelectorGui, terrainSelected, forceTerrainSelection =
RbxGui.CreateTerrainMaterialSelector(
UDim2.new(1, -10, 0, 184),
UDim2.new(0, 5, 1, -190)
)
terrainSelectorGui.Parent = containerFrame
terrainSelectorGui.BackgroundTransparency = 1
terrainSelectorGui.BorderSizePixel = 0
terrainSelected.Event:connect(function(newMaterial)
currentMaterial = newMaterial
end)
-- Purpose:
-- Retrive the size text to display for a given radius, where 1 == 1 block and 2 == 3 blocks, etc.
function SizeText(radiusT)
return "Size: " .. (((radiusT - 1) * 2) + 1)
end
--current radius display label
radiusLabel = Instance.new "TextLabel"
radiusLabel.Name = "RadiusLabel"
radiusLabel.Position = UDim2.new(0, 10, 0, 5)
radiusLabel.Size = UDim2.new(1, -3, 0, 14)
radiusLabel.Text = SizeText(radius)
radiusLabel.TextColor3 = Color3.new(0.95, 0.95, 0.95)
radiusLabel.Font = Enum.Font.ArialBold
radiusLabel.FontSize = Enum.FontSize.Size14
radiusLabel.BorderColor3 = Color3.new(0, 0, 0)
radiusLabel.BackgroundTransparency = 1
radiusLabel.TextXAlignment = Enum.TextXAlignment.Left
radiusLabel.Parent = containerFrame
--radius slider
local radSliderGui, radSliderPosition =
RbxGui.CreateSlider(6, 0, UDim2.new(0, 0, 0, 18))
radSliderGui.Size = UDim2.new(1, -2, 0, 20)
radSliderGui.Position = UDim2.new(0, 0, 0, 24)
radSliderGui.Parent = containerFrame
local radBar = radSliderGui:FindFirstChild "Bar"
radBar.Size = UDim2.new(1, -20, 0, 5)
radBar.Position = UDim2.new(0, 10, 0.5, -3)
radSliderPosition.Value = radius
radSliderPosition.Changed:connect(function()
radius = radSliderPosition.Value
radiusLabel.Text = SizeText(radius)
destroySelection()
end)
local brushTypeChanged = function(newBrush)
brushType = newBrush
end
-- brush type drop-down
local brushDropDown, forceSelection =
RbxGui.CreateDropDownMenu(brushTypes, brushTypeChanged)
forceSelection "Square"
brushDropDown.Size = UDim2.new(1, -10, 0.01, 25)
brushDropDown.Position = UDim2.new(0, 5, 0, 65)
brushDropDown.Parent = containerFrame
local brushLabel = radiusLabel:Clone()
brushLabel.Name = "BrushLabel"
brushLabel.Text = "Brush Type"
brushLabel.Position = UDim2.new(0, 10, 0, 50)
brushLabel.Parent = containerFrame
this.Deactivation:connect(function()
Off()
end)
--------------------------
--SUCCESSFUL LOAD MESSAGE-
--------------------------
loaded = true

View File

@ -0,0 +1,442 @@
while game == nil do
wait(1 / 30)
end
---------------
--PLUGIN SETUP-
---------------
local loaded = false
local on = false
local On, Off
local this = PluginManager():CreatePlugin()
this.Deactivation:connect(function()
Off()
end)
local toolbar = this:CreateToolbar "Terrain"
local toolbarbutton = toolbar:CreateButton(
"Stamper",
"Part Stamper - Toggle List (Shift + F)",
"stamp.png"
)
toolbarbutton.Click:connect(function()
if on then
Off()
elseif loaded then
On()
end
end)
game:WaitForChild "Workspace"
game.Workspace:WaitForChild "Terrain"
-----------------
--DEFAULT VALUES-
-----------------
local currStampGui
local currStampId
local recentsFrame
local waterTypeChangedEvent
local waterForceAndDirection = { "None", "NegX" }
local setPanelVisibility
local getPanelVisibility
local stampControl
local lastStampModel
local keyCon
-- ids of users we want to load sets in from
local userSetIds =
{ 11744447, 18881789, 18881808, 18881829, 18881853, 18881866 }
local recentButtonStack = {}
-- mouse management
local mouse
-- Libraries
local RbxStamper
local RbxGui
Spawn(function()
RbxGui = LoadLibrary "RbxGui"
RbxStamper = LoadLibrary "RbxStamper"
end)
local BaseUrl = game:GetService("ContentProvider").BaseUrl:lower()
-----------------------
--FUNCTION DEFINITIONS-
-----------------------
function getRbxGui()
if not RbxGui then
print "teh new rbxgui"
RbxGui = LoadLibrary "RbxGui"
end
return RbxGui
end
function getRbxStamper()
if not RbxStamper then
print "teh new stamper"
RbxStamper = LoadLibrary "RbxStamper"
end
return RbxStamper
end
function showLoadingDialog()
currStampGui.LoadingFrame.LoadingText:TweenPosition(
UDim2.new(0, 150, 0, 0),
Enum.EasingDirection.Out,
Enum.EasingStyle.Quad,
2,
true
)
currStampGui.LoadingFrame.Visible = true
end
function hideLoadingDialog()
currStampGui.LoadingFrame.LoadingText:TweenPosition(
UDim2.new(0, 0, 0, 0),
Enum.EasingDirection.Out,
Enum.EasingStyle.Quad,
0.1,
true
)
currStampGui.LoadingFrame.Visible = false
end
local partSelected = function(name, id, terrainShape)
if not id then
return
end
if not name then
return
end
currStampId = id
if stampControl then
stampControl.Destroy()
end
if stampCon then
stampCon:disconnect()
stampCon = nil
end
setPanelVisibility(false)
showLoadingDialog()
lastStampModel = getRbxStamper().GetStampModel(id, terrainShape)
updateRecentParts(name, id, terrainShape)
hideLoadingDialog()
if lastStampModel.Name == "MegaClusterCube" then
local clusterTag = lastStampModel:FindFirstChild "ClusterMaterial"
-- we are going to stamp water, send info to stamper about this
if
clusterTag
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]
end
end
setupStamper(lastStampModel, mouse)
end
function updateWaterInfo()
if stampControl then
stampControl.Destroy()
end
if stampCon then
stampCon:disconnect()
stampCon = nil
end
showLoadingDialog()
lastStampModel = getRbxStamper().GetStampModel(currStampId)
hideLoadingDialog()
if lastStampModel.Name == "MegaClusterCube" then
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]
end
end
setupStamper(lastStampModel, mouse)
end
local dialogClosed = function()
if lastStampModel then
if stampControl then
stampControl.Destroy()
end
setupStamper(lastStampModel, mouse)
end
end
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()
end
setupStamper(lastStampModel, mouse)
end
end
function handlePartShow()
if getPanelVisibility() then
partOff()
else
partOn()
end
end
On = function()
if not game.Workspace.Terrain then
return
end
if this then
this:Activate(true)
mouse = this:GetMouse()
end
if toolbarbutton then
toolbarbutton:SetActive(true)
end
if not currStampGui then -- first load, lets make the gui
createGui()
end
if setPanelVisibility then
setPanelVisibility(true)
end
if recentsFrame then
recentsFrame.Visible = true
end
if keyHandler then
keyCon = mouse.KeyDown:connect(keyHandler)
end
on = true
end
Off = function()
if toolbarbutton then
toolbarbutton:SetActive(false)
end
if stampControl then
stampControl.Destroy()
end
if keyCon then
keyCon:disconnect()
keyCon = nil
end
if currStampGui and currStampGui:FindFirstChild "WaterFrame" then
currStampGui.WaterFrame.Visible = false
end
if lastStampModel then
lastStampModel:Destroy()
end
if setPanelVisibility then
setPanelVisibility(false)
end
if recentsFrame then
recentsFrame.Visible = false
end
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 = game:GetService "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-
--------------------------
loaded = true

View File

@ -0,0 +1,905 @@
while game == nil do
wait(1 / 30)
end
---------------
--PLUGIN SETUP-
---------------
local loaded = false
local on = false
local On, Off
local this = PluginManager():CreatePlugin()
local mouse = this:GetMouse()
mouse.Button1Down:connect(function()
mouseDown(mouse)
end)
mouse.Button1Up:connect(function()
mouseUp(mouse)
end)
local toolbar = this:CreateToolbar "Terrain"
local toolbarbutton =
toolbar:CreateButton("Flood Fill", "Flood Fill", "floodFill.png")
toolbarbutton.Click:connect(function()
if on then
Off()
elseif loaded then
On()
end
end)
game:WaitForChild "Workspace"
game.Workspace:WaitForChild "Terrain"
-----------------------------
--LOCAL FUNCTION DEFINITIONS-
-----------------------------
local c = game.Workspace.Terrain
-- local WorldToCellPreferSolid = c.WorldToCellPreferSolid
local WorldToCellPreferEmpty = c.WorldToCellPreferEmpty
local CellCenterToWorld = c.CellCenterToWorld
local GetCell = c.GetCell
local SetCell = c.SetCell
-- local maxYExtents = c.MaxExtents.Max.Y
local emptyMaterial = Enum.CellMaterial.Empty
-- local waterMaterial = Enum.CellMaterial.Water
-- local floodFill
-----------------
--DEFAULT VALUES-
-----------------
local startCell
local processing = false
-- gui values
local screenGui
local dragBar, closeEvent, helpFrame --, lastCell
local progressBar, setProgressFunc, cancelEvent
local ConfirmationPopupObject
--- exposed values to user via gui
local currentMaterial = 1
-- load our libraries
local RbxGui = LoadLibrary "RbxGui"
-- local RbxUtil = LoadLibrary "RbxUtility"
game:GetService("ContentProvider")
:Preload "http://banland.xyz/asset/?id=82741829"
------------------------- OBJECT DEFINITIONS ---------------------
-- helper function for objects
local function getClosestColorToTerrainMaterial(terrainValue)
if terrainValue == 1 then
return BrickColor.new "Bright green"
elseif terrainValue == 2 then
return BrickColor.new "Bright yellow"
elseif terrainValue == 3 then
return BrickColor.new "Bright red"
elseif terrainValue == 4 then
return BrickColor.new "Sand red"
elseif terrainValue == 5 then
return BrickColor.new "Black"
elseif terrainValue == 6 then
return BrickColor.new "Dark stone grey"
elseif terrainValue == 7 then
return BrickColor.new "Sand blue"
elseif terrainValue == 8 then
return BrickColor.new "Deep orange"
elseif terrainValue == 9 then
return BrickColor.new "Dark orange"
elseif terrainValue == 10 then
return BrickColor.new "Reddish brown"
elseif terrainValue == 11 then
return BrickColor.new "Light orange"
elseif terrainValue == 12 then
return BrickColor.new "Light stone grey"
elseif terrainValue == 13 then
return BrickColor.new "Sand green"
elseif terrainValue == 14 then
return BrickColor.new "Medium stone grey"
elseif terrainValue == 15 then
return BrickColor.new "Really red"
elseif terrainValue == 16 then
return BrickColor.new "Really blue"
elseif terrainValue == 17 then
return BrickColor.new "Bright blue"
else
return BrickColor.new "Bright green"
end
end
-- Used to create a highlighter that follows the mouse.
-- It is a class mouse highlighter. To use, call MouseHighlighter.Create(mouse) where mouse is the mouse to track.
local MouseHighlighter = {}
MouseHighlighter.__index = MouseHighlighter
local startTween, stopTween
-- Create a mouse movement highlighter.
-- plugin - Plugin to get the mouse from.
function MouseHighlighter.Create(mouseUse)
local highlighter = {}
local mouseH = mouseUse
highlighter.OnClicked = nil
highlighter.mouseDown = false
-- Store the last point used to draw.
highlighter.lastUsedPoint = nil
-- Will hold a part the highlighter will be attached to. This will be moved where the mouse is.
highlighter.selectionPart = nil
-- 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 = 0.5
highlighter.selectionPart.Anchored = true
highlighter.selectionPart.Locked = true
highlighter.selectionPart.CanCollide = false
highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom
highlighter.selectionPart.Size = Vector3.new(4, 4, 4)
highlighter.selectionPart.BottomSurface = 0
highlighter.selectionPart.TopSurface = 0
highlighter.selectionPart.BrickColor =
getClosestColorToTerrainMaterial(currentMaterial)
highlighter.selectionPart.Parent = game.Workspace
local billboardGui = Instance.new "BillboardGui"
billboardGui.Size = UDim2.new(5, 0, 5, 0)
billboardGui.StudsOffset = Vector3.new(0, 2.5, 0)
billboardGui.Parent = highlighter.selectionPart
local imageLabel = Instance.new "ImageLabel"
imageLabel.BackgroundTransparency = 1
imageLabel.Size = UDim2.new(1, 0, 1, 0)
imageLabel.Position = UDim2.new(-0.35, 0, -0.5, 0)
imageLabel.Image = "http://banland.xyz/asset/?id=82741829"
imageLabel.Parent = billboardGui
local lastTweenChange
startTween = function()
local tweenDown, tweenUp
while tweenUp or tweenDown do
wait(0.2)
end
lastTweenChange = tick()
delay(0, function()
local thisTweenStamp = lastTweenChange
tweenDown = function()
if
imageLabel
and imageLabel:IsDescendantOf(game.Workspace)
and thisTweenStamp == lastTweenChange
then
imageLabel:TweenPosition(
UDim2.new(-0.35, 0, -0.5, 0),
Enum.EasingDirection.In,
Enum.EasingStyle.Quad,
0.4,
true,
tweenUp
)
end
end
tweenUp = function()
if
imageLabel
and imageLabel:IsDescendantOf(game.Workspace)
and thisTweenStamp == lastTweenChange
then
imageLabel:TweenPosition(
UDim2.new(-0.35, 0, -0.7, 0),
Enum.EasingDirection.Out,
Enum.EasingStyle.Sine,
0.4,
true,
tweenDown
)
end
end
tweenUp()
end)
end
stopTween = function()
lastTweenChange = tick()
end
mouseH.TargetFilter = highlighter.selectionPart
setmetatable(highlighter, MouseHighlighter)
-- Update where the highlighter is displayed.
-- position - Where to display the highlighter, in world space.
local function UpdatePosition(position)
if not position then
return
end
if not mouseH.Target then
stopTween()
highlighter.selectionPart.Parent = nil
return
end
if highlighter.selectionPart.Parent ~= game.Workspace then
highlighter.selectionPart.Parent = game.Workspace
startTween()
end
local vectorPos = Vector3.new(position.x, position.y, position.z)
local cellPos = WorldToCellPreferEmpty(c, vectorPos)
local regionToSelect
local cellMaterial = GetCell(c, cellPos.x, cellPos.y, cellPos.z)
local y = cellPos.y
-- only select empty cells
while cellMaterial ~= emptyMaterial do
y = y + 1
cellMaterial = GetCell(c, cellPos.x, y, cellPos.z)
end
cellPos = Vector3.new(cellPos.x, y, cellPos.z)
local lowVec = CellCenterToWorld(c, cellPos.x, cellPos.y - 1, cellPos.z)
local highVec =
CellCenterToWorld(c, cellPos.x, cellPos.y + 1, cellPos.z)
regionToSelect = Region3.new(lowVec, highVec)
highlighter.selectionPart.CFrame = regionToSelect.CFrame
if nil ~= highlighter.OnClicked and highlighter.mouseDown then
if nil == highlighter.lastUsedPoint then
highlighter.lastUsedPoint = WorldToCellPreferEmpty(
c,
Vector3.new(mouseH.Hit.x, mouseH.Hit.y, mouseH.Hit.z)
)
else
cellPos = WorldToCellPreferEmpty(
c,
Vector3.new(mouseH.Hit.x, mouseH.Hit.y, mouseH.Hit.z)
)
-- holdChange = cellPos - highlighter.lastUsedPoint
-- Require terrain.
if 0 == GetCell(c, cellPos.X, cellPos.Y, cellPos.Z).Value then
return
else
highlighter.lastUsedPoint = cellPos
end
end
end
end
-- Function to call when the mouse has moved. Updates where to display the highlighter.
function MouseMoved()
if on and not processing then
UpdatePosition(mouseH.Hit)
end
end
return highlighter
end
function MouseHighlighter:SetMaterial(newMaterial)
self.selectionPart.BrickColor =
getClosestColorToTerrainMaterial(newMaterial)
end
function MouseHighlighter:GetPosition()
local position = self.selectionPart.CFrame.p
return WorldToCellPreferEmpty(c, position)
end
-- Hide the highlighter.
function MouseHighlighter:DisablePreview()
stopTween()
self.selectionPart.Parent = nil
end
-- Show the highlighter.
function MouseHighlighter:EnablePreview()
self.selectionPart.Parent = game.Workspace
startTween()
end
-- Create the mouse movement highlighter.
local mouseHighlighter = MouseHighlighter.Create(mouse)
mouseHighlighter:DisablePreview()
-- Used to create a highlighter.
-- A highlighter is a open, rectuangular area displayed in 3D.
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.
-- textOffset - Offset to position text at.
-- confirmFunction - Function to run on confirmation.
-- declineFunction - Function to run when declining.
--
-- Return:
-- Value a table with confirmation gui and options.
function ConfirmationPopup.Create(
confirmText,
confirmSmallText,
confirmButtonText,
declineButtonText,
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.
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.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.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
-- Confirm
popup.confirmButton = CreateStandardButton(
"ConfirmButton",
UDim2.new(0.5, -120, 1, -50),
confirmButtonText,
confirmFunction,
popup.confirmationFrame
)
-- Decline
popup.declineButton = CreateStandardButton(
"DeclineButton",
UDim2.new(0.5, 0, 1, -50),
declineButtonText,
declineFunction,
popup.confirmationFrame
)
setmetatable(popup, ConfirmationPopup)
return popup
end
-- Clear the popup, free up assets.
function ConfirmationPopup:Clear()
if nil ~= self.confirmButton then
self.confirmButton.Parent = nil
end
if nil ~= self.declineButton then
self.declineButton.Parent = nil
end
if nil ~= self.confirmationFrame then
self.confirmationFrame.Parent = nil
end
if nil ~= self.confirmLabel then
self.confirmLabel.Parent = nil
end
self.confirmButton = nil
self.declineButton = nil
self.conformationFrame = nil
self.conformText = nil
end
-----------------------
--FUNCTION DEFINITIONS-
-----------------------
local floodFill = function(x, y, z)
LoadProgressBar "Processing"
breadthFill(x, y, z)
UnloadProgressBar()
game:GetService("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)
processing = true
-- Start the progress bar.
progressBar, setProgressFunc, cancelEvent = RbxGui.CreateLoadingFrame(text)
progressBar.Position = UDim2.new(0.5, -progressBar.Size.X.Offset / 2, 0, 15)
progressBar.Parent = screenGui
local loadingPercent = progressBar:FindFirstChild("LoadingPercent", true)
if loadingPercent then
loadingPercent.Parent = nil
end
local loadingBar = progressBar:FindFirstChild("LoadingBar", true)
if loadingBar then
loadingBar.Parent = nil
end
local cancelButton = progressBar:FindFirstChild("CancelButton", true)
if cancelButton then
cancelButton.Text = "Stop"
end
cancelEvent.Event:connect(function(_)
processing = false
-- 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 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
spinnerIcons[spinnerNum] = spinnerImage
spinnerNum = spinnerNum + 1
end
--Make it spin
delay(0, function()
local spinPos = 0
while processing do
local pos = 0
while pos < 8 do
if pos == spinPos or pos == ((spinPos + 1) % 8) then
spinnerIcons[pos + 1].Image =
"http://banland.xyz/asset/?id=45880668"
else
spinnerIcons[pos + 1].Image =
"http://banland.xyz/asset/?id=45880710"
end
pos = pos + 1
end
spinPos = (spinPos + 1) % 8
wait(0.2)
end
end)
end
-- Unload the progress bar.
function UnloadProgressBar()
processing = false
if progressBar then
progressBar.Parent = nil
progressBar = nil
end
if setProgressFunc then
setProgressFunc = nil
end
if cancelEvent then
cancelEvent = nil
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
)
if not 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
end
yDepthChecks = newYChecks
end
end
function doBreadthFill(x, y, z)
if getMaterial(x, y, z) ~= emptyMaterial then
return
end
local yDepthChecks = {}
local cellsToCheck = breadthFillHelper(x, y, z, yDepthChecks)
local count = 0
while cellsToCheck and #cellsToCheck > 0 do
local cellCheckQueue = {}
for i = 1, #cellsToCheck do
if not processing then
return
end
count = count + 1
local newCellsToCheck = breadthFillHelper(
cellsToCheck[i].xPos,
cellsToCheck[i].yPos,
cellsToCheck[i].zPos,
yDepthChecks
)
if newCellsToCheck and #newCellsToCheck > 0 then
for j = 1, #newCellsToCheck do
table.insert(cellCheckQueue, {
xPos = newCellsToCheck[j].xPos,
yPos = newCellsToCheck[j].yPos,
zPos = newCellsToCheck[j].zPos,
})
end
end
if count >= 3000 then
wait()
count = 0
end
end
cellsToCheck = cellCheckQueue
end
return yDepthChecks
end
function cellInTerrain(x, y, z)
if x < c.MaxExtents.Min.X or x >= c.MaxExtents.Max.X then
return false
end
if y < c.MaxExtents.Min.Y or y >= c.MaxExtents.Max.Y then
return false
end
if z < c.MaxExtents.Min.Z or z >= c.MaxExtents.Max.Z then
return false
end
return true
end
function breadthFillHelper(x, y, z, yDepthChecks)
-- first, lets try and fill this cell
if cellInTerrain(x, y, z) and getMaterial(x, y, z) == emptyMaterial then
SetCell(c, x, y, z, currentMaterial, 0, 0)
end
local cellsToFill = {}
if
cellInTerrain(x + 1, y, z)
and getMaterial(x + 1, y, z) == emptyMaterial
then
table.insert(cellsToFill, { xPos = x + 1, yPos = y, zPos = z })
end
if
cellInTerrain(x - 1, y, z)
and getMaterial(x - 1, y, z) == emptyMaterial
then
table.insert(cellsToFill, { xPos = x - 1, yPos = y, zPos = z })
end
if
cellInTerrain(x, y, z + 1)
and getMaterial(x, y, z + 1) == emptyMaterial
then
table.insert(cellsToFill, { xPos = x, yPos = y, zPos = z + 1 })
end
if
cellInTerrain(x, y, z - 1)
and getMaterial(x, y, z - 1) == emptyMaterial
then
table.insert(cellsToFill, { xPos = x, yPos = y, zPos = z - 1 })
end
if
cellInTerrain(x, y - 1, z)
and getMaterial(x, y - 1, z) == emptyMaterial
then
table.insert(yDepthChecks, { xPos = x, yPos = y - 1, zPos = z })
end
for i = 1, #cellsToFill do
SetCell(
c,
cellsToFill[i].xPos,
cellsToFill[i].yPos,
cellsToFill[i].zPos,
currentMaterial,
0,
0
)
end
if #cellsToFill <= 0 then
return nil
end
return cellsToFill
end
On = function()
if not c then
return
end
if this then
this:Activate(true)
end
if toolbarbutton then
toolbarbutton:SetActive(true)
end
if dragBar then
dragBar.Visible = true
end
on = true
end
Off = function()
if toolbarbutton then
toolbarbutton:SetActive(false)
end
if mouseHighlighter then
mouseHighlighter:DisablePreview()
end
if dragBar then
dragBar.Visible = false
end
on = false
end
mouseHighlighter.OnClicked = mouseUp
------
--GUI-
------
screenGui = Instance.new "ScreenGui"
screenGui.Name = "FloodFillGui"
screenGui.Parent = game:GetService "CoreGui"
local containerFrame
dragBar, containerFrame, helpFrame, closeEvent = RbxGui.CreatePluginFrame(
"Flood Fill",
UDim2.new(0, 163, 0, 195),
UDim2.new(0, 0, 0, 0),
false,
screenGui
)
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 = [[
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()
end)
local terrainSelectorGui, terrainSelected, forceTerrainMaterialSelection =
RbxGui.CreateTerrainMaterialSelector(
UDim2.new(1, -10, 0, 184),
UDim2.new(0, 5, 0, 5)
)
terrainSelectorGui.Parent = containerFrame
terrainSelectorGui.BackgroundTransparency = 1
terrainSelectorGui.BorderSizePixel = 0
terrainSelected.Event:connect(function(newMaterial)
currentMaterial = newMaterial
if mouseHighlighter then
mouseHighlighter:SetMaterial(currentMaterial)
end
end)
forceTerrainMaterialSelection(Enum.CellMaterial.Water)
this.Deactivation:connect(function()
Off()
end)
--------------------------
--SUCCESSFUL LOAD MESSAGE-
--------------------------
loaded = true