Compile terrain plugins from Luau
This commit is contained in:
parent
6f48e7f584
commit
759056c0da
40
compile.luau
40
compile.luau
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue