Initial commit
225
client/studio/CMakeLists.txt
Normal file
@@ -0,0 +1,225 @@
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
add_executable(Studio
|
||||
src/main.cpp
|
||||
resources/qt.qrc
|
||||
|
||||
src/AddEmulationDeviceDialog.cpp
|
||||
src/AddEmulationDeviceDialog.hpp
|
||||
src/AuthenticationHelper.cpp
|
||||
src/AuthenticationHelper.hpp
|
||||
src/AuthoringSettings.cpp
|
||||
src/AuthoringSettings.hpp
|
||||
src/AutoSaveDialog.cpp
|
||||
src/AutoSaveDialog.hpp
|
||||
src/CommonInsertWidget.cpp
|
||||
src/CommonInsertWidget.hpp
|
||||
src/CSGOperations.cpp
|
||||
src/CSGOperations.hpp
|
||||
src/DebuggerClient.cpp
|
||||
src/DebuggerClient.hpp
|
||||
src/DebuggerWidgets.cpp
|
||||
src/DebuggerWidgets.hpp
|
||||
src/DocDockManager.cpp
|
||||
src/DocDockManager.hpp
|
||||
src/DocDockWidget.cpp
|
||||
src/DocDockWidget.hpp
|
||||
src/DocTabManager.cpp
|
||||
src/DocTabManager.hpp
|
||||
src/ExternalHandlers.cpp
|
||||
src/ExternalHandlers.hpp
|
||||
src/FindDialog.cpp
|
||||
src/FindDialog.hpp
|
||||
src/FunctionMarshaller.cpp
|
||||
src/FunctionMarshaller.hpp
|
||||
src/GalleryItemColor.cpp
|
||||
src/GalleryItemColor.hpp
|
||||
src/IExternalHandler.hpp
|
||||
src/InsertObjectListWidget.cpp
|
||||
src/InsertObjectListWidget.hpp
|
||||
src/InsertObjectListWidgetItem.cpp
|
||||
src/InsertObjectListWidgetItem.hpp
|
||||
src/InsertServiceDialog.cpp
|
||||
src/InsertServiceDialog.hpp
|
||||
src/IRobloxDoc.hpp
|
||||
src/KeepSavedChangesDialog.cpp
|
||||
src/KeepSavedChangesDialog.hpp
|
||||
src/LogProvider.cpp
|
||||
src/LogProvider.hpp
|
||||
src/LuaSourceBuffer.cpp
|
||||
src/LuaSourceBuffer.hpp
|
||||
src/ManageEmulationDeviceDialog.cpp
|
||||
src/ManageEmulationDeviceDialog.hpp
|
||||
src/NameValueStoreManager.cpp
|
||||
src/NameValueStoreManager.hpp
|
||||
src/ogrewidget.cpp
|
||||
src/ogrewidget.hpp
|
||||
src/PluginAction.hpp
|
||||
src/PropertyItems.cpp
|
||||
src/PropertyItems.hpp
|
||||
src/QDirBoundProp.hpp
|
||||
src/QFontBoundProp.hpp
|
||||
src/QtUtilities.cpp
|
||||
src/QtUtilities.hpp
|
||||
src/RbxContent.cpp
|
||||
src/RbxContent.hpp
|
||||
src/RbxWorkspace.cpp
|
||||
src/RbxWorkspace.hpp
|
||||
src/RenderStatsItem.cpp
|
||||
src/RenderStatsItem.hpp
|
||||
src/Roblox.cpp
|
||||
src/Roblox.hpp
|
||||
src/RobloxApplicationManager.cpp
|
||||
src/RobloxApplicationManager.hpp
|
||||
src/RobloxBasicDoc.cpp
|
||||
src/RobloxBasicDoc.hpp
|
||||
src/RobloxBrowser.cpp
|
||||
src/RobloxBrowser.hpp
|
||||
src/RobloxCookieJar.cpp
|
||||
src/RobloxCookieJar.hpp
|
||||
src/RobloxCustomWidgets.cpp
|
||||
src/RobloxCustomWidgets.hpp
|
||||
src/RobloxDiagnosticsView.cpp
|
||||
src/RobloxDiagnosticsView.hpp
|
||||
src/RobloxDocManager.cpp
|
||||
src/RobloxDocManager.hpp
|
||||
src/RobloxFindWidget.cpp
|
||||
src/RobloxFindWidget.hpp
|
||||
src/RobloxGameExplorer.cpp
|
||||
src/RobloxGameExplorer.hpp
|
||||
src/RobloxIDEDoc.cpp
|
||||
src/RobloxIDEDoc.hpp
|
||||
src/RobloxInputConfigDialog.cpp
|
||||
src/RobloxInputConfigDialog.hpp
|
||||
src/RobloxKeyboardConfig.cpp
|
||||
src/RobloxKeyboardConfig.hpp
|
||||
src/RobloxMainWindow.cpp
|
||||
src/RobloxMainWindow.hpp
|
||||
src/RobloxMouseConfig.cpp
|
||||
src/RobloxMouseConfig.hpp
|
||||
src/RobloxNetworkAccessManager.cpp
|
||||
src/RobloxNetworkAccessManager.hpp
|
||||
src/RobloxNetworkReply.cpp
|
||||
src/RobloxNetworkReply.hpp
|
||||
src/RobloxObjectBrowserDoc.cpp
|
||||
src/RobloxObjectBrowserDoc.hpp
|
||||
src/RobloxPluginHost.cpp
|
||||
src/RobloxPluginHost.hpp
|
||||
src/RobloxPropertyWidget.cpp
|
||||
src/RobloxPropertyWidget.hpp
|
||||
src/RobloxQuickAccessConfig.cpp
|
||||
src/RobloxQuickAccessConfig.hpp
|
||||
src/RobloxReportView.cpp
|
||||
src/RobloxReportView.hpp
|
||||
src/RobloxRibbonMainWindow.cpp
|
||||
src/RobloxRibbonMainWindow.hpp
|
||||
src/RobloxSavingStateDialog.cpp
|
||||
src/RobloxSavingStateDialog.hpp
|
||||
src/RobloxScriptDoc.cpp
|
||||
src/RobloxScriptDoc.hpp
|
||||
src/RobloxScriptReview.cpp
|
||||
src/RobloxScriptReview.hpp
|
||||
src/RobloxSettings.cpp
|
||||
src/RobloxSettings.hpp
|
||||
src/RobloxSettingsDialog.cpp
|
||||
src/RobloxSettingsDialog.hpp
|
||||
src/RobloxStudioVerbs.cpp
|
||||
src/RobloxStudioVerbs.hpp
|
||||
src/RobloxStudioVersion.hpp
|
||||
src/RobloxTabWidget.hpp
|
||||
src/RobloxTaskScheduler.cpp
|
||||
src/RobloxTaskScheduler.hpp
|
||||
src/RobloxTextOutputWidget.cpp
|
||||
src/RobloxTextOutputWidget.hpp
|
||||
src/RobloxToolBox.cpp
|
||||
src/RobloxToolBox.hpp
|
||||
src/RobloxTreeWidget.cpp
|
||||
src/RobloxTreeWidget.hpp
|
||||
src/RobloxUser.cpp
|
||||
src/RobloxUser.hpp
|
||||
src/RobloxView.cpp
|
||||
src/RobloxView.hpp
|
||||
src/RobloxWebDoc.cpp
|
||||
src/RobloxWebDoc.hpp
|
||||
src/RobloxWebPage.cpp
|
||||
src/RobloxWebPage.hpp
|
||||
src/ScopedDebugTimer.hpp
|
||||
src/ScriptAnalysisWidget.cpp
|
||||
src/ScriptAnalysisWidget.hpp
|
||||
src/ScriptComboBox.cpp
|
||||
src/ScriptComboBox.hpp
|
||||
src/ScriptPickerDialog.cpp
|
||||
src/ScriptPickerDialog.hpp
|
||||
src/ScriptSideWidget.cpp
|
||||
src/ScriptSideWidget.hpp
|
||||
src/ScriptSyntaxHighlighter.cpp
|
||||
src/ScriptSyntaxHighlighter.hpp
|
||||
src/ScriptTextEditor.cpp
|
||||
src/ScriptTextEditor.hpp
|
||||
src/SelectionHighlightAdornable.cpp
|
||||
src/SelectionHighlightAdornable.hpp
|
||||
src/ShortcutHelpDialog.cpp
|
||||
src/ShortcutHelpDialog.hpp
|
||||
src/SplineEditor.cpp
|
||||
src/SplineEditor.hpp
|
||||
src/stdafx.cpp
|
||||
src/stdafx.hpp
|
||||
src/StudioDeviceEmulator.cpp
|
||||
src/StudioDeviceEmulator.hpp
|
||||
src/StudioIntellesense.cpp
|
||||
src/StudioIntellesense.hpp
|
||||
src/StudioMacUtilities.hpp
|
||||
src/StudioSerializerHelper.cpp
|
||||
src/StudioSerializerHelper.hpp
|
||||
src/StudioUtilities.cpp
|
||||
src/StudioUtilities.hpp
|
||||
src/UpdateUIManager.cpp
|
||||
src/UpdateUIManager.hpp
|
||||
src/UserInput.cpp
|
||||
src/UserInput.hpp
|
||||
src/UserInputUtil.cpp
|
||||
src/UserInputUtil.hpp
|
||||
src/WebDialog.cpp
|
||||
src/WebDialog.hpp
|
||||
${CLIENT_DIR}/common/AppSettings.cpp
|
||||
${CLIENT_DIR}/common/AppSettings.hpp
|
||||
${CLIENT_DIR}/common/GrayChatBar.hpp
|
||||
${CLIENT_DIR}/common/GrayChatBar.cpp
|
||||
${CLIENT_DIR}/common/SDLGameController.cpp
|
||||
${CLIENT_DIR}/common/SDLGameController.hpp
|
||||
)
|
||||
|
||||
target_link_libraries(Studio
|
||||
3D
|
||||
AppStudio
|
||||
Core
|
||||
RakNet
|
||||
BulletPhysics
|
||||
NetworkStudio
|
||||
Graphics
|
||||
QtitanRibbon
|
||||
)
|
||||
|
||||
if(AYA_OS_WINDOWS)
|
||||
target_sources(Studio PRIVATE
|
||||
resources/winrc.h
|
||||
resources/script.rc
|
||||
)
|
||||
|
||||
set_target_properties(Studio PROPERTIES WIN32_EXECUTABLE TRUE)
|
||||
windeployqt(Studio)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(Studio PRIVATE QT_NO_KEYWORDS)
|
||||
|
||||
target_include_directories(Studio PRIVATE src resources)
|
||||
set_target_properties(Studio PROPERTIES OUTPUT_NAME "Aya.Studio")
|
||||
|
||||
add_custom_command(TARGET Studio POST_BUILD
|
||||
COMMENT "Copying runtime files to build directory"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${RUNTIME_FILES}"
|
||||
"${CMAKE_RUNTIME_OUTPUT_DIRECTORY}"
|
||||
)
|
||||
852
client/studio/resources/BuiltInPlugins/PhysicsAnalyzer.rbxmx
Normal file
@@ -0,0 +1,852 @@
|
||||
<roblox xmlns:xmime="http://www.w3.org/2005/05/xmlmime"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="4">
|
||||
<External>null</External>
|
||||
<External>nil</External>
|
||||
<Item class="Model" referent="RBX3F9EDB07622E42D797B3F001F3E09746">
|
||||
<Properties>
|
||||
<CoordinateFrame name="ModelInPrimary">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
<Z>0</Z>
|
||||
<R00>1</R00>
|
||||
<R01>0</R01>
|
||||
<R02>0</R02>
|
||||
<R10>0</R10>
|
||||
<R11>1</R11>
|
||||
<R12>0</R12>
|
||||
<R20>0</R20>
|
||||
<R21>0</R21>
|
||||
<R22>1</R22>
|
||||
</CoordinateFrame>
|
||||
<string name="Name">PhysicsAnalyzerPlugin</string>
|
||||
<Ref name="PrimaryPart">null</Ref>
|
||||
</Properties>
|
||||
<Item class="Script" referent="RBXB4C77FAE3AAF45D291F682F80C833828">
|
||||
<Properties>
|
||||
<bool name="Disabled">false</bool>
|
||||
<Content name="LinkedSource">
|
||||
<null></null>
|
||||
</Content>
|
||||
<string name="Name">PhysicsAnalyzerScript</string>
|
||||
<ProtectedString name="Source"><![CDATA[-- Detector will send a signal with an int parameter, this is the number of problems detected
|
||||
-- This script can query for a list of parts for each problem
|
||||
-- The plugin is responsible for:
|
||||
-- 1) Pausing the game
|
||||
-- 2) Highlighting the parts
|
||||
-- 3) Selecting the parts in the Explorer
|
||||
-- 4) Focus on the parts in a group
|
||||
-- 5) Providing an interface for skipping to next issue
|
||||
|
||||
-----------
|
||||
--MODULES--
|
||||
-----------
|
||||
|
||||
local Extent = require(script.Parent.Extent)
|
||||
local Camera = require(script.Parent.Camera)
|
||||
|
||||
-------
|
||||
--GUI--
|
||||
-------
|
||||
|
||||
local PhysicsAnalyzerGui = script.Parent.PhysicsAnalyzerGui
|
||||
local MainFrame = PhysicsAnalyzerGui.MainFrame
|
||||
|
||||
local NumberFound = MainFrame.NumberFound
|
||||
local CurrentStatus = MainFrame.CurrentStatus
|
||||
local RunButton = MainFrame.RunButton
|
||||
|
||||
local ScrollingFrame = MainFrame.ScrollingFrame
|
||||
local Canvas = ScrollingFrame.Canvas
|
||||
local ItemTemplate = MainFrame.ItemTemplate
|
||||
|
||||
local HelpButton = MainFrame.HelpButton
|
||||
|
||||
------------
|
||||
--SERVICES--
|
||||
------------
|
||||
local SelectionService = game:GetService("Selection")
|
||||
local RunService = game:GetService("RunService")
|
||||
|
||||
PhysicsAnalyzerGui.Parent = game:GetService("CoreGui")
|
||||
|
||||
-----------
|
||||
--SIGNALS--
|
||||
-----------
|
||||
|
||||
local PhysicsAnalyzerIssuesSignal = nil
|
||||
local RunServiceSteppedSignal = nil
|
||||
local RunButtonInputSignal = nil
|
||||
local HelpButtonSignal = nil
|
||||
|
||||
-------------
|
||||
--VARIABLES--
|
||||
-------------
|
||||
|
||||
local previousIssueCount = 0;
|
||||
local issues = {}
|
||||
|
||||
-------------
|
||||
--FUNCTIONS--
|
||||
-------------
|
||||
|
||||
local function clearAllIssues()
|
||||
for i = 1, #issues do
|
||||
issues[i][3]:disconnect()
|
||||
issues[i][2]:Destroy()
|
||||
end
|
||||
|
||||
issues = {}
|
||||
|
||||
NumberFound.Text = ""
|
||||
end
|
||||
|
||||
local function addIssue(issue)
|
||||
|
||||
-- local label = "Issue " .. #issues .. (issue[1] and (": " .. issue[1].Name ..
|
||||
-- (issue[2] and " / " .. issue[2].Name or "")) or (""))
|
||||
|
||||
local label = "Issue " .. #issues .. ": "
|
||||
|
||||
for j=1, #issue do
|
||||
if( j > 1 ) then
|
||||
label = label .. " / "
|
||||
end
|
||||
label = label .. issue[j].Name
|
||||
end
|
||||
|
||||
local item = ItemTemplate:Clone()
|
||||
item.Parent = Canvas
|
||||
item.Position = UDim2.new(0, 0, 0, 21 * #issues)
|
||||
item.Text = label
|
||||
|
||||
local itemConnection = item.InputBegan:connect(function(evt)
|
||||
if evt.UserInputType == Enum.UserInputType.MouseButton1 then
|
||||
moveAndSelectParts(issue)
|
||||
end
|
||||
end)
|
||||
|
||||
item.Visible = true
|
||||
|
||||
issues[#issues + 1] = {issue, item, itemConnection}
|
||||
|
||||
ScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, #issues * 21)
|
||||
if (ScrollingFrame.CanvasSize.Y.Offset > ScrollingFrame.AbsoluteSize.Y) then
|
||||
Canvas.Size = UDim2.new(1, -16, 1, 0)
|
||||
else
|
||||
Canvas.Size = UDim2.new(1, 0, 1, 0)
|
||||
end
|
||||
|
||||
previousIssueCount = #issues;
|
||||
end
|
||||
|
||||
function moveAndSelectParts(parts)
|
||||
game.Selection:Set(parts)
|
||||
|
||||
local extents = Extent.getExtents(parts)
|
||||
Camera.zoomToExtents(extents)
|
||||
end
|
||||
|
||||
local function gotIssues(count)
|
||||
MainFrame.Size = UDim2.new(0, 200, 0, 200)
|
||||
ScrollingFrame.Visible = true
|
||||
|
||||
if (count == previousIssueCount) then
|
||||
return
|
||||
end
|
||||
|
||||
clearAllIssues()
|
||||
RunService:Pause()
|
||||
game.Workspace:SetPhysicsAnalyzerBreakOnIssue(true)
|
||||
|
||||
--Updating GUI
|
||||
NumberFound.Text = count .. " overconstraints detected"
|
||||
RunButton.Visible = true
|
||||
CurrentStatus.Text = "Paused"
|
||||
|
||||
for i = 0, count - 1 do
|
||||
local issue = game.Workspace:GetPhysicsAnalyzerIssue(i)
|
||||
addIssue(issue)
|
||||
end
|
||||
|
||||
game.Workspace:SetPhysicsAnalyzerBreakOnIssue(false)
|
||||
end
|
||||
|
||||
local function connectEvents()
|
||||
RunServiceSteppedSignal = RunService.Stepped:connect(function()
|
||||
clearAllIssues()
|
||||
CurrentStatus.Text = "Running"
|
||||
end)
|
||||
|
||||
RunButtonInputSignal = RunButton.InputBegan:connect(function(evt)
|
||||
if (evt.UserInputType == Enum.UserInputType.MouseButton1) then
|
||||
RunService:Run()
|
||||
RunButton.Visible = false
|
||||
end
|
||||
end)
|
||||
|
||||
HelpButtonSignal = HelpButton.InputBegan:connect(function(evt)
|
||||
if (evt.UserInputType == Enum.UserInputType.MouseButton1) then
|
||||
plugin:OpenWikiPage("Physics_Analyzer")
|
||||
end
|
||||
end)
|
||||
|
||||
PhysicsAnalyzerIssuesSignal = game.Workspace.PhysicsAnalyzerIssuesFound:connect(gotIssues)
|
||||
end
|
||||
|
||||
local function disconnectEvents()
|
||||
if PhysicsAnalyzerIssuesSignal then
|
||||
PhysicsAnalyzerIssuesSignal:disconnect()
|
||||
end
|
||||
|
||||
if RunServiceSteppedSignal then
|
||||
RunServiceSteppedSignal:disconnect()
|
||||
end
|
||||
|
||||
if RunButtonInputSignal then
|
||||
RunButtonInputSignal:disconnect()
|
||||
end
|
||||
|
||||
if HelpButtonSignal then
|
||||
HelpButtonSignal:disconnect()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function physicsAnalyzerEnabled(enabled)
|
||||
MainFrame.Visible = enabled
|
||||
|
||||
if enabled then
|
||||
connectEvents()
|
||||
else
|
||||
disconnectEvents()
|
||||
end
|
||||
end
|
||||
|
||||
----------
|
||||
--EVENTS--
|
||||
----------
|
||||
|
||||
settings().Physics.Changed:connect(function(itemChanged)
|
||||
if itemChanged == "PhysicsAnalyzerEnabled" then
|
||||
physicsAnalyzerEnabled(settings().Physics.PhysicsAnalyzerEnabled)
|
||||
end
|
||||
end)
|
||||
|
||||
physicsAnalyzerEnabled(settings().Physics.PhysicsAnalyzerEnabled)
|
||||
|
||||
|
||||
|
||||
]]> </ProtectedString>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ModuleScript" referent="RBXDB111B99CBE14ED8A5E9B4B20FA6CCA3">
|
||||
<Properties>
|
||||
<Content name="LinkedSource">
|
||||
<null></null>
|
||||
</Content>
|
||||
<string name="Name">Extent</string>
|
||||
<ProtectedString name="Source"><![CDATA[------------------
|
||||
--MODULE EXTENTS--
|
||||
------------------
|
||||
|
||||
--[[
|
||||
This modulescript is used for calculating the extents of a group of parts
|
||||
--]]
|
||||
|
||||
-------------
|
||||
--FUNCTIONS--
|
||||
-------------
|
||||
|
||||
local function createExtents()
|
||||
local extents = {}
|
||||
extents.mt = {}
|
||||
setmetatable(extents, extents.mt)
|
||||
|
||||
extents.min = Vector3.new(1,1,1) * math.huge
|
||||
extents.max = Vector3.new(1,1,1) * -math.huge
|
||||
|
||||
extents.mt.__index = function (t, k)
|
||||
|
||||
if k == "center" then
|
||||
return (extents.min + extents.max) * 0.5
|
||||
end
|
||||
|
||||
if k == "addPoint" then
|
||||
return function (point)
|
||||
if point.x < extents.min.x then extents.min = Vector3.new(point.x, extents.min.y, extents.min.z) end
|
||||
if point.y < extents.min.y then extents.min = Vector3.new(extents.min.x, point.y, extents.min.z) end
|
||||
if point.z < extents.min.z then extents.min = Vector3.new(extents.min.x, extents.min.y, point.z) end
|
||||
if point.x > extents.max.x then extents.max = Vector3.new(point.x, extents.max.y, extents.max.z) end
|
||||
if point.y > extents.max.y then extents.max = Vector3.new(extents.max.x, point.y, extents.max.z) end
|
||||
if point.z > extents.max.z then extents.max = Vector3.new(extents.max.x, extents.max.y, point.z) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return extents
|
||||
end
|
||||
|
||||
local function addToExtents(extents, part)
|
||||
for i = 0, 7 do
|
||||
local corner = (Vector3.new(math.floor(i / 4), math.floor(i / 2) % 2, i % 2) * 2) - Vector3.new(1, 1, 1)
|
||||
local worldPosition = part.CFrame:pointToWorldSpace(corner * part.Size)
|
||||
extents.addPoint(worldPosition)
|
||||
end
|
||||
return extents
|
||||
end
|
||||
|
||||
local function getExtents(parts)
|
||||
|
||||
local extents = createExtents()
|
||||
|
||||
for i = 1, #parts do
|
||||
if parts[i]:IsA("BasePart") then
|
||||
extents = addToExtents(extents, parts[i])
|
||||
end
|
||||
end
|
||||
|
||||
return extents
|
||||
|
||||
end
|
||||
|
||||
-----------------
|
||||
--ENCAPSULATION--
|
||||
-----------------
|
||||
|
||||
local module = {}
|
||||
module.getExtents = getExtents
|
||||
return module
|
||||
]]> </ProtectedString>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ModuleScript" referent="RBXF7D9C89485434B36B2422E149374F5DF">
|
||||
<Properties>
|
||||
<Content name="LinkedSource">
|
||||
<null></null>
|
||||
</Content>
|
||||
<string name="Name">Camera</string>
|
||||
<ProtectedString name="Source"><![CDATA[-----------------
|
||||
--MODULE CAMERA--
|
||||
-----------------
|
||||
|
||||
--[[
|
||||
This modulescript is used for zooming the camera to given extents
|
||||
--]]
|
||||
|
||||
-------------
|
||||
--FUNCTIONS--
|
||||
-------------
|
||||
|
||||
local distanceFromRadius = 3
|
||||
|
||||
local function zoomToExtents(extents)
|
||||
|
||||
local camera = game.Workspace.CurrentCamera
|
||||
|
||||
local center = extents.center
|
||||
local radius = (extents.min - center).magnitude
|
||||
|
||||
camera.CameraType = Enum.CameraType.Fixed
|
||||
|
||||
local rotation = camera.CoordinateFrame - camera.CoordinateFrame.p
|
||||
local position = center - (rotation.lookVector * (radius + distanceFromRadius))
|
||||
|
||||
camera.CoordinateFrame = rotation + position
|
||||
camera.Focus = CFrame.new(center)
|
||||
end
|
||||
|
||||
-----------------
|
||||
--ENCAPSULATION--
|
||||
-----------------
|
||||
|
||||
local module = {}
|
||||
module.zoomToExtents = zoomToExtents
|
||||
return module
|
||||
]]> </ProtectedString>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ScreenGui" referent="RBXC61727E732814E20A690ED394899F571">
|
||||
<Properties>
|
||||
<string name="Name">PhysicsAnalyzerGui</string>
|
||||
</Properties>
|
||||
<Item class="Frame" referent="RBX9C1A115B17C64C2DA4C550472FC4C2FC">
|
||||
<Properties>
|
||||
<bool name="Active">true</bool>
|
||||
<Color3 name="BackgroundColor3">4294967295</Color3>
|
||||
<float name="BackgroundTransparency">0</float>
|
||||
<Color3 name="BorderColor3">4279970357</Color3>
|
||||
<int name="BorderSizePixel">1</int>
|
||||
<bool name="ClipsDescendants">false</bool>
|
||||
<bool name="Draggable">false</bool>
|
||||
<string name="Name">MainFrame</string>
|
||||
<Ref name="NextSelectionDown">null</Ref>
|
||||
<Ref name="NextSelectionLeft">null</Ref>
|
||||
<Ref name="NextSelectionRight">null</Ref>
|
||||
<Ref name="NextSelectionUp">null</Ref>
|
||||
<UDim2 name="Position">
|
||||
<XS>0</XS>
|
||||
<XO>0</XO>
|
||||
<YS>0</YS>
|
||||
<YO>0</YO>
|
||||
</UDim2>
|
||||
<float name="Rotation">0</float>
|
||||
<bool name="Selectable">false</bool>
|
||||
<Ref name="SelectionImageObject">null</Ref>
|
||||
<UDim2 name="Size">
|
||||
<XS>0</XS>
|
||||
<XO>200</XO>
|
||||
<YS>0</YS>
|
||||
<YO>70</YO>
|
||||
</UDim2>
|
||||
<token name="SizeConstraint">0</token>
|
||||
<token name="Style">6</token>
|
||||
<bool name="Visible">true</bool>
|
||||
<int name="ZIndex">1</int>
|
||||
</Properties>
|
||||
<Item class="TextLabel" referent="RBX21D1D53505FA4E5EA81BC1E4FEE93833">
|
||||
<Properties>
|
||||
<bool name="Active">false</bool>
|
||||
<Color3 name="BackgroundColor3">4294967295</Color3>
|
||||
<float name="BackgroundTransparency">0.75</float>
|
||||
<Color3 name="BorderColor3">4279970357</Color3>
|
||||
<int name="BorderSizePixel">0</int>
|
||||
<bool name="ClipsDescendants">false</bool>
|
||||
<bool name="Draggable">false</bool>
|
||||
<token name="Font">4</token>
|
||||
<token name="FontSize">6</token>
|
||||
<string name="Name">TitleLabel</string>
|
||||
<Ref name="NextSelectionDown">null</Ref>
|
||||
<Ref name="NextSelectionLeft">null</Ref>
|
||||
<Ref name="NextSelectionRight">null</Ref>
|
||||
<Ref name="NextSelectionUp">null</Ref>
|
||||
<UDim2 name="Position">
|
||||
<XS>0</XS>
|
||||
<XO>0</XO>
|
||||
<YS>0</YS>
|
||||
<YO>0</YO>
|
||||
</UDim2>
|
||||
<float name="Rotation">0</float>
|
||||
<bool name="Selectable">false</bool>
|
||||
<Ref name="SelectionImageObject">null</Ref>
|
||||
<UDim2 name="Size">
|
||||
<XS>1</XS>
|
||||
<XO>0</XO>
|
||||
<YS>0</YS>
|
||||
<YO>25</YO>
|
||||
</UDim2>
|
||||
<token name="SizeConstraint">0</token>
|
||||
<string name="Text">Physics Analyzer</string>
|
||||
<Color3 name="TextColor3">4294967295</Color3>
|
||||
<bool name="TextScaled">false</bool>
|
||||
<Color3 name="TextStrokeColor3">4278190080</Color3>
|
||||
<float name="TextStrokeTransparency">1</float>
|
||||
<float name="TextTransparency">0</float>
|
||||
<bool name="TextWrapped">false</bool>
|
||||
<token name="TextXAlignment">2</token>
|
||||
<token name="TextYAlignment">1</token>
|
||||
<bool name="Visible">true</bool>
|
||||
<int name="ZIndex">1</int>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ScrollingFrame" referent="RBX903E924BF45441B8948310D28BDE9938">
|
||||
<Properties>
|
||||
<bool name="Active">false</bool>
|
||||
<Color3 name="BackgroundColor3">4294967295</Color3>
|
||||
<float name="BackgroundTransparency">1</float>
|
||||
<Color3 name="BorderColor3">4279970357</Color3>
|
||||
<int name="BorderSizePixel">0</int>
|
||||
<Content name="BottomImage">
|
||||
<url>ayaasset://textures/ui/studs.png</url>
|
||||
</Content>
|
||||
<Vector2 name="CanvasPosition">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
</Vector2>
|
||||
<UDim2 name="CanvasSize">
|
||||
<XS>0</XS>
|
||||
<XO>0</XO>
|
||||
<YS>0</YS>
|
||||
<YO>100</YO>
|
||||
</UDim2>
|
||||
<bool name="ClipsDescendants">true</bool>
|
||||
<bool name="Draggable">false</bool>
|
||||
<Content name="MidImage">
|
||||
<url>ayaasset://textures/ui/smooth.png</url>
|
||||
</Content>
|
||||
<string name="Name">ScrollingFrame</string>
|
||||
<Ref name="NextSelectionDown">null</Ref>
|
||||
<Ref name="NextSelectionLeft">null</Ref>
|
||||
<Ref name="NextSelectionRight">null</Ref>
|
||||
<Ref name="NextSelectionUp">null</Ref>
|
||||
<UDim2 name="Position">
|
||||
<XS>0</XS>
|
||||
<XO>0</XO>
|
||||
<YS>0</YS>
|
||||
<YO>75</YO>
|
||||
</UDim2>
|
||||
<float name="Rotation">0</float>
|
||||
<int name="ScrollBarThickness">12</int>
|
||||
<bool name="ScrollingEnabled">true</bool>
|
||||
<bool name="Selectable">true</bool>
|
||||
<Ref name="SelectionImageObject">null</Ref>
|
||||
<UDim2 name="Size">
|
||||
<XS>1</XS>
|
||||
<XO>0</XO>
|
||||
<YS>1</YS>
|
||||
<YO>-75</YO>
|
||||
</UDim2>
|
||||
<token name="SizeConstraint">0</token>
|
||||
<Content name="TopImage">
|
||||
<url>ayaasset://textures/ui/studs.png</url>
|
||||
</Content>
|
||||
<bool name="Visible">false</bool>
|
||||
<int name="ZIndex">1</int>
|
||||
</Properties>
|
||||
<Item class="Frame" referent="RBX0748620B01764D05897BE23D03333C45">
|
||||
<Properties>
|
||||
<bool name="Active">false</bool>
|
||||
<Color3 name="BackgroundColor3">4294967295</Color3>
|
||||
<float name="BackgroundTransparency">1</float>
|
||||
<Color3 name="BorderColor3">4279970357</Color3>
|
||||
<int name="BorderSizePixel">0</int>
|
||||
<bool name="ClipsDescendants">true</bool>
|
||||
<bool name="Draggable">false</bool>
|
||||
<string name="Name">Canvas</string>
|
||||
<Ref name="NextSelectionDown">null</Ref>
|
||||
<Ref name="NextSelectionLeft">null</Ref>
|
||||
<Ref name="NextSelectionRight">null</Ref>
|
||||
<Ref name="NextSelectionUp">null</Ref>
|
||||
<UDim2 name="Position">
|
||||
<XS>0</XS>
|
||||
<XO>0</XO>
|
||||
<YS>0</YS>
|
||||
<YO>0</YO>
|
||||
</UDim2>
|
||||
<float name="Rotation">0</float>
|
||||
<bool name="Selectable">false</bool>
|
||||
<Ref name="SelectionImageObject">null</Ref>
|
||||
<UDim2 name="Size">
|
||||
<XS>1</XS>
|
||||
<XO>-16</XO>
|
||||
<YS>1</YS>
|
||||
<YO>0</YO>
|
||||
</UDim2>
|
||||
<token name="SizeConstraint">0</token>
|
||||
<token name="Style">0</token>
|
||||
<bool name="Visible">true</bool>
|
||||
<int name="ZIndex">1</int>
|
||||
</Properties>
|
||||
</Item>
|
||||
</Item>
|
||||
<Item class="TextButton" referent="RBX5909EC7335A24E6E8BD4123F5940CB4A">
|
||||
<Properties>
|
||||
<bool name="Active">true</bool>
|
||||
<bool name="AutoButtonColor">true</bool>
|
||||
<Color3 name="BackgroundColor3">4294967295</Color3>
|
||||
<float name="BackgroundTransparency">0.899999976</float>
|
||||
<Color3 name="BorderColor3">4279970357</Color3>
|
||||
<int name="BorderSizePixel">0</int>
|
||||
<bool name="ClipsDescendants">false</bool>
|
||||
<bool name="Draggable">false</bool>
|
||||
<token name="Font">3</token>
|
||||
<token name="FontSize">5</token>
|
||||
<bool name="Modal">false</bool>
|
||||
<string name="Name">ItemTemplate</string>
|
||||
<Ref name="NextSelectionDown">null</Ref>
|
||||
<Ref name="NextSelectionLeft">null</Ref>
|
||||
<Ref name="NextSelectionRight">null</Ref>
|
||||
<Ref name="NextSelectionUp">null</Ref>
|
||||
<UDim2 name="Position">
|
||||
<XS>0</XS>
|
||||
<XO>0</XO>
|
||||
<YS>0</YS>
|
||||
<YO>0</YO>
|
||||
</UDim2>
|
||||
<float name="Rotation">0</float>
|
||||
<bool name="Selectable">true</bool>
|
||||
<bool name="Selected">false</bool>
|
||||
<Ref name="SelectionImageObject">null</Ref>
|
||||
<UDim2 name="Size">
|
||||
<XS>1</XS>
|
||||
<XO>0</XO>
|
||||
<YS>0</YS>
|
||||
<YO>20</YO>
|
||||
</UDim2>
|
||||
<token name="SizeConstraint">0</token>
|
||||
<token name="Style">0</token>
|
||||
<string name="Text">Problem 0</string>
|
||||
<Color3 name="TextColor3">4294967295</Color3>
|
||||
<bool name="TextScaled">false</bool>
|
||||
<Color3 name="TextStrokeColor3">4278190080</Color3>
|
||||
<float name="TextStrokeTransparency">1</float>
|
||||
<float name="TextTransparency">0</float>
|
||||
<bool name="TextWrapped">false</bool>
|
||||
<token name="TextXAlignment">2</token>
|
||||
<token name="TextYAlignment">1</token>
|
||||
<bool name="Visible">false</bool>
|
||||
<int name="ZIndex">1</int>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="TextLabel" referent="RBXCDD1A7B9C4994E1B9948AF615DD254A1">
|
||||
<Properties>
|
||||
<bool name="Active">false</bool>
|
||||
<Color3 name="BackgroundColor3">4294967295</Color3>
|
||||
<float name="BackgroundTransparency">1</float>
|
||||
<Color3 name="BorderColor3">4279970357</Color3>
|
||||
<int name="BorderSizePixel">0</int>
|
||||
<bool name="ClipsDescendants">false</bool>
|
||||
<bool name="Draggable">false</bool>
|
||||
<token name="Font">4</token>
|
||||
<token name="FontSize">5</token>
|
||||
<string name="Name">Status</string>
|
||||
<Ref name="NextSelectionDown">null</Ref>
|
||||
<Ref name="NextSelectionLeft">null</Ref>
|
||||
<Ref name="NextSelectionRight">null</Ref>
|
||||
<Ref name="NextSelectionUp">null</Ref>
|
||||
<UDim2 name="Position">
|
||||
<XS>0</XS>
|
||||
<XO>0</XO>
|
||||
<YS>0</YS>
|
||||
<YO>30</YO>
|
||||
</UDim2>
|
||||
<float name="Rotation">0</float>
|
||||
<bool name="Selectable">false</bool>
|
||||
<Ref name="SelectionImageObject">null</Ref>
|
||||
<UDim2 name="Size">
|
||||
<XS>0</XS>
|
||||
<XO>50</XO>
|
||||
<YS>0</YS>
|
||||
<YO>20</YO>
|
||||
</UDim2>
|
||||
<token name="SizeConstraint">0</token>
|
||||
<string name="Text">Status:</string>
|
||||
<Color3 name="TextColor3">4294967295</Color3>
|
||||
<bool name="TextScaled">false</bool>
|
||||
<Color3 name="TextStrokeColor3">4278190080</Color3>
|
||||
<float name="TextStrokeTransparency">1</float>
|
||||
<float name="TextTransparency">0</float>
|
||||
<bool name="TextWrapped">false</bool>
|
||||
<token name="TextXAlignment">2</token>
|
||||
<token name="TextYAlignment">1</token>
|
||||
<bool name="Visible">true</bool>
|
||||
<int name="ZIndex">1</int>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="TextLabel" referent="RBX9B7E5EB7F2C64FF4AEE9FD0D4EFACF49">
|
||||
<Properties>
|
||||
<bool name="Active">false</bool>
|
||||
<Color3 name="BackgroundColor3">4294967295</Color3>
|
||||
<float name="BackgroundTransparency">1</float>
|
||||
<Color3 name="BorderColor3">4279970357</Color3>
|
||||
<int name="BorderSizePixel">0</int>
|
||||
<bool name="ClipsDescendants">false</bool>
|
||||
<bool name="Draggable">false</bool>
|
||||
<token name="Font">3</token>
|
||||
<token name="FontSize">5</token>
|
||||
<string name="Name">CurrentStatus</string>
|
||||
<Ref name="NextSelectionDown">null</Ref>
|
||||
<Ref name="NextSelectionLeft">null</Ref>
|
||||
<Ref name="NextSelectionRight">null</Ref>
|
||||
<Ref name="NextSelectionUp">null</Ref>
|
||||
<UDim2 name="Position">
|
||||
<XS>0</XS>
|
||||
<XO>47</XO>
|
||||
<YS>0</YS>
|
||||
<YO>31</YO>
|
||||
</UDim2>
|
||||
<float name="Rotation">0</float>
|
||||
<bool name="Selectable">false</bool>
|
||||
<Ref name="SelectionImageObject">null</Ref>
|
||||
<UDim2 name="Size">
|
||||
<XS>0</XS>
|
||||
<XO>50</XO>
|
||||
<YS>0</YS>
|
||||
<YO>20</YO>
|
||||
</UDim2>
|
||||
<token name="SizeConstraint">0</token>
|
||||
<string name="Text">Ready</string>
|
||||
<Color3 name="TextColor3">4294967295</Color3>
|
||||
<bool name="TextScaled">false</bool>
|
||||
<Color3 name="TextStrokeColor3">4278190080</Color3>
|
||||
<float name="TextStrokeTransparency">1</float>
|
||||
<float name="TextTransparency">0</float>
|
||||
<bool name="TextWrapped">false</bool>
|
||||
<token name="TextXAlignment">0</token>
|
||||
<token name="TextYAlignment">1</token>
|
||||
<bool name="Visible">true</bool>
|
||||
<int name="ZIndex">1</int>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="TextLabel" referent="RBXB4BD17ABC60E45D2A4F98110C999CEA9">
|
||||
<Properties>
|
||||
<bool name="Active">false</bool>
|
||||
<Color3 name="BackgroundColor3">4294967295</Color3>
|
||||
<float name="BackgroundTransparency">1</float>
|
||||
<Color3 name="BorderColor3">4279970357</Color3>
|
||||
<int name="BorderSizePixel">0</int>
|
||||
<bool name="ClipsDescendants">false</bool>
|
||||
<bool name="Draggable">false</bool>
|
||||
<token name="Font">4</token>
|
||||
<token name="FontSize">5</token>
|
||||
<string name="Name">NumberFound</string>
|
||||
<Ref name="NextSelectionDown">null</Ref>
|
||||
<Ref name="NextSelectionLeft">null</Ref>
|
||||
<Ref name="NextSelectionRight">null</Ref>
|
||||
<Ref name="NextSelectionUp">null</Ref>
|
||||
<UDim2 name="Position">
|
||||
<XS>0</XS>
|
||||
<XO>5</XO>
|
||||
<YS>0</YS>
|
||||
<YO>55</YO>
|
||||
</UDim2>
|
||||
<float name="Rotation">0</float>
|
||||
<bool name="Selectable">false</bool>
|
||||
<Ref name="SelectionImageObject">null</Ref>
|
||||
<UDim2 name="Size">
|
||||
<XS>1</XS>
|
||||
<XO>-5</XO>
|
||||
<YS>0</YS>
|
||||
<YO>20</YO>
|
||||
</UDim2>
|
||||
<token name="SizeConstraint">0</token>
|
||||
<string name="Text"></string>
|
||||
<Color3 name="TextColor3">4294924122</Color3>
|
||||
<bool name="TextScaled">false</bool>
|
||||
<Color3 name="TextStrokeColor3">4278190080</Color3>
|
||||
<float name="TextStrokeTransparency">1</float>
|
||||
<float name="TextTransparency">0</float>
|
||||
<bool name="TextWrapped">false</bool>
|
||||
<token name="TextXAlignment">0</token>
|
||||
<token name="TextYAlignment">1</token>
|
||||
<bool name="Visible">true</bool>
|
||||
<int name="ZIndex">1</int>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ImageButton" referent="RBXB619D19972004CB3992613F98E612BD0">
|
||||
<Properties>
|
||||
<bool name="Active">true</bool>
|
||||
<bool name="AutoButtonColor">true</bool>
|
||||
<Color3 name="BackgroundColor3">4294967295</Color3>
|
||||
<float name="BackgroundTransparency">1</float>
|
||||
<Color3 name="BorderColor3">4279970357</Color3>
|
||||
<int name="BorderSizePixel">0</int>
|
||||
<bool name="ClipsDescendants">false</bool>
|
||||
<bool name="Draggable">false</bool>
|
||||
<Content name="Image">
|
||||
<url>roblox.com/asset?id=358274726</url>
|
||||
</Content>
|
||||
<Color3 name="ImageColor3">4294967295</Color3>
|
||||
<Vector2 name="ImageRectOffset">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
</Vector2>
|
||||
<Vector2 name="ImageRectSize">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
</Vector2>
|
||||
<float name="ImageTransparency">0</float>
|
||||
<bool name="Modal">false</bool>
|
||||
<string name="Name">RunButton</string>
|
||||
<Ref name="NextSelectionDown">null</Ref>
|
||||
<Ref name="NextSelectionLeft">null</Ref>
|
||||
<Ref name="NextSelectionRight">null</Ref>
|
||||
<Ref name="NextSelectionUp">null</Ref>
|
||||
<UDim2 name="Position">
|
||||
<XS>1</XS>
|
||||
<XO>-25</XO>
|
||||
<YS>0</YS>
|
||||
<YO>30</YO>
|
||||
</UDim2>
|
||||
<float name="Rotation">0</float>
|
||||
<token name="ScaleType">0</token>
|
||||
<bool name="Selectable">true</bool>
|
||||
<bool name="Selected">false</bool>
|
||||
<Ref name="SelectionImageObject">null</Ref>
|
||||
<UDim2 name="Size">
|
||||
<XS>0</XS>
|
||||
<XO>20</XO>
|
||||
<YS>0</YS>
|
||||
<YO>20</YO>
|
||||
</UDim2>
|
||||
<token name="SizeConstraint">0</token>
|
||||
<Rect2D name="SliceCenter">
|
||||
<min>
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
</min>
|
||||
<max>
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
</max>
|
||||
</Rect2D>
|
||||
<token name="Style">0</token>
|
||||
<bool name="Visible">false</bool>
|
||||
<int name="ZIndex">1</int>
|
||||
</Properties>
|
||||
</Item>
|
||||
<Item class="ImageButton" referent="RBX18E6EA46987C449D9CA4697F8E7FC57B">
|
||||
<Properties>
|
||||
<bool name="Active">true</bool>
|
||||
<bool name="AutoButtonColor">true</bool>
|
||||
<Color3 name="BackgroundColor3">4294967295</Color3>
|
||||
<float name="BackgroundTransparency">1</float>
|
||||
<Color3 name="BorderColor3">4279970357</Color3>
|
||||
<int name="BorderSizePixel">0</int>
|
||||
<bool name="ClipsDescendants">false</bool>
|
||||
<bool name="Draggable">false</bool>
|
||||
<Content name="Image">
|
||||
<url>roblox.com/asset?id=320903644</url>
|
||||
</Content>
|
||||
<Color3 name="ImageColor3">4294967295</Color3>
|
||||
<Vector2 name="ImageRectOffset">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
</Vector2>
|
||||
<Vector2 name="ImageRectSize">
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
</Vector2>
|
||||
<float name="ImageTransparency">0</float>
|
||||
<bool name="Modal">false</bool>
|
||||
<string name="Name">HelpButton</string>
|
||||
<Ref name="NextSelectionDown">null</Ref>
|
||||
<Ref name="NextSelectionLeft">null</Ref>
|
||||
<Ref name="NextSelectionRight">null</Ref>
|
||||
<Ref name="NextSelectionUp">null</Ref>
|
||||
<UDim2 name="Position">
|
||||
<XS>1</XS>
|
||||
<XO>-19</XO>
|
||||
<YS>0</YS>
|
||||
<YO>4</YO>
|
||||
</UDim2>
|
||||
<float name="Rotation">0</float>
|
||||
<token name="ScaleType">0</token>
|
||||
<bool name="Selectable">true</bool>
|
||||
<bool name="Selected">false</bool>
|
||||
<Ref name="SelectionImageObject">null</Ref>
|
||||
<UDim2 name="Size">
|
||||
<XS>0</XS>
|
||||
<XO>16</XO>
|
||||
<YS>0</YS>
|
||||
<YO>16</YO>
|
||||
</UDim2>
|
||||
<token name="SizeConstraint">0</token>
|
||||
<Rect2D name="SliceCenter">
|
||||
<min>
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
</min>
|
||||
<max>
|
||||
<X>0</X>
|
||||
<Y>0</Y>
|
||||
</max>
|
||||
</Rect2D>
|
||||
<token name="Style">0</token>
|
||||
<bool name="Visible">true</bool>
|
||||
<int name="ZIndex">1</int>
|
||||
</Properties>
|
||||
</Item>
|
||||
</Item>
|
||||
</Item>
|
||||
</Item>
|
||||
</roblox>
|
||||
6891
client/studio/resources/BuiltInPlugins/TerrainTools.rbxmx
Normal file
6783
client/studio/resources/BuiltInPlugins/TransformDragger.rbxmx
Normal file
996
client/studio/resources/BuiltInPlugins/terrain/00 - terrain.lua
Normal file
@@ -0,0 +1,996 @@
|
||||
-- Local function definitions
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
---------------
|
||||
--PLUGIN SETUP-
|
||||
---------------
|
||||
local loaded = false
|
||||
local on = false
|
||||
|
||||
local plugin = PluginManager():CreatePlugin()
|
||||
local toolbar = plugin:CreateToolbar("Terrain")
|
||||
local toolbarbutton = toolbar:CreateButton("Generator", "Terrain Generator", "terrain.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 SetCells = c.SetCells
|
||||
local AutoWedge = c.AutowedgeCells
|
||||
local CellCenterToWorld = c.CellCenterToWorld
|
||||
|
||||
-- Create a set of terrain options. Contains everything needed to now how to generate terrain.
|
||||
local TerrainOptions = {}
|
||||
TerrainOptions.__index = TerrainOptions
|
||||
|
||||
-- This will create with default options.
|
||||
function TerrainOptions.Create()
|
||||
local options = {}
|
||||
setmetatable(options, TerrainOptions)
|
||||
|
||||
-- Offset to create terrain at.
|
||||
options.xpos = 0
|
||||
options.zpos = 0
|
||||
|
||||
-- X width
|
||||
options.width = 512
|
||||
|
||||
-- Z length
|
||||
options.length = 512
|
||||
|
||||
-- Amplitude, how high hills can be. The higher the number the higher a hill can be.
|
||||
options.a = 30
|
||||
|
||||
-- Frequency, how often hills occur. The higher the number the more hills.
|
||||
options.f = 8
|
||||
|
||||
-- How deep water should be. 0 means no water.
|
||||
options.waterHeight = 0
|
||||
|
||||
-- How how the base of the world should be. 0 means no base.
|
||||
options.baseHeight = 1
|
||||
|
||||
-- What material type terrain will be.
|
||||
options.terrainMaterial = 1
|
||||
|
||||
-- Display the location.
|
||||
return options
|
||||
end
|
||||
|
||||
-- Stores the current terrain options to set/display.
|
||||
local terrainOptions = TerrainOptions.Create()
|
||||
|
||||
-- This will store a copy of terrainOptions when the generate button is pressed.
|
||||
-- This will then be used for the generation instead of terrainOptions. This way terrainOptions can be changed while generating without messing up what is being generated.
|
||||
local generateOptions = nil
|
||||
|
||||
-- Create a deep copy of a table.
|
||||
-- Copies the elements.
|
||||
function CopyTable(object)
|
||||
local holdTable = {}
|
||||
local function Copy(object)
|
||||
if type(object) ~= "table" then
|
||||
return object
|
||||
elseif holdTable[object] then
|
||||
return holdTable[object]
|
||||
end
|
||||
local clone = {}
|
||||
holdTable[object] = clone
|
||||
for index, value in pairs(object) do
|
||||
clone[Copy(index)] = Copy(value)
|
||||
end
|
||||
return setmetatable(clone, getmetatable(object))
|
||||
end
|
||||
return Copy(object)
|
||||
end
|
||||
|
||||
-- Creates a copy of terrain options data.
|
||||
--
|
||||
-- Return:
|
||||
-- Value is a new terrain options set with the old terrain options data.
|
||||
function TerrainOptions:Clone(ID)
|
||||
return CopyTable(self)
|
||||
end
|
||||
|
||||
-----------------
|
||||
--DEFAULT VALUES-
|
||||
-----------------
|
||||
-- Stores the conformation popup when confirming a choice. If not nil then some actions should be blocked.
|
||||
local ConfirmationPopupObject = nil
|
||||
|
||||
-- If true, the clear terrain conformation won't be shown.
|
||||
local hideClearConformation = false
|
||||
|
||||
-- If true, the genearte terrain conformation won't be shown.
|
||||
local hideGenerateConformation = false
|
||||
|
||||
------
|
||||
--GUI-
|
||||
------
|
||||
--screengui
|
||||
local g = Instance.new("ScreenGui", game:GetService("CoreGui"))
|
||||
g.Name = 'TerrainCreatorGui'
|
||||
|
||||
-- UI gui load. Required for sliders.
|
||||
local RbxGui = LoadLibrary("RbxGui")
|
||||
|
||||
-- Gui frame for the plugin.
|
||||
local terrainPropertiesDragBar, terrainFrame, terrainHelpFrame, terrainCloseEvent = RbxGui.CreatePluginFrame("Terrain Generator",UDim2.new(0,186,0,428),UDim2.new(0,0,0,0),false,g)
|
||||
terrainPropertiesDragBar.Visible = false
|
||||
terrainCloseEvent.Event:connect(function ( )
|
||||
Off()
|
||||
end)
|
||||
|
||||
terrainHelpFrame.Size = UDim2.new(0,300,0,350)
|
||||
|
||||
local terrainHelpText = Instance.new("TextLabel",terrainHelpFrame)
|
||||
terrainHelpText.Name = "HelpText"
|
||||
terrainHelpText.Font = Enum.Font.ArialBold
|
||||
terrainHelpText.FontSize = Enum.FontSize.Size12
|
||||
terrainHelpText.TextColor3 = Color3.new(227/255,227/255,227/255)
|
||||
terrainHelpText.TextXAlignment = Enum.TextXAlignment.Left
|
||||
terrainHelpText.TextYAlignment = Enum.TextYAlignment.Top
|
||||
terrainHelpText.Position = UDim2.new(0,4,0,4)
|
||||
terrainHelpText.Size = UDim2.new(1,-8,0,157)
|
||||
terrainHelpText.BackgroundTransparency = 1
|
||||
terrainHelpText.TextWrap = true
|
||||
terrainHelpText.Text = [[Create terrain with hills, water.
|
||||
X-Offset and Z-Offset:
|
||||
Center point of terrain that will be created.
|
||||
Terrain must be in a specific region. If part of the terrain is outside that region, it won't be created.
|
||||
|
||||
Width: Terrain size in the X direction
|
||||
|
||||
Length: Terrain size in the Z direction
|
||||
|
||||
Amplitude:
|
||||
Maximum height of hills.
|
||||
]]
|
||||
|
||||
local helpSecondText = terrainHelpText:clone()
|
||||
helpSecondText.Name = "HelpSecondText"
|
||||
helpSecondText.Position = UDim2.new(0,0,1,0)
|
||||
helpSecondText.Size = UDim2.new(1,0,0,180)
|
||||
helpSecondText.Text = [[Frequency:
|
||||
How often hills are made.
|
||||
|
||||
Base Height:
|
||||
How high the base of terrain should be.
|
||||
|
||||
Water Height:
|
||||
How high water should be. Terrain will overwrite water.
|
||||
]]
|
||||
helpSecondText.Parent = terrainHelpText
|
||||
|
||||
local helpThirdText = helpSecondText:clone()
|
||||
helpThirdText.Name = "HelpThirdText"
|
||||
helpThirdText.Position = UDim2.new(0,0,.6,0)
|
||||
helpThirdText.Size = UDim2.new(1,0,0,180)
|
||||
helpThirdText.Text = [[
|
||||
|
||||
Generate Button:
|
||||
Create the terrain.
|
||||
|
||||
Clear Button:
|
||||
Remove all terrain.
|
||||
]]
|
||||
helpThirdText.Parent = helpSecondText
|
||||
|
||||
-- Used to create a highlighter.
|
||||
-- A highlighter is a open, rectuangular area displayed in 3D.
|
||||
local Highlighter = {}
|
||||
Highlighter.__index = Highlighter
|
||||
|
||||
-- Create a highlighter object.
|
||||
--
|
||||
-- position - Where the highlighter should be displayed. If nil, the highlighter will not be shown immediatly.
|
||||
-- ID - Number ID to use for the box.
|
||||
-- color -- Color to use for the highlighter. Something like BrickColor.new("Really red")
|
||||
function Highlighter.Create(position,
|
||||
ID,
|
||||
color)
|
||||
-- This will be the created highlighter.
|
||||
local highlighter = {}
|
||||
|
||||
-- How high to show the highlighter.
|
||||
highlighter.height = 0
|
||||
highlighter.width = 0
|
||||
highlighter.length = 0
|
||||
|
||||
highlighter.x = 0
|
||||
highlighter.y = 0
|
||||
highlighter.z = 0
|
||||
|
||||
-- Create the part that the highlighter will be attached to.
|
||||
highlighter.selectionPart = Instance.new("Part")
|
||||
highlighter.selectionPart.Name = "SelectionPart"
|
||||
highlighter.selectionPart.Archivable = false
|
||||
highlighter.selectionPart.Transparency = 1
|
||||
highlighter.selectionPart.Anchored = true
|
||||
highlighter.selectionPart.Locked = true
|
||||
highlighter.selectionPart.CanCollide = false
|
||||
highlighter.selectionPart.FormFactor = Enum.FormFactor.Custom
|
||||
|
||||
highlighter.selectionBox = Instance.new("SelectionBox")
|
||||
highlighter.selectionBox.Name = 'box' .. ID
|
||||
|
||||
highlighter.selectionBox.Archivable = false
|
||||
highlighter.selectionBox.Color = color
|
||||
highlighter.selectionBox.Adornee = highlighter.selectionPart
|
||||
setmetatable(highlighter, Highlighter)
|
||||
|
||||
return highlighter
|
||||
end
|
||||
|
||||
-- Hide the highlighter.
|
||||
function Highlighter:DisablePreview()
|
||||
self.selectionBox.Parent = nil
|
||||
end
|
||||
|
||||
-- Show the highlighter.
|
||||
function Highlighter:EnablePreview()
|
||||
self.selectionBox.Parent = game:GetService("CoreGui") -- This will make it not show up in workspace.
|
||||
end
|
||||
|
||||
-- Update where the highlighter is displayed.
|
||||
-- cellPos - Where to display the highlighter, in cells.
|
||||
function Highlighter:UpdatePosition(cellPos)
|
||||
if not cellPos then
|
||||
self:DisablePreview()
|
||||
return
|
||||
end
|
||||
|
||||
local regionToSelect = nil
|
||||
|
||||
self.x = cellPos.x
|
||||
self.y = cellPos.y
|
||||
self.z = cellPos.z
|
||||
|
||||
local lowVec = CellCenterToWorld(c, cellPos.x - self.width/2 , cellPos.y - 1, cellPos.z - self.length/2)
|
||||
local highVec = CellCenterToWorld(c, cellPos.x + self.width/2, cellPos.y + self.height, cellPos.z + self.length/2)
|
||||
regionToSelect = Region3.new(lowVec, highVec)
|
||||
|
||||
self.selectionPart.Size = regionToSelect.Size - Vector3.new(-4,4,-4)
|
||||
self.selectionPart.CFrame = regionToSelect.CFrame
|
||||
end
|
||||
|
||||
-- Set the dimensions of the highlighter.
|
||||
-- Updates the display size.
|
||||
-- length - Length of the area (z direction)
|
||||
-- width - Width of the area (x direction)
|
||||
-- height - Height of the area (y direction)
|
||||
function Highlighter:UpdateDimensions(length, width, height)
|
||||
self.length = length
|
||||
self.width = width
|
||||
self.height = height
|
||||
self:UpdatePosition(Vector3.new(self.x, self.y, self.z))
|
||||
end
|
||||
|
||||
-- Create a standard text label. Use this for all lables in the popup so it is easy to standardize.
|
||||
-- labelName - What to set the text label name as.
|
||||
-- pos - Where to position the label. Should be of type UDim2.
|
||||
-- size - How large to make the label. Should be of type UDim2.
|
||||
-- text - Text to display.
|
||||
-- parent - What to set the text parent as.
|
||||
-- Return:
|
||||
-- Value is the created label.
|
||||
function CreateStandardLabel(labelName,
|
||||
pos,
|
||||
size,
|
||||
text,
|
||||
parent)
|
||||
local label = Instance.new("TextLabel", parent)
|
||||
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, -21, 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
|
||||
|
||||
-- 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
|
||||
|
||||
-- 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.
|
||||
buttonTextColor = Color3.new(1, 1, 1);
|
||||
buttonFont = Enum.Font.ArialBold;
|
||||
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)
|
||||
button = Instance.new("TextButton", parent)
|
||||
button.Name = name
|
||||
button.Position = pos
|
||||
|
||||
button.Size = UDim2.new(0,120,0,40)
|
||||
button.Text = text
|
||||
|
||||
if size then
|
||||
button.Size = size
|
||||
end
|
||||
|
||||
button.Style = Enum.ButtonStyle.RobloxButton
|
||||
|
||||
button.TextColor3 = buttonTextColor
|
||||
button.Font = buttonFont
|
||||
button.FontSize = buttonFontSize
|
||||
|
||||
button.MouseButton1Click:connect(funcOnPress)
|
||||
|
||||
return button
|
||||
end
|
||||
|
||||
cancelValues = {cancelAction = false, -- Used to cancel currently occuring actions. If set to true then terrain generation will stop.
|
||||
progressBar = nil, -- Will store the progress bar when needed.
|
||||
setAmountFunc = nil, -- Stores a function tied to the progres bar that sets the progress bar precentage done.
|
||||
bindForCancel = nil} -- Stores a function bind that will be set with the function to run when the cancel button is pressed in the progress bar.
|
||||
|
||||
-- Load the progress bar to display when drawing a river.
|
||||
-- text - Text to display.
|
||||
function LoadProgressBar(text)
|
||||
if cancelValues.progressBar == nil then
|
||||
cancelValues.isDrawing = true
|
||||
|
||||
-- Start the progress bar.
|
||||
cancelValues.progressBar, cancelValues.setAmountFunc, cancelValues.bindForCancel = RbxGui.CreateLoadingFrame(text)
|
||||
|
||||
cancelValues.progressBar.Position = UDim2.new(.5, -cancelValues.progressBar.Size.X.Offset/2, 0, 15)
|
||||
cancelValues.progressBar.Parent = g
|
||||
|
||||
cancelValues.bindForCancel.Event:connect(function(arguments)
|
||||
cancelValues.cancelActions = true -- Set the flag that everything should stop.
|
||||
|
||||
coroutine.yield()
|
||||
end)
|
||||
else
|
||||
print('Tried to start the progress bar when it was already running.')
|
||||
end
|
||||
end
|
||||
|
||||
-- Unload the progress bar.
|
||||
function UnloadProgressBar()
|
||||
cancelValues.isDrawing = false
|
||||
cancelValues.cancelActions = false
|
||||
|
||||
if nil ~= cancelValues.progressBar then
|
||||
cancelValues.progressBar.Parent = nil
|
||||
cancelValues.progressBar = nil
|
||||
cancelValues.setAmountFunc = nil
|
||||
cancelValues.bindForCancel = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Slider for controlling the x offset to generate terrain at.
|
||||
offsetXLabel = CreateStandardLabel("offsetXLabel", UDim2.new(0, 8, 0, 10), UDim2.new(0, 67, 0, 14), "", terrainFrame)
|
||||
offsetXSliderGui, offsetXSliderPosition = CreateStandardSlider('offsetXSliderGui', UDim2.new(0,1,0,26), UDim2.new(0,10,0.5,-2), 128,
|
||||
function(offsetXSliderPosition)
|
||||
terrainOptions.xpos = (offsetXSliderPosition.Value - 1) * 4 - 252
|
||||
offsetXLabel.Text = "X-Offset: "..terrainOptions.xpos
|
||||
end, nil, terrainFrame)
|
||||
offsetXSliderPosition.Value = (terrainOptions.xpos+252)/4 + 1
|
||||
|
||||
-- Slider for controlling the z offset to generate terrain at.
|
||||
offsetZLabel = CreateStandardLabel("OffsetZLabel", UDim2.new(0, 8, 0, 51), UDim2.new(0, 67, 0, 14), "", terrainFrame)
|
||||
offsetZSliderGui, offsetZSliderPosition = CreateStandardSlider('OffsetZSliderGui', UDim2.new(0,1,0,67), UDim2.new(0,10,0.5,-2), 128,
|
||||
function(offsetZSliderPosition)
|
||||
terrainOptions.zpos = (offsetZSliderPosition.Value - 1) * 4 - 252
|
||||
offsetZLabel.Text = "Z-Offset: "..terrainOptions.zpos
|
||||
end, nil, terrainFrame)
|
||||
offsetZSliderPosition.Value = (terrainOptions.zpos+252)/4 + 1
|
||||
|
||||
-----------------------
|
||||
--FUNCTION DEFINITIONS-
|
||||
-----------------------
|
||||
|
||||
--makes a column of blocks from 1 up to height at location (x,z) in cluster c
|
||||
function coordHeight(x, z, height)
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(x, 1, z), Vector3int16.new(x, height, z)), terrainOptions.terrainMaterial, 0, 0)
|
||||
end
|
||||
|
||||
--makes a heightmap for a layer of mountains (width x depth)
|
||||
--with a width frequency wf and depthfrequency df (width should be divisible by wf, depth should be divisible by df) (for unsquished results, width/wf = depth/df)
|
||||
--with a range of amplitudes between 0 and a
|
||||
function mountLayer(width, depth, wf, df, a)
|
||||
local heightmap = {}
|
||||
for i = 0, width-1 do
|
||||
heightmap[i] = {}
|
||||
for k = 0, depth-1 do
|
||||
heightmap[i][k] = 0
|
||||
end
|
||||
end
|
||||
math.randomseed(tick())
|
||||
local corners = {}
|
||||
for i = 0,wf do
|
||||
corners[i] = {}
|
||||
for k = 0, df do
|
||||
corners[i][k] = a*math.random()
|
||||
end
|
||||
end
|
||||
for i = 0, wf do
|
||||
corners[i][0] = 0
|
||||
corners[i][math.floor(df)] = 0
|
||||
end
|
||||
for k = 0, df do
|
||||
corners[0][k]=0
|
||||
corners[math.floor(wf)][k]=0
|
||||
end
|
||||
|
||||
for i = 0, width-(width/wf), width/wf do
|
||||
for k = 0, depth-(depth/df), depth/df do
|
||||
local c1 = corners[i/(width/wf)][k/(depth/df)]
|
||||
local c2 = corners[i/(width/wf)][(k+depth/df)/(depth/df)]
|
||||
local c3 = corners[(i+width/wf)/(width/wf)][k/(depth/df)]
|
||||
local c4 = corners[(i+width/wf)/(width/wf)][(k+depth/df)/(depth/df)]
|
||||
for x = i, i+(width/wf)-1 do
|
||||
for z = k, k+(depth/df)-1 do
|
||||
local avgc1c3 = (math.abs(x-i)*c3 + math.abs(x-(i+width/wf))*c1)/(width/wf)
|
||||
local avgc2c4 = (math.abs(x-i)*c4 + math.abs(x-(i+width/wf))*c2)/(width/wf)
|
||||
local avg = math.floor((math.abs(z-k)*avgc2c4 + math.abs(z-(k+depth/df))*avgc1c3)/(depth/df))
|
||||
if (avg > 100) then
|
||||
print(avg)
|
||||
avg = 1
|
||||
end
|
||||
heightmap[x][z]= avg
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return heightmap
|
||||
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
|
||||
SetCell(c, i, h, k, terrainOptions.terrainMaterial, 0, 0)
|
||||
end
|
||||
end
|
||||
shellheightmap[i][k] = originalheight
|
||||
end
|
||||
end
|
||||
end
|
||||
return shellheightmap
|
||||
end
|
||||
|
||||
-- Set the camera to look at the terrain from a distance so that all terrain will be in view.
|
||||
-- centerX, centerZ - Center coordinate of land. This doesn't take into account clipping.
|
||||
-- length, width - Land dimensions.
|
||||
function SetCamera(centerX, centerZ,
|
||||
length, width, height)
|
||||
local currCamera = game.Workspace.CurrentCamera
|
||||
local cameraPos = Vector3.new(0, 400, 1600)
|
||||
local cameraFocus = Vector3.new(0, height * 4, 0)
|
||||
|
||||
-- Nothing set so use the default.
|
||||
if nil ~= centerX then
|
||||
local scale = 0
|
||||
local lengthScale = 0
|
||||
local widthScale = 0
|
||||
|
||||
if length <= 64 then
|
||||
lengthScale = .35
|
||||
elseif length <= 128 then
|
||||
lengthScale = .5
|
||||
elseif length <= 256 then
|
||||
lengthScale = .7
|
||||
else
|
||||
lengthScale = 1.3
|
||||
end
|
||||
|
||||
if width <= 64 then
|
||||
widthScale = .35
|
||||
elseif width <= 128 then
|
||||
widthScale = .4
|
||||
elseif width <= 256 then
|
||||
widthScale = .4
|
||||
else
|
||||
widthScale = .7
|
||||
end
|
||||
|
||||
if widthScale > lengthScale then
|
||||
scale = widthScale
|
||||
else
|
||||
scale = lengthScale
|
||||
end
|
||||
|
||||
local distance = Vector3.new(0, (200*scale) + 200, (1100*scale))
|
||||
cameraPos = Vector3.new(centerX + distance.X, distance.Y, centerZ + distance.Z)
|
||||
cameraFocus = Vector3.new(centerX, height * 4, centerZ)
|
||||
end
|
||||
|
||||
currCamera.CoordinateFrame = CFrame.new(cameraPos.X, cameraPos.Y, cameraPos.Z)
|
||||
currCamera.Focus = CFrame.new(cameraFocus.X, cameraFocus.Y, cameraFocus.Z)
|
||||
end
|
||||
|
||||
-- Function used by the clear button. Prompts the user first.
|
||||
-- Will not show if disabled or terrain is being processed.
|
||||
function ConfirmClearTerrain()
|
||||
-- Only do if something isn't already being processed.
|
||||
if nil == cancelValues.progressBar then
|
||||
if nil == ConfirmationPopupObject then
|
||||
if not hideClearConformation then
|
||||
ConfirmationPopupObject = ConfirmationPopup.Create("Clear Terrain?",
|
||||
"Clear",
|
||||
"Cancel",
|
||||
function()
|
||||
ClearConformation()
|
||||
ClearTerrain()
|
||||
end,
|
||||
ClearConformation,
|
||||
function()
|
||||
hideClearConformation = not hideClearConformation
|
||||
return not hideClearConformation
|
||||
end)
|
||||
else
|
||||
ClearTerrain()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Function used by the generate button. Prompts the user first.
|
||||
-- Will not show if disabled or terrain is being processed.
|
||||
function ConfirmGenerateTerrain()
|
||||
-- Only do if something isn't already being processed.
|
||||
if nil == cancelValues.progressBar then
|
||||
if nil == ConfirmationPopupObject then
|
||||
if not hideGenerateConformation then
|
||||
ConfirmationPopupObject = ConfirmationPopup.Create("Generate Terrain?",
|
||||
"Generate",
|
||||
"Cancel",
|
||||
function()
|
||||
ClearConformation()
|
||||
GenerateTerrain()
|
||||
end,
|
||||
ClearConformation,
|
||||
function()
|
||||
hideGenerateConformation = not hideGenerateConformation
|
||||
return not hideGenerateConformation
|
||||
end)
|
||||
else
|
||||
GenerateTerrain()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Create terrain based on the current properties.
|
||||
function GenerateTerrain()
|
||||
toolbarbutton:SetActive(false)
|
||||
|
||||
generateOptions = terrainOptions:Clone()
|
||||
|
||||
-- Turn off the plugin
|
||||
Off()
|
||||
|
||||
-- Create the progress bar that will track terrain creation completion.
|
||||
LoadProgressBar("Generating Terrain")
|
||||
|
||||
--Generate Terrain
|
||||
-- offset terrain additionally by whatever the smallest cell is
|
||||
--xpos2 = generateOptions.xpos + game.Workspace.Terrain.MaxExtents.Min.X
|
||||
--zpos2 = generateOptions.zpos + game.Workspace.Terrain.MaxExtents.Min.Z
|
||||
xpos2 = generateOptions.xpos - generateOptions.width/2
|
||||
zpos2 = generateOptions.zpos - generateOptions.length/2
|
||||
|
||||
-- Reposition to get a good view.
|
||||
SetCamera(generateOptions.xpos, generateOptions.zpos, generateOptions.length, generateOptions.width, math.max(generateOptions.baseHeight, generateOptions.a / 2))
|
||||
|
||||
--make 3 layers of mountains (you can change the frequency and amplitude of each layer and add or remove layers as you see fit (but don't forget to add the new layers to the loop below)
|
||||
a1 = mountLayer(generateOptions.width, generateOptions.length, generateOptions.f * generateOptions.width/512, generateOptions.f * generateOptions.length/512, 3/5*generateOptions.a)
|
||||
a2 = mountLayer(generateOptions.width, generateOptions.length, 2 * generateOptions.f *generateOptions.width/512, 2 * generateOptions.f * generateOptions.length/512, 2/5*generateOptions.a)
|
||||
heightmap = {}
|
||||
for x = 0, generateOptions.width - 1 do
|
||||
heightmap[x + xpos2] = {}
|
||||
for z = 0, generateOptions.length - 1 do
|
||||
heightmap[x + xpos2][z + zpos2] = a1[x][z] + a2[x][z]
|
||||
end
|
||||
end
|
||||
shellheightmap = {}
|
||||
for x = 0, generateOptions.width - 1 do
|
||||
shellheightmap[x + xpos2] = {}
|
||||
for z = 0, generateOptions.length - 1 do
|
||||
shellheightmap[x + xpos2][z + zpos2] = heightmap[x + xpos2][z + zpos2]
|
||||
end
|
||||
end
|
||||
gprogress = 0
|
||||
k = 1 + zpos2
|
||||
|
||||
local waitCount = 0
|
||||
|
||||
-- Fill in the edges that don't get done in the terrain calculations.
|
||||
if 0 ~= generateOptions.waterHeight then
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(xpos2, 1, zpos2), Vector3int16.new(xpos2, generateOptions.waterHeight, zpos2 + generateOptions.length-1)), Enum.CellMaterial.Water, 0, 0)
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(xpos2 + generateOptions.width-1, 1, zpos2), Vector3int16.new(xpos2 + generateOptions.width-1, generateOptions.waterHeight, zpos2 + generateOptions.length-1)), Enum.CellMaterial.Water, 0, 0)
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(xpos2, 1, zpos2), Vector3int16.new(xpos2 + generateOptions.width-1, generateOptions.waterHeight, zpos2)), Enum.CellMaterial.Water, 0, 0)
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(xpos2, 1, zpos2 + generateOptions.length-1), Vector3int16.new(xpos2 + generateOptions.width-1, generateOptions.waterHeight, zpos2 + generateOptions.length-1)), Enum.CellMaterial.Water, 0, 0)
|
||||
end
|
||||
if 0 ~= generateOptions.baseHeight then
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(xpos2, 0, zpos2), Vector3int16.new(xpos2, generateOptions.baseHeight, zpos2 + generateOptions.length-1)), terrainOptions.terrainMaterial, 0, 0)
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(xpos2 + generateOptions.width-1, 0, zpos2), Vector3int16.new(xpos2 + generateOptions.width-1, generateOptions.baseHeight, zpos2 + generateOptions.length-1)), terrainOptions.terrainMaterial, 0, 0)
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(xpos2, 0, zpos2), Vector3int16.new(xpos2 + generateOptions.width-1, generateOptions.baseHeight, zpos2)), terrainOptions.terrainMaterial, 0, 0)
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(xpos2, 0, zpos2 + generateOptions.length-1), Vector3int16.new(xpos2 + generateOptions.width-1, generateOptions.baseHeight, zpos2 + generateOptions.length-1)), terrainOptions.terrainMaterial, 0, 0)
|
||||
end
|
||||
|
||||
while k < generateOptions.length - 1 + zpos2 do
|
||||
for x = 1 + xpos2, generateOptions.width - 2 + xpos2 do
|
||||
-- End on cancel.
|
||||
if cancelValues.cancelActions then
|
||||
game:GetService("ChangeHistoryService"): SetWaypoint("CancelGenerate")
|
||||
UnloadProgressBar()
|
||||
return
|
||||
end
|
||||
|
||||
-- Create water.
|
||||
if 0 ~= generateOptions.waterHeight then
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(x, 1, k), Vector3int16.new(x, generateOptions.waterHeight, k)), Enum.CellMaterial.Water, 0, 0)
|
||||
end
|
||||
|
||||
-- Create the base for terrain.
|
||||
if 0 ~= generateOptions.baseHeight then
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(x, 0, k), Vector3int16.new(x, generateOptions.baseHeight, k)), terrainOptions.terrainMaterial, 0, 0)
|
||||
end
|
||||
|
||||
coordHeight(x, k, heightmap[x][k])
|
||||
shellheightmap = makeShell(x, k, heightmap, shellheightmap)
|
||||
end
|
||||
k = k + 1
|
||||
gprogress = gprogress + 2/(generateOptions.length * 3)
|
||||
|
||||
-- Update the amount completed.
|
||||
cancelValues.setAmountFunc(gprogress)
|
||||
|
||||
if waitCount > 5 then waitCount = 0 wait(0.01) else waitCount = waitCount + 1 end
|
||||
end
|
||||
k = 1 + zpos2
|
||||
waitCount = 0
|
||||
local maxHeight = -1
|
||||
local oldK = k
|
||||
while k < generateOptions.length - 1 + zpos2 do
|
||||
for x = 1 + xpos2, generateOptions.width - 2 + xpos2 do
|
||||
-- End on cancel.
|
||||
if cancelValues.cancelActions then
|
||||
game:GetService("ChangeHistoryService"): SetWaypoint("GenerateGenerate")
|
||||
UnloadProgressBar()
|
||||
return
|
||||
end
|
||||
|
||||
height = shellheightmap[x][k]
|
||||
if height == nil then
|
||||
height = -1
|
||||
end
|
||||
if height > maxHeight then
|
||||
maxHeight = height
|
||||
end
|
||||
end
|
||||
k = k + 1
|
||||
gprogress = gprogress + 1/(generateOptions.length * 3)
|
||||
|
||||
-- Update the amount completed.
|
||||
cancelValues.setAmountFunc(gprogress)
|
||||
|
||||
if waitCount > 10 then
|
||||
waitCount = 0
|
||||
AutoWedge(c, Region3int16.new(Vector3int16.new(1 + xpos2, 0, oldK), Vector3int16.new(generateOptions.width - 2 + xpos2, maxHeight, k)))
|
||||
oldK = k+1
|
||||
maxHeight = -1
|
||||
wait()
|
||||
else waitCount = waitCount + 1 end
|
||||
if k == generateOptions.length - 2 + zpos2 then
|
||||
AutoWedge(c, Region3int16.new(Vector3int16.new(1 + xpos2, 0, oldK), Vector3int16.new(generateOptions.width - 2 + xpos2, maxHeight, k)))
|
||||
end
|
||||
end
|
||||
|
||||
-- Clean up the progress bar.
|
||||
UnloadProgressBar()
|
||||
--Generate Terrain End
|
||||
game:GetService("ChangeHistoryService"): SetWaypoint("Generate")
|
||||
end
|
||||
|
||||
-- Clears all terrain.
|
||||
-- Clearing is immediate.
|
||||
function ClearTerrain()
|
||||
toolbarbutton:SetActive(false)
|
||||
Off()
|
||||
|
||||
--Erase Terrain
|
||||
LoadProgressBar('Clearing Terrain')
|
||||
|
||||
wait()
|
||||
c:Clear()
|
||||
|
||||
--Erase Terrain End
|
||||
UnloadProgressBar()
|
||||
game:GetService("ChangeHistoryService"): SetWaypoint("Reset")
|
||||
end
|
||||
|
||||
-- Used to create a highlighter.
|
||||
-- A highlighter is a open, rectuangular area displayed in 3D.
|
||||
ConfirmationPopup = {}
|
||||
ConfirmationPopup.__index = ConfirmationPopup
|
||||
|
||||
-- Create a confirmation popup.
|
||||
--
|
||||
-- confirmText - What to display in the popup.
|
||||
-- confirmButtonText - What to display in the popup.
|
||||
-- declineButtonText - What to display in the popup.
|
||||
-- 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,
|
||||
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", g)
|
||||
popup.confirmationFrame.Name = "ConfirmationFrame"
|
||||
popup.confirmationFrame.Size = UDim2.new(0, 280, 0, 140)
|
||||
popup.confirmationFrame.Position = UDim2.new(.5, -popup.confirmationFrame.Size.X.Offset/2, 0.5, -popup.confirmationFrame.Size.Y.Offset/2)
|
||||
popup.confirmationFrame.Style = Enum.FrameStyle.RobloxRound
|
||||
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
|
||||
|
||||
-- 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
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------------------
|
||||
-- Create Gui Interfaces
|
||||
--------------------------------------------------------------------------------------------
|
||||
|
||||
local maxXZExtentsLog = c.MaxExtents.Max.X > 512 and 6 or 4
|
||||
local maxYExtents = math.min(c.MaxExtents.Max.Y, 256)
|
||||
|
||||
-- Slider for controlling the width of the terran area. Terran is created in a region this wide.
|
||||
widthLabel = CreateStandardLabel("WidthLabel", UDim2.new(0, 8, 0, 92), UDim2.new(0, 67, 0, 14), "", terrainFrame)
|
||||
widthSliderGui, widthSliderPosition = CreateStandardSlider('WidthSliderGui', UDim2.new(0,1,0,108), UDim2.new(0,10,0.5,-2), maxXZExtentsLog,
|
||||
function(widthSliderPosition)
|
||||
terrainOptions.width = math.pow(2, widthSliderPosition.Value + 5)
|
||||
widthLabel.Text = "Width: "..terrainOptions.width
|
||||
end, 4, terrainFrame)
|
||||
|
||||
-- Slider for controlling the length of the terran area. Terran is created in a region this long.
|
||||
lengthLabel = CreateStandardLabel("LengthLabel", UDim2.new(0, 8, 0, 133), UDim2.new(0, 67, 0, 14), "", terrainFrame)
|
||||
lengthSliderGui, lengthSliderPosition = CreateStandardSlider('LengthSliderGui', UDim2.new(0,1,0,149), UDim2.new(0,10,0.5,-2), maxXZExtentsLog,
|
||||
function(lengthSliderPosition)
|
||||
terrainOptions.length = math.pow(2, lengthSliderPosition.Value + 5)
|
||||
lengthLabel.Text = "Length: "..terrainOptions.length
|
||||
end, 4, terrainFrame)
|
||||
|
||||
-- Slider for controlling the amplitude of hills.
|
||||
amplitudeLabel = CreateStandardLabel("AmplitudeLabel", UDim2.new(0, 8, 0, 174), UDim2.new(0, 67, 0, 14), "", terrainFrame)
|
||||
amplitudeSliderGui, amplitudeSliderPosition = CreateStandardSlider('AmplitudeSliderGui', UDim2.new(0,1,0,190), UDim2.new(0,10,0.5,-2), 62,
|
||||
function(amplitudeSliderPosition)
|
||||
terrainOptions.a = amplitudeSliderPosition.Value + 1
|
||||
amplitudeLabel.Text = "Amplitude: ".. terrainOptions.a
|
||||
end, nil, terrainFrame)
|
||||
amplitudeSliderPosition.Value = terrainOptions.a-1
|
||||
|
||||
-- Slider for controlling the frequency of hills.
|
||||
frequencyLabel = CreateStandardLabel("FrequencyLabel", UDim2.new(0, 8, 0, 215), UDim2.new(0, 67, 0, 14), "", terrainFrame)
|
||||
frequencySliderGui, frequencySliderPosition = CreateStandardSlider('FrequencySliderGui', UDim2.new(0,1,0,231), UDim2.new(0,10,0.5,-2), 8,
|
||||
function(frequencySliderPosition)
|
||||
terrainOptions.f = math.pow(2, frequencySliderPosition.Value - 1)
|
||||
frequencyLabel.Text = "Frequency: ".. terrainOptions.f
|
||||
end, nil, terrainFrame)
|
||||
frequencySliderPosition.Value = 4
|
||||
|
||||
-- Slider for controlling the baseHeight, how deep the base terrain should be.
|
||||
baseHeightLabel = CreateStandardLabel("BaseHeightLabel", UDim2.new(0, 8, 0, 256), UDim2.new(0, 67, 0, 14), "", terrainFrame)
|
||||
baseHeightSliderGui, baseHeightSliderPosition = CreateStandardSlider('BaseHeightSliderGui', UDim2.new(0,1,0,272), UDim2.new(0,10,0.5,-2), maxYExtents,
|
||||
function(baseHeightSliderPosition)
|
||||
terrainOptions.baseHeight = baseHeightSliderPosition.Value - 1
|
||||
baseHeightLabel.Text = "Base Height: ".. (terrainOptions.baseHeight)
|
||||
end, nil, terrainFrame)
|
||||
baseHeightSliderPosition.Value = terrainOptions.baseHeight + 1
|
||||
|
||||
-- Slider for controlling the waterHeight, how much water to fill.
|
||||
waterHeightLabel = CreateStandardLabel("WaterHeightLabel", UDim2.new(0, 8, 0, 297), UDim2.new(0, 67, 0, 14), "", terrainFrame)
|
||||
waterHeightSliderGui, waterHeightSliderPosition = CreateStandardSlider('WaterHeightSliderGui', UDim2.new(0,1,0,313), UDim2.new(0,10,0.5,-2), maxYExtents,
|
||||
function(waterHeightSliderPosition)
|
||||
terrainOptions.waterHeight = waterHeightSliderPosition.Value - 1
|
||||
waterHeightLabel.Text = "Water Height: ".. (terrainOptions.waterHeight)
|
||||
end, nil, terrainFrame)
|
||||
waterHeightSliderPosition.Value = terrainOptions.waterHeight + 1
|
||||
|
||||
-- Button to generate terrain using the current settings.
|
||||
generateButton = CreateStandardButton("GenerateButton",
|
||||
UDim2.new(0.5, -75, 0, 343),
|
||||
'Generate',
|
||||
ConfirmGenerateTerrain,
|
||||
terrainFrame,
|
||||
UDim2.new(0,150,0,40))
|
||||
|
||||
-- Button to clear terrain using. All terrain will be removed.
|
||||
clearButton = CreateStandardButton("ClearButton",
|
||||
UDim2.new(0.5, -75, 0, 385),
|
||||
'Clear',
|
||||
ConfirmClearTerrain,
|
||||
terrainFrame,
|
||||
UDim2.new(0,150,0,40))
|
||||
|
||||
-- Unload the conformation popup if it exists.
|
||||
-- Does nothing if the popup isn't set.
|
||||
function ClearConformation()
|
||||
if nil ~= ConfirmationPopupObject then
|
||||
ConfirmationPopupObject:Clear()
|
||||
ConfirmationPopupObject = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Run when the popup is activated.
|
||||
function On()
|
||||
if not c then
|
||||
return
|
||||
end
|
||||
plugin:Activate(true)
|
||||
toolbarbutton:SetActive(true)
|
||||
terrainPropertiesDragBar.Visible = true
|
||||
on = true
|
||||
end
|
||||
|
||||
-- Run when the popup is deactivated.
|
||||
function Off()
|
||||
toolbarbutton:SetActive(false)
|
||||
ClearConformation()
|
||||
on = false
|
||||
|
||||
-- Hide the popup gui.
|
||||
terrainPropertiesDragBar.Visible = false
|
||||
end
|
||||
|
||||
plugin.Deactivation:connect(function()
|
||||
Off()
|
||||
end)
|
||||
|
||||
loaded = true
|
||||
340
client/studio/resources/BuiltInPlugins/terrain/01 - builder.lua
Normal file
@@ -0,0 +1,340 @@
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
---------------
|
||||
--PLUGIN SETUP-
|
||||
---------------
|
||||
loaded = false
|
||||
-- True if the plugin is on, false if not.
|
||||
on = false
|
||||
|
||||
plugin = PluginManager():CreatePlugin()
|
||||
mouse = plugin:GetMouse()
|
||||
mouse.Button1Down:connect(function() onClicked(mouse) end)
|
||||
toolbar = plugin:CreateToolbar("Terrain")
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
MouseHighlighter = {}
|
||||
MouseHighlighter.__index = MouseHighlighter
|
||||
|
||||
-- Create a mouse movement highlighter.
|
||||
-- plugin - Plugin to get the mouse from.
|
||||
function MouseHighlighter.Create(mouseUse)
|
||||
local highlighter = {}
|
||||
|
||||
local mouse = 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.
|
||||
mouse.Move:connect(function() MouseMoved() end)
|
||||
|
||||
mouse.Button1Down:connect(function() highlighter.mouseDown = true end)
|
||||
mouse.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
|
||||
mouse.TargetFilter = highlighter.selectionPart
|
||||
setmetatable(highlighter, MouseHighlighter)
|
||||
|
||||
-- Function to call when the mouse has moved. Updates where to display the highlighter.
|
||||
function MouseMoved()
|
||||
if on then
|
||||
UpdatePosition(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.
|
||||
-- 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
|
||||
|
||||
-- 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 = nil
|
||||
|
||||
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(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
|
||||
else
|
||||
cellPos = WorldToCellPreferEmpty(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.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.
|
||||
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", parent)
|
||||
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
|
||||
g = Instance.new("ScreenGui", game:GetService("CoreGui"))
|
||||
g.Name = 'BuilderGui'
|
||||
|
||||
-- UI gui load. Required for sliders.
|
||||
local RbxGui = LoadLibrary("RbxGui")
|
||||
|
||||
-- Store properties here.
|
||||
local properties = {autoWedgeEnabled = false}
|
||||
|
||||
-- Gui frame for the plugin.
|
||||
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",builderHelpFrame)
|
||||
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.]]
|
||||
|
||||
addText = 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(mouse)
|
||||
if on then
|
||||
local cellPos = WorldToCellPreferEmpty(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
|
||||
local x = cellPos.x
|
||||
local y = cellPos.y
|
||||
local z = cellPos.z
|
||||
|
||||
local solidCellPos = WorldToCellPreferSolid(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.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(mouse.Hit.x, mouse.Hit.y, mouse.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(mouse.Hit)
|
||||
end
|
||||
end
|
||||
|
||||
mouseHighlighter.OnClicked = onClicked
|
||||
|
||||
-- Run when the popup is activated.
|
||||
function On()
|
||||
if not c then
|
||||
return
|
||||
end
|
||||
plugin:Activate(true)
|
||||
toolbarbutton:SetActive(true)
|
||||
builderPropertiesDragBar.Visible = true
|
||||
on = true
|
||||
end
|
||||
|
||||
-- Run when the popup is deactivated.
|
||||
function Off()
|
||||
toolbarbutton:SetActive(false)
|
||||
on = false
|
||||
|
||||
-- Hide the popup gui.
|
||||
builderPropertiesDragBar.Visible = false
|
||||
mouseHighlighter:DisablePreview()
|
||||
end
|
||||
|
||||
plugin.Deactivation:connect(function()
|
||||
Off()
|
||||
end)
|
||||
|
||||
loaded = true
|
||||
285
client/studio/resources/BuiltInPlugins/terrain/02 - remover.lua
Normal file
@@ -0,0 +1,285 @@
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
-----------------
|
||||
--DEFAULT VALUES-
|
||||
-----------------
|
||||
loaded = false
|
||||
-- True if the plugin is on, false if not.
|
||||
on = false
|
||||
|
||||
---------------
|
||||
--PLUGIN SETUP-
|
||||
---------------
|
||||
plugin = PluginManager():CreatePlugin()
|
||||
mouse = plugin:GetMouse()
|
||||
mouse.Button1Down:connect(function() onClicked(mouse) end)
|
||||
toolbar = plugin:CreateToolbar("Terrain")
|
||||
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.
|
||||
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.
|
||||
MouseHighlighter = {}
|
||||
MouseHighlighter.__index = MouseHighlighter
|
||||
|
||||
-- Create a mouse movement highlighter.
|
||||
-- plugin - Plugin to get the mouse from.
|
||||
function MouseHighlighter.Create(mouseUse)
|
||||
local highlighter = {}
|
||||
|
||||
local mouse = 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.
|
||||
mouse.Move:connect(function() MouseMoved() end)
|
||||
|
||||
mouse.Button1Down:connect(function() highlighter.mouseDown = true end)
|
||||
mouse.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
|
||||
mouse.TargetFilter = highlighter.selectionPart
|
||||
setmetatable(highlighter, MouseHighlighter)
|
||||
|
||||
-- Function to call when the mouse has moved. Updates where to display the highlighter.
|
||||
function MouseMoved()
|
||||
if on then
|
||||
UpdatePosition(mouse.Hit)
|
||||
end
|
||||
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
|
||||
|
||||
if not mouse.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 = nil
|
||||
|
||||
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(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
|
||||
else
|
||||
cellPos = WorldToCellPreferSolid(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.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.
|
||||
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", parent)
|
||||
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
|
||||
g = Instance.new("ScreenGui", game:GetService("CoreGui"))
|
||||
g.Name = 'RemoverGui'
|
||||
|
||||
-- UI gui load. Required for sliders.
|
||||
local RbxGui = LoadLibrary("RbxGui")
|
||||
|
||||
-- Store properties here.
|
||||
local properties = {autoWedgeEnabled = false}
|
||||
|
||||
-- Gui frame for the plugin.
|
||||
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",removerHelpFrame)
|
||||
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).]]
|
||||
|
||||
removeText = 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(mouse)
|
||||
if on then
|
||||
local cellPos = WorldToCellPreferSolid(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.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(mouse.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.
|
||||
function On()
|
||||
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.
|
||||
function Off()
|
||||
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,556 @@
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
---------------
|
||||
--PLUGIN SETUP-
|
||||
---------------
|
||||
loaded = false
|
||||
-- True if the plugin is on, false if not.
|
||||
on = false
|
||||
|
||||
plugin = PluginManager():CreatePlugin()
|
||||
mouse = plugin:GetMouse()
|
||||
mouse.Button1Down:connect(function() onClicked(mouse) end)
|
||||
toolbar = plugin:CreateToolbar("Terrain")
|
||||
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.
|
||||
waterMaterialID = 17
|
||||
|
||||
-- Elevation related options. Contains everything needed for making the elevation.
|
||||
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.
|
||||
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.
|
||||
MouseHighlighter = {}
|
||||
MouseHighlighter.__index = MouseHighlighter
|
||||
|
||||
-- Create a mouse movement highlighter.
|
||||
-- plugin - Plugin to get the mouse from.
|
||||
function MouseHighlighter.Create(mouseUse)
|
||||
local highlighter = {}
|
||||
|
||||
local mouse = 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.
|
||||
mouse.Move:connect(function() MouseMoved() end)
|
||||
|
||||
mouse.Button1Down:connect(function() highlighter.mouseDown = true end)
|
||||
mouse.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
|
||||
mouse.TargetFilter = highlighter.selectionPart
|
||||
setmetatable(highlighter, MouseHighlighter)
|
||||
|
||||
-- Function to call when the mouse has moved. Updates where to display the highlighter.
|
||||
function MouseMoved()
|
||||
if on then
|
||||
UpdatePosition(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.
|
||||
-- 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
|
||||
|
||||
-- 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 = nil
|
||||
|
||||
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(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
|
||||
else
|
||||
cellPos = WorldToCellPreferEmpty(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.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.
|
||||
mouseHighlighter = MouseHighlighter.Create(mouse)
|
||||
|
||||
------
|
||||
--GUI-
|
||||
------
|
||||
--screengui
|
||||
local g = Instance.new("ScreenGui", game:GetService("CoreGui"))
|
||||
g.Name = 'ElevationGui'
|
||||
|
||||
-- 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", parent)
|
||||
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.
|
||||
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",elevationHelpFrame)
|
||||
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.]]
|
||||
|
||||
-- Slider for controlling radius.
|
||||
radiusLabel = CreateStandardLabel("RadiusLabel", UDim2.new(0, 8, 0, 10), UDim2.new(0, 67, 0, 14), "", elevationFrame)
|
||||
radiusSliderGui, radiusSliderPosition = CreateStandardSlider('radiusSliderGui', UDim2.new(0,1,0,26), UDim2.new(0,10,0.5,-2), 11,
|
||||
function(radiusSliderPosition)
|
||||
elevationOptions.r = radiusSliderPosition.Value -- 1
|
||||
radiusLabel.Text = "Radius: "..elevationOptions.r
|
||||
end, nil, elevationFrame)
|
||||
radiusSliderPosition.Value = 1
|
||||
|
||||
-- Slider for controlling the z offset to generate terrain at.
|
||||
slopeLabel = CreateStandardLabel("SlopeLabel", UDim2.new(0, 8, 0, 51), UDim2.new(0, 67, 0, 14), "", elevationFrame)
|
||||
slopeSliderGui, 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)
|
||||
h = 0
|
||||
material, wedge, rotation = GetCell(c, x, h + 1, z)
|
||||
while material.Value > 0 do
|
||||
h = h + 1
|
||||
material, wedge, rotation = 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
|
||||
|
||||
|
||||
--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
|
||||
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 height = heightmap[i][k]
|
||||
if height == nil then
|
||||
height = -1
|
||||
end
|
||||
|
||||
-- Autowedge if enabled.
|
||||
if elevationOptions.autowedge then
|
||||
for h = height, 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(mouse)
|
||||
if on then
|
||||
oldheightmap = {}
|
||||
heightmap = {}
|
||||
local cellPos = WorldToCellPreferEmpty(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))
|
||||
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(mouse.Hit.x, mouse.Hit.y, mouse.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
|
||||
local isWater
|
||||
isWater, 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()
|
||||
|
||||
mousedown = true
|
||||
local originalY = mouse.Y
|
||||
local prevY = originalY
|
||||
local d = 0
|
||||
local range = 0
|
||||
while mousedown == true do
|
||||
if math.abs(mouse.Y - prevY) >= 5 then
|
||||
prevY = mouse.Y
|
||||
r2 = elevationOptions.r + math.floor(50 * 1/elevationOptions.s * math.abs(originalY - prevY)/mouse.ViewSizeY)
|
||||
if r2 > range then
|
||||
range = r2
|
||||
end
|
||||
d = math.floor(50 * (originalY - prevY)/mouse.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.
|
||||
function On()
|
||||
if not c then
|
||||
return
|
||||
end
|
||||
plugin:Activate(true)
|
||||
toolbarbutton:SetActive(true)
|
||||
elevationPropertiesDragBar.Visible = true
|
||||
|
||||
on = true
|
||||
end
|
||||
|
||||
-- Run when the popup is deactivated.
|
||||
function Off()
|
||||
toolbarbutton:SetActive(false)
|
||||
on = false
|
||||
|
||||
-- Hide the popup gui.
|
||||
elevationPropertiesDragBar.Visible = false
|
||||
mouseHighlighter:DisablePreview()
|
||||
end
|
||||
|
||||
plugin.Deactivation:connect(function()
|
||||
Off()
|
||||
end)
|
||||
|
||||
loaded = true
|
||||
639
client/studio/resources/BuiltInPlugins/terrain/04 - brush.lua
Normal file
@@ -0,0 +1,639 @@
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
---------------
|
||||
--PLUGIN SETUP-
|
||||
---------------
|
||||
local loaded = false
|
||||
local on = false
|
||||
|
||||
self = PluginManager():CreatePlugin()
|
||||
|
||||
toolbar = self:CreateToolbar("Terrain")
|
||||
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 SetCell = c.SetCell
|
||||
local SetCells = c.SetCells
|
||||
local AutowedgeCells = c.AutowedgeCells
|
||||
local AutowedgeCell = c.AutowedgeCell
|
||||
local WorldToCellPreferSolid = c.WorldToCellPreferSolid
|
||||
local CellCenterToWorld = c.CellCenterToWorld
|
||||
local AutoWedge = c.AutowedgeCell
|
||||
|
||||
local buildTerrainMode = 'Add'
|
||||
local removeTerrainMode = 'Remove'
|
||||
|
||||
-----------------
|
||||
--DEFAULT VALUES-
|
||||
-----------------
|
||||
local radius = 5
|
||||
local depth = 0
|
||||
local mousedown = false
|
||||
local mousemoving = false
|
||||
local brushheight = nil
|
||||
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 = self: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 j = -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 = nil
|
||||
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 = nil
|
||||
if depth > 0 then
|
||||
local yWithDepth = nil
|
||||
if brushheight then
|
||||
yWithDepth = brushheight + depth
|
||||
else
|
||||
yWithDepth = cellPos.y + depth
|
||||
end
|
||||
|
||||
local lowY = nil
|
||||
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
|
||||
|
||||
function On()
|
||||
if not c then
|
||||
return
|
||||
end
|
||||
if self then
|
||||
self: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
|
||||
|
||||
function Off()
|
||||
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.
|
||||
function CreateStandardLabel(labelName,
|
||||
pos,
|
||||
size,
|
||||
text,
|
||||
parent)
|
||||
local label = Instance.new("TextLabel", parent)
|
||||
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", game:GetService("CoreGui"))
|
||||
g.Name = "TerrainBrushGui"
|
||||
|
||||
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",elevationHelpFrame)
|
||||
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).
|
||||
]]
|
||||
|
||||
--current radius display label
|
||||
radl = Instance.new("TextLabel", elevationFrame)
|
||||
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
|
||||
|
||||
--radius slider
|
||||
radSliderGui, radSliderPosition = RbxGui.CreateSlider(5, 0, UDim2.new(0, 10, 0, 92))
|
||||
radSliderGui.Parent = elevationFrame
|
||||
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
|
||||
dfl = Instance.new("TextLabel", elevationFrame)
|
||||
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
|
||||
|
||||
--depth factor slider
|
||||
addSliderGui, addSliderPosition = RbxGui.CreateSlider(31, 0, UDim2.new(0, 10, 0,132))
|
||||
addSliderGui.Parent = elevationFrame
|
||||
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
|
||||
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(mode)
|
||||
if mode == buildTerrainMode then
|
||||
addSliderGui.Visible = true
|
||||
local hold = addSliderPosition.Value
|
||||
addSliderPosition.Value = 0
|
||||
addSliderPosition.Value = hold
|
||||
removeSliderGui.Visible = false
|
||||
elseif mode == 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.
|
||||
buildModeLabel = CreateStandardLabel("BuildModeLabel", UDim2.new(0, 8, 0, 10), UDim2.new(0, 67, 0, 14), "Build Mode:", elevationFrame)
|
||||
buildModeDropdown, 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)
|
||||
|
||||
self.Deactivation:connect(function()
|
||||
Off()
|
||||
end)
|
||||
|
||||
--------------------------
|
||||
--SUCCESSFUL LOAD MESSAGE-
|
||||
--------------------------
|
||||
|
||||
loaded = true
|
||||
253
client/studio/resources/BuiltInPlugins/terrain/06 - craters.lua
Normal file
@@ -0,0 +1,253 @@
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
---------------
|
||||
--PLUGIN SETUP-
|
||||
---------------
|
||||
local loaded = false
|
||||
local on = false
|
||||
|
||||
self = PluginManager():CreatePlugin()
|
||||
mouse = self:GetMouse()
|
||||
mouse.Button1Down:connect(function() onClicked(mouse) end)
|
||||
toolbar = self:CreateToolbar("Terrain")
|
||||
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
|
||||
--d is the depth factor, a percent of the depth of a perfect sphere
|
||||
function makeCrater(x, y, z, r, d)
|
||||
local heightmap = {}
|
||||
for i = x - (r + 1), x + (r + 1) do
|
||||
heightmap[i] = {}
|
||||
end
|
||||
|
||||
for j = 0, r + 1 do
|
||||
local cellschanged = false
|
||||
for i = x - (r + 1), x + (r + 1) do
|
||||
for k = z - (r + 1), z + (r + 1) do
|
||||
distance = math.sqrt(math.pow(dist(x, z, i, k), 2) + math.pow(y - (y - j*(100/d)), 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
|
||||
material, wedge, rotation = 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, r do
|
||||
wait(0)
|
||||
|
||||
i = x - ri
|
||||
for k = z - r, z + r do
|
||||
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 - r, z + r do
|
||||
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(mouse)
|
||||
if on and not debounce then
|
||||
debounce = true
|
||||
|
||||
local cellPos = WorldToCellPreferSolid(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.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
|
||||
|
||||
function On()
|
||||
if not c then
|
||||
return
|
||||
end
|
||||
if self then
|
||||
self:Activate(true)
|
||||
end
|
||||
if toolbarbutton then
|
||||
toolbarbutton:SetActive(true)
|
||||
end
|
||||
if craterDragBar then
|
||||
craterDragBar.Visible = true
|
||||
end
|
||||
on = true
|
||||
end
|
||||
|
||||
function Off()
|
||||
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
|
||||
g = Instance.new("ScreenGui", game:GetService("CoreGui"))
|
||||
g.Name = "CraterGui"
|
||||
|
||||
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",craterHelpFrame)
|
||||
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.
|
||||
]]
|
||||
|
||||
--current radius display label
|
||||
radl = Instance.new("TextLabel", craterFrame)
|
||||
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
|
||||
|
||||
--radius slider
|
||||
radSliderGui, radSliderPosition = RbxGui.CreateSlider(128, 0, UDim2.new(0, 10, 0, 32))
|
||||
radSliderGui.Parent = craterFrame
|
||||
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
|
||||
dfl = Instance.new("TextLabel", craterFrame)
|
||||
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
|
||||
|
||||
--depth factor slider
|
||||
dfSliderGui, dfSliderPosition = RbxGui.CreateSlider(100, 0, UDim2.new(0, 10, 0, 72))
|
||||
dfSliderGui.Parent = craterFrame
|
||||
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
|
||||
|
||||
self.Deactivation:connect(function()
|
||||
Off()
|
||||
end)
|
||||
|
||||
--------------------------
|
||||
--SUCCESSFUL LOAD MESSAGE-
|
||||
--------------------------
|
||||
loaded = true
|
||||
291
client/studio/resources/BuiltInPlugins/terrain/08 - roads.lua
Normal file
@@ -0,0 +1,291 @@
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
---------------
|
||||
--PLUGIN SETUP-
|
||||
---------------
|
||||
local loaded = false
|
||||
local on = false
|
||||
|
||||
self = PluginManager():CreatePlugin()
|
||||
mouse = self:GetMouse()
|
||||
mouse.Button1Down:connect(function() onClicked(mouse) end)
|
||||
toolbar = self:CreateToolbar("Terrain")
|
||||
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 SetCell = c.SetCell
|
||||
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,roadFrame, roadCloseEvent = nil
|
||||
|
||||
-----------------------
|
||||
--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 h = height, 0, -1 do
|
||||
material, wedge, rotation = GetCell(c, x, h, z)
|
||||
if material.Value > 0 then
|
||||
return true
|
||||
elseif height == 0 then
|
||||
return false
|
||||
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, y1, x2, y2), 2) + math.pow(z2-z1, 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(x1, z1, x2, z2, h, p)
|
||||
if x2 < x1 then
|
||||
incx = -1
|
||||
n = 1
|
||||
else
|
||||
incx = 1
|
||||
n = -1
|
||||
end
|
||||
for x = x1, x2+n, incx do
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(x, h+1, z1-1), Vector3int16.new(x, h+3, z1+1)), 0, 0, 0)
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(x, h, z1-1), Vector3int16.new(x, h, z1+1)), DefaultTerrainMaterial, 0, 0)
|
||||
end
|
||||
if p then
|
||||
for x = x1, x2+n, 16*incx do
|
||||
if coordCheck(x, z1, h-1) then
|
||||
coordHeight(x, z1, h-1)
|
||||
end
|
||||
if coordCheck(x, z1-1, h-1) then
|
||||
coordHeight(x, z1-1, h-1)
|
||||
end
|
||||
if coordCheck(x, z1+1, h-1) then
|
||||
coordHeight(x, z1+1, h-1)
|
||||
end
|
||||
end
|
||||
end
|
||||
if z2 < z1 then
|
||||
incz = -1
|
||||
m = 1
|
||||
n = 2
|
||||
else
|
||||
incz = 1
|
||||
m = -1
|
||||
n = -2
|
||||
end
|
||||
for z = z1+m, z2+n, incz do
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(x2-1, h+1, z), Vector3int16.new(x2+1, h+3, z)), 0, 0, 0)
|
||||
SetCells(c, Region3int16.new(Vector3int16.new(x2-1, h, z), Vector3int16.new(x2+1, h, z)), DefaultTerrainMaterial, 0, 0)
|
||||
end
|
||||
if p then
|
||||
for z = z1+m, z2+n, 16*incz do
|
||||
if coordCheck(x2, z, h-1) then
|
||||
coordHeight(x2, z, h-1)
|
||||
end
|
||||
if coordCheck(x2-1, z, h-1) then
|
||||
coordHeight(x2-1, z, h-1)
|
||||
end
|
||||
if coordCheck(x2+1, z, h-1) then
|
||||
coordHeight(x2+1, z, h-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 (mouse)
|
||||
if on then
|
||||
|
||||
local cellPos = WorldToCellPreferEmpty(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))
|
||||
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(mouse.Hit.x, mouse.Hit.y, mouse.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, c)
|
||||
mode = 0
|
||||
else
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function On()
|
||||
if not c then
|
||||
return
|
||||
end
|
||||
if self then
|
||||
self:Activate(true)
|
||||
end
|
||||
if toolbarbutton then
|
||||
toolbarbutton:SetActive(true)
|
||||
end
|
||||
if roadDragBar then
|
||||
roadDragBar.Visible = true
|
||||
end
|
||||
|
||||
mode = 0
|
||||
on = true
|
||||
end
|
||||
|
||||
function Off()
|
||||
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", game:GetService("CoreGui"))
|
||||
g.Name = "RoadGui"
|
||||
|
||||
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",roadFrame)
|
||||
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
|
||||
|
||||
roadHelpFrame.Size = UDim2.new(0,200,0,150)
|
||||
local helpText = Instance.new("TextLabel",roadHelpFrame)
|
||||
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.]]
|
||||
|
||||
self.Deactivation:connect(function()
|
||||
Off()
|
||||
end)
|
||||
|
||||
--------------------------
|
||||
--SUCCESSFUL LOAD MESSAGE-
|
||||
--------------------------
|
||||
loaded = true
|
||||
@@ -0,0 +1,625 @@
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
---------------
|
||||
--PLUGIN SETUP-
|
||||
---------------
|
||||
loaded = false
|
||||
on = false
|
||||
|
||||
self = PluginManager():CreatePlugin()
|
||||
mouse = self:GetMouse()
|
||||
mouse.Button1Down:connect(function() mouseDown(mouse) end)
|
||||
mouse.Button1Up:connect(function() mouseUp(mouse) end)
|
||||
mouse.Move:connect(function() mouseMove(mouse) end)
|
||||
|
||||
toolbar = self:CreateToolbar("Terrain")
|
||||
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 = nil
|
||||
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
|
||||
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( mouse )
|
||||
local cellPos = WorldToCellPreferSolid(c, mouse.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(mouse, massSelection)
|
||||
currSelectionUpdate, currSelectionDestroy = RbxUtil.SelectTerrainRegion(calculateRegion(mouse),BrickColor.new("Lime green"),massSelection,game.CoreGui)
|
||||
end
|
||||
|
||||
function updateSelection(mouse)
|
||||
if not currSelectionUpdate then
|
||||
createSelection(mouse, radius > 4)
|
||||
else
|
||||
currSelectionUpdate(calculateRegion(mouse),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 = nil
|
||||
|
||||
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(mouse)
|
||||
if on and mouse.Target == game.Workspace.Terrain then
|
||||
dragging = true
|
||||
if mouse and mouse.Hit then
|
||||
if mouse.Target == game.Workspace.Terrain then
|
||||
local newCell = WorldToCellPreferSolid(c, mouse.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(mouse)
|
||||
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(mouse)
|
||||
if on then
|
||||
if mouse.Target == game.Workspace.Terrain then
|
||||
if lastCell ~= WorldToCellPreferSolid(c, mouse.Hit.p) then
|
||||
updateSelection(mouse)
|
||||
local newCell = WorldToCellPreferSolid(c, mouse.Hit.p)
|
||||
|
||||
if dragging then
|
||||
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( lastCell,newCell)
|
||||
local currCell = lastCell
|
||||
|
||||
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
|
||||
|
||||
function On()
|
||||
if not c then
|
||||
return
|
||||
end
|
||||
self:Activate(true)
|
||||
toolbarbutton:SetActive(true)
|
||||
|
||||
dragBar.Visible = true
|
||||
on = true
|
||||
end
|
||||
|
||||
function Off()
|
||||
toolbarbutton:SetActive(false)
|
||||
|
||||
destroySelection()
|
||||
dragBar.Visible = false
|
||||
on = false
|
||||
end
|
||||
|
||||
------
|
||||
--GUI-
|
||||
------
|
||||
|
||||
--screengui
|
||||
local g = Instance.new("ScreenGui", game:GetService("CoreGui"))
|
||||
g.Name = "MaterialPainterGui"
|
||||
|
||||
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",helpFrame)
|
||||
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.]]
|
||||
|
||||
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(radius)
|
||||
return 'Size: ' .. (((radius-1) * 2) + 1)
|
||||
end
|
||||
|
||||
--current radius display label
|
||||
radiusLabel = Instance.new("TextLabel", containerFrame)
|
||||
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
|
||||
|
||||
--radius slider
|
||||
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
|
||||
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
|
||||
|
||||
self.Deactivation:connect(function()
|
||||
Off()
|
||||
end)
|
||||
|
||||
--------------------------
|
||||
--SUCCESSFUL LOAD MESSAGE-
|
||||
--------------------------
|
||||
loaded = true
|
||||
411
client/studio/resources/BuiltInPlugins/terrain/10 - stamper.lua
Normal file
@@ -0,0 +1,411 @@
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
---------------
|
||||
--PLUGIN SETUP-
|
||||
---------------
|
||||
local on = false
|
||||
local loaded = false
|
||||
|
||||
self = PluginManager():CreatePlugin()
|
||||
|
||||
toolbar = self:CreateToolbar("Terrain")
|
||||
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 partButtonOn = true
|
||||
|
||||
local currStampGui = nil
|
||||
local currStampId = nil
|
||||
local recentsFrame = nil
|
||||
local waterTypeChangedEvent = nil
|
||||
local waterForceAndDirection = {"None","NegX"}
|
||||
|
||||
local setPanelVisibility = nil
|
||||
local getPanelVisibility = nil
|
||||
|
||||
local stampControl = nil
|
||||
local lastStampModel = nil
|
||||
|
||||
local keyCon = nil
|
||||
|
||||
-- ids of users we want to load sets in from
|
||||
local userSetIds = {11744447,18881789,18881808,18881829,18881853,18881866}
|
||||
local recentButtonStack = {}
|
||||
|
||||
-- mouse management
|
||||
local mouse = nil
|
||||
|
||||
-- Libraries
|
||||
local RbxStamper = nil
|
||||
local RbxGui = nil
|
||||
|
||||
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()
|
||||
partButtonOn = true
|
||||
end
|
||||
|
||||
function partOff()
|
||||
setPanelVisibility(false)
|
||||
if lastStampModel then
|
||||
if stampControl then
|
||||
stampControl.Destroy()
|
||||
end
|
||||
setupStamper(lastStampModel, mouse)
|
||||
end
|
||||
partButtonOn = false
|
||||
end
|
||||
|
||||
function handlePartShow()
|
||||
if getPanelVisibility() then
|
||||
partOff()
|
||||
else
|
||||
partOn()
|
||||
end
|
||||
end
|
||||
|
||||
function On()
|
||||
if not game.Workspace.Terrain then
|
||||
return
|
||||
end
|
||||
|
||||
if self then
|
||||
self:Activate(true)
|
||||
mouse = self: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
|
||||
|
||||
function Off()
|
||||
|
||||
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
|
||||
------------------------------------------------------------------------------------------
|
||||
-- Connect to deactivation event once the handler is defined
|
||||
------------------------------------------------------------------------------------------
|
||||
self.Deactivation:connect(function()
|
||||
Off()
|
||||
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,765 @@
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
---------------
|
||||
--PLUGIN SETUP-
|
||||
---------------
|
||||
local loaded = false
|
||||
local on = false
|
||||
|
||||
self = PluginManager():CreatePlugin()
|
||||
local mouse = self:GetMouse()
|
||||
mouse.Button1Down:connect(function() mouseDown(mouse) end)
|
||||
mouse.Button1Up:connect(function() mouseUp(mouse) end)
|
||||
|
||||
toolbar = self:CreateToolbar("Terrain")
|
||||
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 = nil
|
||||
|
||||
-----------------
|
||||
--DEFAULT VALUES-
|
||||
-----------------
|
||||
local startCell = nil
|
||||
local processing = false
|
||||
|
||||
-- gui values
|
||||
local screenGui = nil
|
||||
local dragBar, closeEvent, helpFrame, lastCell = nil
|
||||
local progressBar, setProgressFunc, cancelEvent = nil
|
||||
local ConfirmationPopupObject = nil
|
||||
|
||||
--- 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://www.roblox.com/asset/?id=82741829")
|
||||
|
||||
|
||||
------------------------- OBJECT DEFINITIONS ---------------------
|
||||
|
||||
-- helper function for objects
|
||||
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.
|
||||
MouseHighlighter = {}
|
||||
MouseHighlighter.__index = MouseHighlighter
|
||||
|
||||
-- Create a mouse movement highlighter.
|
||||
-- plugin - Plugin to get the mouse from.
|
||||
function MouseHighlighter.Create(mouseUse)
|
||||
local highlighter = {}
|
||||
|
||||
local mouse = 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.
|
||||
mouse.Move:connect(function() MouseMoved() end)
|
||||
|
||||
mouse.Button1Down:connect(function() highlighter.mouseDown = true end)
|
||||
mouse.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",highlighter.selectionPart)
|
||||
billboardGui.Size = UDim2.new(5,0,5,0)
|
||||
billboardGui.StudsOffset = Vector3.new(0,2.5,0)
|
||||
|
||||
local imageLabel = Instance.new("ImageLabel",billboardGui)
|
||||
imageLabel.BackgroundTransparency = 1
|
||||
imageLabel.Size = UDim2.new(1,0,1,0)
|
||||
imageLabel.Position = UDim2.new(-0.35,0,-0.5,0)
|
||||
imageLabel.Image = "http://www.roblox.com/asset/?id=82741829"
|
||||
|
||||
local lastTweenChange = nil
|
||||
|
||||
function startTween()
|
||||
while tweenUp or tweenDown do
|
||||
wait(0.2)
|
||||
end
|
||||
lastTweenChange = tick()
|
||||
|
||||
delay(0,function ( )
|
||||
local thisTweenStamp = lastTweenChange
|
||||
local tweenDown, tweenUp = nil
|
||||
|
||||
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
|
||||
|
||||
function stopTween()
|
||||
lastTweenChange = tick()
|
||||
end
|
||||
|
||||
mouse.TargetFilter = highlighter.selectionPart
|
||||
setmetatable(highlighter, MouseHighlighter)
|
||||
|
||||
-- Function to call when the mouse has moved. Updates where to display the highlighter.
|
||||
function MouseMoved()
|
||||
if on and not processing then
|
||||
UpdatePosition(mouse.Hit)
|
||||
end
|
||||
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
|
||||
|
||||
if not mouse.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 = nil
|
||||
|
||||
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(mouse.Hit.x, mouse.Hit.y, mouse.Hit.z))
|
||||
else
|
||||
cellPos = WorldToCellPreferEmpty(c, Vector3.new(mouse.Hit.x, mouse.Hit.y, mouse.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
|
||||
|
||||
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.
|
||||
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", parent)
|
||||
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.
|
||||
buttonTextColor = Color3.new(1, 1, 1);
|
||||
buttonFont = Enum.Font.ArialBold;
|
||||
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)
|
||||
button = Instance.new("TextButton", parent)
|
||||
button.Name = name
|
||||
button.Position = pos
|
||||
|
||||
button.Size = UDim2.new(0,120,0,40)
|
||||
button.Text = text
|
||||
|
||||
if size then
|
||||
button.Size = size
|
||||
end
|
||||
|
||||
button.Style = Enum.ButtonStyle.RobloxButton
|
||||
|
||||
button.TextColor3 = buttonTextColor
|
||||
button.Font = buttonFont
|
||||
button.FontSize = buttonFontSize
|
||||
|
||||
button.MouseButton1Click:connect(funcOnPress)
|
||||
|
||||
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",screenGui)
|
||||
popup.confirmationFrame.Name = "ConfirmationFrame"
|
||||
popup.confirmationFrame.Size = UDim2.new(0, 280, 0, 160)
|
||||
popup.confirmationFrame.Position = UDim2.new(.5, -popup.confirmationFrame.Size.X.Offset/2, 0.5, -popup.confirmationFrame.Size.Y.Offset/2)
|
||||
popup.confirmationFrame.Style = Enum.FrameStyle.RobloxRound
|
||||
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(mouse)
|
||||
if on and mouse.Target == game.Workspace.Terrain then
|
||||
startCell = mouseHighlighter:GetPosition()
|
||||
end
|
||||
end
|
||||
|
||||
function mouseUp(mouse)
|
||||
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(.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(arguments)
|
||||
processing = false
|
||||
spin = false
|
||||
end)
|
||||
|
||||
local spinnerFrame = Instance.new("Frame",progressBar)
|
||||
spinnerFrame.Name = "Spinner"
|
||||
spinnerFrame.Size = UDim2.new(0, 80, 0, 80)
|
||||
spinnerFrame.Position = UDim2.new(0.5, -40, 0.5, -55)
|
||||
spinnerFrame.BackgroundTransparency = 1
|
||||
|
||||
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(.5+.3*math.cos(math.rad(45*spinnerNum)), -8, .5+.3*math.sin(math.rad(45*spinnerNum)), -8)
|
||||
spinnerImage.BackgroundTransparency = 1
|
||||
spinnerImage.Image = "http://www.roblox.com/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://www.roblox.com/Asset/?id=45880668"
|
||||
else
|
||||
spinnerIcons[pos+1].Image = "http://www.roblox.com/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 i = 1, #currYDepthChecks do
|
||||
table.insert(newYChecks,{xPos = currYDepthChecks[i].xPos, yPos = currYDepthChecks[i].yPos, zPos = currYDepthChecks[i].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 i = 1, #newCellsToCheck do
|
||||
table.insert(cellCheckQueue,{xPos = newCellsToCheck[i].xPos, yPos = newCellsToCheck[i].yPos, zPos = newCellsToCheck[i].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
|
||||
|
||||
function On()
|
||||
if not c then
|
||||
return
|
||||
end
|
||||
if self then
|
||||
self:Activate(true)
|
||||
end
|
||||
if toolbarbutton then
|
||||
toolbarbutton:SetActive(true)
|
||||
end
|
||||
if dragBar then
|
||||
dragBar.Visible = true
|
||||
end
|
||||
|
||||
on = true
|
||||
end
|
||||
|
||||
function Off()
|
||||
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", game:GetService("CoreGui"))
|
||||
screenGui.Name = "FloodFillGui"
|
||||
|
||||
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",helpFrame)
|
||||
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.
|
||||
]]
|
||||
|
||||
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)
|
||||
|
||||
self.Deactivation:connect(function()
|
||||
Off()
|
||||
end)
|
||||
|
||||
--------------------------
|
||||
--SUCCESSFUL LOAD MESSAGE-
|
||||
--------------------------
|
||||
loaded = true
|
||||
@@ -0,0 +1,35 @@
|
||||
while game == nil do
|
||||
wait(1/30)
|
||||
end
|
||||
|
||||
self = PluginManager():CreatePlugin()
|
||||
|
||||
toolbar = self:CreateToolbar("Terrain")
|
||||
toolbarbutton = toolbar:CreateButton("Convert To Smooth", "Convert To Smooth", "smooth.png")
|
||||
toolbarbutton.Click:connect(function()
|
||||
local RbxGui = LoadLibrary("RbxGui")
|
||||
local g = Instance.new("ScreenGui", game:GetService("CoreGui"))
|
||||
|
||||
if not workspace.Terrain.IsSmooth then
|
||||
if workspace.Terrain:CountCells() > 0 then
|
||||
local result = nil
|
||||
local confirm = RbxGui.CreateMessageDialog("Convert To Smooth?",
|
||||
"You are converting voxel terrain to smooth voxel terrain. Some materials are not supported any more. This action can not be undone. Proceed?",
|
||||
{{Text="Convert", Function=function() result = true end}, {Text="Cancel", Function=function() result = false end}})
|
||||
|
||||
confirm.Parent = g
|
||||
|
||||
while result == nil do
|
||||
wait(0.1)
|
||||
end
|
||||
|
||||
confirm:Destroy()
|
||||
|
||||
if not result then
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
workspace.Terrain:ConvertToSmooth()
|
||||
end
|
||||
end)
|
||||
BIN
client/studio/resources/BuiltInPlugins/terrain/brush.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/BuiltInPlugins/terrain/builder.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/BuiltInPlugins/terrain/craters.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/BuiltInPlugins/terrain/destroyer.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/BuiltInPlugins/terrain/elevation.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/BuiltInPlugins/terrain/floodFill.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/BuiltInPlugins/terrain/materialBrush.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/BuiltInPlugins/terrain/roads.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/BuiltInPlugins/terrain/smooth.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
client/studio/resources/BuiltInPlugins/terrain/stamp.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/BuiltInPlugins/terrain/terrain.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
245
client/studio/resources/IntelleSenseMetadata.xml
Normal file
@@ -0,0 +1,245 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<StudioAutocomplete>
|
||||
<LuaLibrary name="math">
|
||||
<StaticFunction name ="abs" tooltip="number math.abs(x)%1Returns the absolute value of x." />
|
||||
<StaticFunction name ="acos" tooltip="number math.acos(x)%1Returns the arc cosine of x (in radians)." />
|
||||
<StaticFunction name ="asin" tooltip="number math.asin(x)%1Returns the arc sine of x (in radians)." />
|
||||
<StaticFunction name ="atan" tooltip="number math.atan(x)%1Returns the arc tangent of x (in radians)." />
|
||||
<StaticFunction name ="atan2" tooltip="number math.atan2(y, x)%1Returns the arc tangent of y/x (in radians), but uses the signs of both parameters to find the quadrant of the result. (It also handles correctly the case of x being zero.)" />
|
||||
<StaticFunction name ="ceil" tooltip="number math.ceil(x)%1Returns the smallest integer larger than or equal to x." />
|
||||
<StaticFunction name ="cos" tooltip="number math.cos(x)%1Returns the cosine of x (assumed to be in radians)." />
|
||||
<StaticFunction name ="cosh" tooltip="number math.cosh(x)%1Returns the hyperbolic cosine of x." />
|
||||
<StaticFunction name ="deg" tooltip="number math.deg(x)%1Returns the angle x (given in radians) in degrees." />
|
||||
<StaticFunction name ="exp" tooltip="number math.exp(x)%1Returns the value e^x." />
|
||||
<StaticFunction name ="floor" tooltip="number math.floor(x)%1Returns the largest integer smaller than or equal to x." />
|
||||
<StaticFunction name ="fmod" tooltip="number math.fmod(x, y)%1Returns the remainder of the division of x by y that rounds the quotient towards zero." />
|
||||
<StaticFunction name ="frexp" tooltip="number math.frexp(x)%1Returns m and e such that x = m2^e, e is an integer and the absolute value of m is in the range [0.5, 1) (or zero when x is zero)." />
|
||||
<StaticFunction name ="ldexp" tooltip="number math.ldexp(x, e)%1Returns m2^e (e should be an integer)." />
|
||||
<StaticFunction name ="log" tooltip="number math.log(x)%1Returns the natural logarithm of x." />
|
||||
<StaticFunction name ="log10" tooltip="number math.log10(x)%1Returns the base-10 logarithm of x." />
|
||||
<StaticFunction name ="max" tooltip="number math.max(x, ...)%1Returns the maximum value among its arguments." />
|
||||
<StaticFunction name ="min" tooltip="number math.min(x, ...)%1Returns the minimum value among its arguments." />
|
||||
<StaticFunction name ="modf" tooltip="number math.modf(x)%1Returns two numbers, the integral part of x and the fractional part of x." />
|
||||
<StaticFunction name ="noise" tooltip="number math.noise(x [, y [, z]])%1Returns a value between -0.5 and 0.5 generated from its arguments" />
|
||||
<StaticFunction name ="pow" tooltip="number math.pow(x, y)%1Returns x^y. (You can also use the expression x^y to compute this value.)" />
|
||||
<StaticFunction name ="rad" tooltip="number math.rad(x)%1Returns the angle x (given in degrees) in radians." />
|
||||
<StaticFunction name ="random" tooltip="number math.random([m [, n]])%1This function is an interface to the simple pseudo-random generator function rand provided by ANSI C. (No guarantees can be given for its statistical properties.)%1%1When called without arguments, returns a uniform pseudo-random real number in the range [0,1). When called with an integer number m, math.random returns a uniform%1pseudo-random integer in the range [1, m]. When called with two integer numbers m and n, math.random returns a uniform pseudo-random integer in the range [m, n]." />
|
||||
<StaticFunction name ="randomseed" tooltip="number math.randomseed(x)%1Sets x as the seed for the pseudo-random generator: equal seeds produce equal sequences of numbers." />
|
||||
<StaticFunction name ="sin" tooltip="number math.sin(x)%1Returns the sine of x (assumed to be in radians)." />
|
||||
<StaticFunction name ="sinh" tooltip="number math.sinh(x)%1Returns the hyperbolic sine of x." />
|
||||
<StaticFunction name ="sqrt" tooltip="number math.sqrt(x)%1Returns the square root of x. (You can also use the expression x^0.5 to compute this value.)" />
|
||||
<StaticFunction name ="tan" tooltip="number math.tan(x)%1Returns the tangent of x (assumed to be in radians)." />
|
||||
<StaticFunction name ="tanh" tooltip="number math.tanh(x)%1Returns the hyperbolic tangent of x." />
|
||||
<StaticProperty name="huge" tooltip="The value HUGE_VAL, a value larger than or equal to any other numerical value."/>
|
||||
<StaticProperty name ="pi" tooltip="The value of pi."/>
|
||||
</LuaLibrary>
|
||||
<LuaLibrary name="string">
|
||||
<StaticFunction name="byte" tooltip="string string.byte(s[,i[,j]])%1Returns the internal numerical codes of the characters s[i], s[i+1], ..., s[j]. The default value for i is 1; the default value for j is i. These indices are corrected following the same rules of function string.sub."/>
|
||||
<StaticFunction name="char" tooltip="string string.char(...)%1Receives zero or more integers. Returns a string with length equal to the number of arguments, in which each character has the internal numerical code equal to its corresponding argument."/>
|
||||
<StaticFunction name="dump" tooltip="string string.dump(function)%1Returns a string containing a binary representation of the given function, so that a later load on this string returns a copy of the function (but with new upvalues)."/>
|
||||
<StaticFunction name="find" tooltip="number string.find(s, pattern [, init [, plain]])%1Looks for the first match of pattern in the string s. If it finds a match, then find returns the indices of s where this occurrence starts and ends; otherwise, it returns nil. A third, optional numerical argument init specifies where to start the search; its default value is 1 and can be negative. A value of true as a fourth, optional argument plain turns off the pattern matching facilities, so the function does a plain 'find='' substring=''' operation"/>
|
||||
<StaticFunction name="format" tooltip="string string.format(formatstring, ...)%1Returns a formatted version of its variable number of arguments following the description given in its first argument (which must be a string)."/>
|
||||
<StaticFunction name="gmatch" tooltip="function string.gmatch(s, pattern)%1Returns an iterator function that, each time it is called, returns the next captures from pattern over the string s."/>
|
||||
<StaticFunction name="gsub" tooltip="string string.gsub(s, pattern, repl [, n])%1Returns a copy of s in which all (or the first n, if given) occurrences of the pattern have been replaced by a replacement string specified by repl, which can be a string, a table, or a function. gsub also returns, as its second value, the total number of matches that occurred."/>
|
||||
<StaticFunction name="len" tooltip="number string.len(s)%1Receives a string and returns its length."/>
|
||||
<StaticFunction name="lower" tooltip="string string.lower(s)%1Receives a string and returns a copy of this string with all uppercase letters changed to lowercase."/>
|
||||
<StaticFunction name="rep" tooltip="string string.rep(s, n [, sep])%1Returns a string that is the concatenation of n copies of the string s separated by the string sep."/>
|
||||
<StaticFunction name="reverse" tooltip="string string.reverse(s)%1Returns a string that is the string s reversed."/>
|
||||
<StaticFunction name="sub" tooltip="string string.sub(s, i [, j])%1Returns the substring of s that starts at i and continues until j; i and j can be negative. If j is absent, then it is assumed to be equal to -1 (which is the same as the string length). "/>
|
||||
<StaticFunction name="upper" tooltip="string string.upper(s)%1Receives a string and returns a copy of this string with all lowercase letters changed to uppercase. All other characters are left unchanged."/>
|
||||
</LuaLibrary>
|
||||
<LuaLibrary name="table">
|
||||
<StaticFunction name="concat" tooltip="string table.concat(list [, sep [, i [, j]]])%1Given a list where all elements are strings or numbers, returns the string list[i]..sep..list[i+1] ... sep..list[j]. The default value for sep is the empty string, the default for i is 1, and the default for j is #list. If i is greater than j, returns the empty string."/>
|
||||
<StaticFunction name="foreach" tooltip="void table.foreach(table, f)%1Apply the function f to the elements of the table passed" />
|
||||
<StaticFunction name="foreachi" tooltip="void table.foreachi(table, f)%1This is similar to table.foreach() except that index-value pairs are passed, not key-value pairs." />
|
||||
<StaticFunction name="getn" tooltip="number table.getn(table)%1Returns the number of elements in the table passed" />
|
||||
<StaticFunction name="insert" tooltip="void table.insert(list, [pos,] value)%1Inserts element value at position pos in list, shifting up the elements list[pos], list[pos+1], ..., list[#list]. The default value for pos is #list+1, so that a call table.insert(t,x) inserts x at the end of list t."/>
|
||||
<StaticFunction name="remove" tooltip="value table.remove(list [, pos])%1Removes from list the element at position pos, returning the value of the removed element. When pos is an integer between 1 and #list, it shifts down the elements list[pos+1], list[pos+2], ..., list[#list] and erases element list[#list]; The index pos can also be 0 when #list is 0, or #list + 1; in those cases, the function erases the element list[pos]."/>
|
||||
<StaticFunction name="sort" tooltip="bool table.sort(list [, comp])%1Sorts list elements in a given order, in-place, from list[1] to list[#list]. If comp is given, then it must be a function that receives two list elements and returns true when the first element must come before the second in the final order (so that not comp(list[i+1],list[i]) will be true after the sort). If comp is not given, then the standard Lua operator [less than] is used instead."/>
|
||||
</LuaLibrary>
|
||||
<LuaLibrary name="coroutine">
|
||||
<StaticFunction name="create" tooltip="thread coroutine.create(f)%1Creates a new coroutine, with body f. f must be a Lua function."/>
|
||||
<StaticFunction name="resume" tooltip="bool coroutine.resume(co [, val1, ...])%1Starts or continues the execution of coroutine co. The first time you resume a coroutine, it starts running its body. The values val1, ... are passed as the arguments to the body function. If the coroutine has yielded, resume restarts it; the values val1, ... are passed as the results from the yield. If the coroutine runs without any errors, resume returns true plus any values passed to yield (if the coroutine yields) or any values returned by the body function (if the coroutine terminates). If there is any error, resume returns false plus the error message."/>
|
||||
<StaticFunction name="running" tooltip="thread coroutine.running()%1Returns the running coroutine plus a boolean, true when the running coroutine is the main one."/>
|
||||
<StaticFunction name="status" tooltip="string coroutine.status(co)%1Returns the status of coroutine co, as a string: 'running', if the coroutine is running (that is, it called status); 'suspended', if the coroutine is suspended in a call to yield, or if it has not started running yet; 'normal' if the coroutine is active but not running (that is, it has resumed another coroutine); and 'dead' if the coroutine has finished its body function, or if it has stopped with an error."/>
|
||||
<StaticFunction name="wrap" tooltip="function coroutine.wrap(f)%1Creates a new coroutine, with body f. f must be a Lua function. Returns a function that resumes the coroutine each time it is called. Any arguments passed to the function behave as the extra arguments to resume. Returns the same values returned by resume, except the first boolean. In case of error, propagates the error."/>
|
||||
<StaticFunction name="yield" tooltip="void coroutine.yield(...)%1Suspends the execution of the calling coroutine. Any arguments to yield are passed as extra results to resume."/>
|
||||
</LuaLibrary>
|
||||
<ItemStruct name="Instance">
|
||||
<StaticFunction name="new" tooltip="Instance Instance.new(val [, parent])%1Creates an new object of type val. The parent argument is optional; If it is supplied, the object will be parented to that object"/>
|
||||
</ItemStruct>
|
||||
<LuaLibrary name="os">
|
||||
<StaticFunction name="time" tooltip="number os.time()%1Returns the number of seconds since the epoch (1 January 1970, 00:00:00)" />
|
||||
<StaticFunction name="difftime" tooltip="number os.difftime(number t1, number t2)%1Returns the number of seconds from t1 to t2" />
|
||||
</LuaLibrary>
|
||||
<ItemStruct name="Vector2">
|
||||
<StaticFunction name="new" tooltip="Vector2 Vector2.new(x, y)%1Creates a new Vector2 using ordinates x and y"/>
|
||||
<Property name="X" tooltip="Number Vector2.X%1The x-coordinate"/>
|
||||
<Property name="Y" tooltip="Number Vector2.Y%1The y-coordinate"/>
|
||||
<Property name="unit" tooltip="Vector2 Vector2.unit%1A normalized copy of the vector"/>
|
||||
<Property name="magnitude" tooltip="Number Vector2.magnitudeThe length of the vector"/>
|
||||
<Function name="lerp" tooltip="Vector2 Vector2:lerp(Vector2 v, number alpha)%1Returns a Vector2 linearly interpolated between this Vector2 and v by the fraction alpha"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="Vector2int16">
|
||||
<StaticFunction name="new" tooltip="Vector2int16 Vector2int16.new(x, y)%1Creates a new Vector2int16 using ordinates x and y. Similar to Vector2, but uses integral coordinates"/>
|
||||
<Property name="X" tooltip="Number Vector2int16.X%1The x-coordinate"/>
|
||||
<Property name="Y" tooltip="Number Vector2int16.Y%1The y-coordinate"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="Vector3">
|
||||
<StaticFunction name="new" tooltip="Vector3 Vector3.new(x, y, z)%1Constructs a new Vector3 using coordinates x, y, z." />
|
||||
<StaticFunction name="FromNormalId" tooltip="Vector3 Vector3.FromNormalID(Enum.NormalId normalId)%1Constructs a new Vector3 in a particular direction." />
|
||||
<StaticFunction name="FromAxis" tooltip="Vector3 Vector3.FromAxis(Enum.Axis axis)%1Constructs a new Vector3 for a particular axis." />
|
||||
<Property name="X" tooltip="number Vector3.X%1The x-coordinate" />
|
||||
<Property name="Y" tooltip="number Vector3.Y%1The y-coordinate" />
|
||||
<Property name="Z" tooltip="number Vector3.Z%1The z-coordinate" />
|
||||
<Property name="Unit" tooltip="Vector3 Vector3.Unit%1A normalized copy of the vector - one which has the same direction as the original but a magnitude of 1"/>
|
||||
<Property name="Magnitude" tooltip="Number Vector3.Magnitude%1The length of the vector"/>
|
||||
<Function name="Lerp" tooltip="Vector3 Vector3:Lerp(Vector3 goal, number alpha)%1Returns a Vector3 linearly interpolated between this Vector3 and the goal by the fraction alpha"/>
|
||||
<Function name="Dot" tooltip="number Vector3:Dot(Vector3 other)%1Returns a scalar dot product of the two vectors"/>
|
||||
<Function name="Cross" tooltip="Vector3 Vector3:Cross(Vector3 other)%1Returns the cross product of the two vectors"/>
|
||||
<Function name="isClose" tooltip="bool Vector3:isClose(Vector3 other [, number epsilon]%1"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="Vector3int16">
|
||||
<StaticFunction name="new" tooltip="Vector3int16 Vector3int16.new(x, y, z)%1Creates a new Vector3int16 using coordinate x, y, z."/>
|
||||
<Property name="X" tooltip="Number Vector3int16.X%1The x-coordinate" />
|
||||
<Property name="Y" tooltip="Number Vector3int16.Y%1The y-coordinate" />
|
||||
<Property name="Z" tooltip="Number Vector3int16.Z%1The z-coordinate" />
|
||||
</ItemStruct>
|
||||
<ItemStruct name="CFrame">
|
||||
<StaticFunction name="new" tooltip="CFrame CFrame.new(number x, number y, number z)%1Creates a CFrame from position (x, y, z) <a href="http://wiki.roblox.com/index.php/CFrame">More Info</a>"/>
|
||||
<StaticFunction name="fromEulerAnglesXYZ" tooltip="CFrame CFrame.fromEulerAnglesXYZ(number rx, number ry, number rz)%1Creates a rotated CFrame using angles (rx, ry, rz) in radians"/>
|
||||
<StaticFunction name="Angles" tooltip="CFrame CFrame.Angles(number rx, number ry, number rz)%1Equivalent to fromEulerAnglesXYZ"/>
|
||||
<StaticFunction name="fromAxisAngle" tooltip="CFrame CFrame.fromAxisAngle(Vector3 v, number r)%1Creates a rotated CFrame from a Unit Vector3 and a rotation in radians"/>
|
||||
<Property name="p" tooltip="Vector3 CFrame.p%1The 3D position of the CFrame"/>
|
||||
<Property name="X" tooltip="Number CFrame.X%1The x-coordinate of the position" />
|
||||
<Property name="Y" tooltip="Number CFrame.Y%1The y-coordinate of the position" />
|
||||
<Property name="Z" tooltip="Number CFrame.Z%1The z-coordinate of the position" />
|
||||
<Property name="lookVector" tooltip="Vector3 CFrame.lookVector%1Returns the facing direction"/>
|
||||
<Function name="inverse" tooltip="CFrame CFrame:inverse()%1Returns the inverse of this CFrame"/>
|
||||
<Function name="lerp" tooltip="CFrame CFrame:lerp(CFrame goal, float alpha)%1Returns a CFrame interpolated between this CFrame and the goal by the fraction alpha"/>
|
||||
<Function name="toWorldSpace" tooltip="CFrame CFrame:toWorldSpace(CFrame cf)%1Returns a CFrame transformed from Object to World space. Equivalent to [CFrame * cf]"/>
|
||||
<Function name="toObjectSpace" tooltip="CFrame CFrame:toObjectSpace(CFrame cf)%1Returns a CFrame transformed from World to Object space. Equivalent to [CFrame:inverse() * cf]"/>
|
||||
<Function name="pointToWorldSpace" tooltip="Vector3 CFrame:pointToWorldSpace(Vector3 v3)%1Returns a Vector3 transformed from Object to World space. Equivalent to [CFrame * v3]"/>
|
||||
<Function name="pointToObjectSpace" tooltip="Vector3 CFrame:pointtoObjectSpace(Vector3 v3)%1Returns a Vector3 transformed from World to Object space. Equivalent to [CFrame:inverse() * v3]"/>
|
||||
<Function name="vectorToWorldSpace" tooltip="Vector3 CFrame:vectorToWorldSpace(Vector3 v3)%1Returns a Vector3 rotated from Object to World space. Equivalent to [(CFrame - CFrame.p) *v3]"/>
|
||||
<Function name="vectorToObjectSpace" tooltip="Vector3 CFrame:vectorToObjectSpace(Vector3 v3)%1Returns a Vector3 rotated from World to Object space.%1Equivalent to [(CFrame:inverse() - CFrame:inverse().p) * v3]"/>
|
||||
<Function name="components" tooltip="... CFrame:components()%1Returns the values: x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22"/>
|
||||
<Function name="toEulerAnglesXYZ" tooltip="... CFrame:toEulerAnglesXYZ()%1Returns approximate angles that could be used to generate CFrame"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="Region3">
|
||||
<StaticFunction name="new" tooltip="Region3 Region3.new(Vector3, Vector3)%1Creates a new Region3 out of two Vector3 values."/>
|
||||
<Property name="CFrame" tooltip="CFrame%1The center location and rotation of the Region3"/>
|
||||
<Property name="Size" tooltip="Vector3%1The 3D size of the Region3"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="Region3int16">
|
||||
<StaticFunction name="new" tooltip="Region3int16 Region3int16.new(Vector3int16, Vector3int16)%1Creates a new Region3int16 out of two Vector3int16 structs"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="BrickColor">
|
||||
<StaticFunction name="new" tooltip="BrickColor BrickColor.new(r, g, b)%1Returns a BrickColor from the values r, g and b. <a href="http://wiki.roblox.com/index.php/BrickColor">More Info</a>"/>
|
||||
<StaticFunction name="palette" tooltip="BrickColor BrickColor.palette(val)%1Returns a BrickColor from val"/>
|
||||
<StaticFunction name="Random" tooltip="BrickColor BrickColor.Random()%1Returns a random BrickColor"/>
|
||||
<StaticFunction name="White" tooltip="BrickColor BrickColor.White()%1Returns the BrickColor white"/>
|
||||
<StaticFunction name="Gray" tooltip="BrickColor BrickColor.Gray()%1Returns the BrickColor Medium stone grey"/>
|
||||
<StaticFunction name="DarkGray" tooltip="BrickColor BrickColor.DarkGray()%1Returns the BrickColor Dark stone grey" />
|
||||
<StaticFunction name="Black" tooltip="BrickColor BrickColor.Black()%1Returns the BrickColor Black" />
|
||||
<StaticFunction name="Red" tooltip="BrickColor BrickColor.Red()%1Returns the BrickColor Bright Red" />
|
||||
<StaticFunction name="Yellow" tooltip="BrickColor BrickColor.Yellow()%1Returns the BrickColor Bright Yellow" />
|
||||
<StaticFunction name="Green" tooltip="BrickColor BrickColor.Green()%1Returns the BrickColor Dark Green" />
|
||||
<StaticFunction name="Blue" tooltip="BrickColor BrickColor.Blue()%1Returns the BrickColor Bright Blue" />
|
||||
<Property name="Number" tooltip="Number BrickColor.Number%1The unique number that identifies the BrickColor"/>
|
||||
<Property name="Name" tooltip="String BrickColor.Name%1The name associated with the BrickColor"/>
|
||||
<Property name="Color" tooltip="Color3 BrickColor.Color%1The Color3 associated with the BrickColor"/>
|
||||
<Property name="r" tooltip="Number BrickColor.R%1The red component (between 0 and 1)"/>
|
||||
<Property name="g" tooltip="Number BrickColor.G%1The green component (between 0 and 1)"/>
|
||||
<Property name="b" tooltip="Number BrickColor.B%1The blue component (between 0 and 1)"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="Ray">
|
||||
<StaticFunction name="new" tooltip="Ray Ray.new(Vector3 Origin, Vector3 Direction)%1Creates a new Ray with given Origin and Direction"/>
|
||||
<Property name="Origin" tooltip="Vector3 Ray.Origin%1The position of the origin"/>
|
||||
<Property name="Direction" tooltip="Vector3 Ray.Direction%1The direction vector of the ray"/>
|
||||
<Property name="Unit" tooltip="Ray Ray.Unit%1The Ray with a normalized direction"/>
|
||||
<Function name="ClosestPoint" tooltip="Vector3 Ray:ClosestPoint(Vector3 point)%1Returns the closest point on the Ray to point. Note Rays are unidirectional"/>
|
||||
<Function name="Distance" tooltip="Number Ray:Distance(Vector3 point)%1 Returns the distance from point to ClosestPoint(point)"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="UDim">
|
||||
<StaticFunction name="new" tooltip="UDim UDim.new(number Scale, number Offset)%1Creates a new UDim from components"/>
|
||||
<Property name="Scale" tooltip="Number UDim.Scale%1The scale value"/>
|
||||
<Property name="Offset" tooltip="Number UDim.Offset%1The offset value"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="UDim2">
|
||||
<StaticFunction name="new" tooltip="UDim2 UDim2.new(Number XScale, Number XOffset, Number YScale, Number YOffset)%1"/>
|
||||
<Property name="X" tooltip="The x dimension scale and offset" />
|
||||
<Property name="Y" tooltip="The y dimension scale and offset" />
|
||||
</ItemStruct>
|
||||
<ItemStruct name="Color3">
|
||||
<StaticFunction name="new" tooltip="Color3 Color3.new(r, g, b)%1Returns a Color3 from the values r, g and b. <a href="http://wiki.roblox.com/index.php/Color3">More Info</a>"/>
|
||||
<Property name="r" tooltip="Number Color3.r%1The red value of the color"/>
|
||||
<Property name="g" tooltip="Number Color3.g%1The green value of the color"/>
|
||||
<Property name="b" tooltip="Number Color3.b%1The blue value of the color"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="ColorSequence">
|
||||
<StaticFunction name="new" tooltip="ColorSequence ColorSequence.new(Color3 c)%1Creates a sequence of two keypoints with `c` for each value"/>
|
||||
<StaticFunction name="new" tooltip="ColorSequence ColorSequence.new(Color3 c0, Color3 c1)%1Creates a sequence of two keypoints with `c0` and `c1` as the value"/>
|
||||
<Property name="Keypoints" tooltip="Array ColorSequence.Keypoints%1An array containing keypoint values for the ColorSequence"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="NumberSequence">
|
||||
<StaticFunction name="new" tooltip="NumberSequence NumberSequence.new(float n)%1Creates a sequence of two keypoints with `n` for each value"/>
|
||||
<StaticFunction name="new" tooltip="NumberSequence NumberSequence.new(float n0, float n1)%1Creates a sequence of two keypoints with `n0` and `n1` as the value"/>
|
||||
<StaticFunction name="new" tooltip="NumberSequence NumberSequence.new(Array keypoints)%1Creates a sequence from a list of keypoints"/>
|
||||
<Property name="Keypoints" tooltip="Array NumberSequence.Keypoints%1An array containing keypoint values for the NumberSequence"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="NumberRange">
|
||||
<StaticFunction name="new" tooltip="NumberRange NumberRange.new(float value)%1Creates a new NumberRange with the same minimum and maximum"/>
|
||||
<StaticFunction name="new" tooltip="NumberRange NumberRange.new(float min, float max)%1Creates a new NumberRange with the provided minimum and maximum."/>
|
||||
<Property name="Min" tooltip="float NumberRange.Min%1Minimum value. Will always be less than or equal to the maximum."/>
|
||||
<Property name="Max" tooltip="float NumberRange.Max%1Maximum value. Will always be greater than or equal to the minimum."/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="Faces">
|
||||
<StaticFunction name="new" tooltip="Faces Faces.new(NormalId, NormalId, ...)%1Creates a new Faces using list of faces" />
|
||||
<Property name="Top" tooltip="bool Faces.Top%1Whether the top face is included" />
|
||||
<Property name="Bottom" tooltip="bool Faces.Bottom%1Whether the bottom face is included" />
|
||||
<Property name="Left" tooltip="bool Faces.Left%1Whether the left face is included" />
|
||||
<Property name="Right" tooltip="bool Faces.Right%1Whether the right face is included" />
|
||||
<Property name="Back" tooltip="bool Faces.Back%1Whether the back face is included" />
|
||||
<Property name="Front" tooltip="bool Faces.Front%1Whether the front face is included" />
|
||||
</ItemStruct>
|
||||
<ItemStruct name="Axes">
|
||||
<StaticFunction name="new" tooltip="Axes Axes.new(Axis\NormalId, Axis/NormalId, ...)%1Creates a new Axes using list of axes and/or faces. NormalIds (faces) are converted to the corresponding axes." />
|
||||
<Property name="X" tooltip="bool Axes.X%1Whether the X axis is enabled" />
|
||||
<Property name="Y" tooltip="bool Axes.Y%1Whether the Y axis is enabled" />
|
||||
<Property name="Z" tooltip="bool Axes.Z%1Whether the Z axis is enabled" />
|
||||
<Property name="Top" tooltip="bool Axes.Top%1Whether the top face is included" />
|
||||
<Property name="Bottom" tooltip="bool Axes.Bottom%1Whether the bottom face is included" />
|
||||
<Property name="Left" tooltip="bool Axes.Left%1Whether the left face is included" />
|
||||
<Property name="Right" tooltip="bool Axes.Right%1Whether the right face is included" />
|
||||
<Property name="Back" tooltip="bool Axes.Back%1Whether the back face is included" />
|
||||
<Property name="Front" tooltip="bool Axes.Front%1Whether the front face is included" />
|
||||
</ItemStruct>
|
||||
<ItemStruct name="ColorSequenceKeypoint">
|
||||
<StaticFunction name="new" tooltip="ColorSequenceKeypoint ColorSequenceKeypoint.new(float time, Color3 color)%1Creates a keypoint with a specified time and color." />
|
||||
<Property name="Time" tooltip="float ColorSequenceKeypoint.Time%1The relative time at which the keypoint is located." />
|
||||
<Property name="Value" tooltip="Color3 ColorSequenceKeypoint.Value%1The Color3 value at the keypoint." />
|
||||
</ItemStruct>
|
||||
<ItemStruct name="NumberSequenceKeypoint">
|
||||
<StaticFunction name="new" tooltip="NumberSequenceKeypoint NumberSequenceKeypoint.new(float time, float value)%1Creates a keypoint with a specified time and value." />
|
||||
<StaticFunction name="new" tooltip="NumberSequenceKeypoint NumberSequenceKeypoint.new(float time, float value, float envelop)%1Creates a keypoint with a specified time, value, and envelope." />
|
||||
<Property name="Envelope" tooltip="float NumberSequenceKeypoint.Envelope%1Indicates the amount of variance allowed from the Value. A computed value." />
|
||||
<Property name="Time" tooltip="float NumberSequenceKeypoint.Time%1The relative time at which the keypoint is positioned." />
|
||||
</ItemStruct>
|
||||
<ItemStruct name="Rect">
|
||||
<StaticFunction name="new" tooltip="Rect Rect.new(min, max)%1Constructs a new Rect with Vector2 min as top left corner and Vector2 max as bottom right corner." />
|
||||
<Property name="Min" tooltip="Number Rect.Min%1The top-left corner" />
|
||||
<Property name="Max" tooltip="Number Rect.Max%1The bottom-right corner" />
|
||||
<Property name="Width" tooltip="Number Rect.Width%1The x width of rect" />
|
||||
<Property name="Height" tooltip="Number Rect.Height%1The y height of rect"/>
|
||||
</ItemStruct>
|
||||
<ItemStruct name="PhysicalProperties">
|
||||
<StaticFunction name="new" tooltip="PhysicalProperties PhysicalProperties.new(material)%1 Constructs a new PhysicalProperties container from the Enum.Material object." />
|
||||
<StaticFunction name="new" tooltip="PhysicalProperties PhysicalProperties.new(density, friction, elasticity, frictionWeight, elasticityWeight)%1Constructs a new PhysicalProperties container." />
|
||||
<Property name="Density" tooltip="Number PhysicalProperties.Density%1Density" />
|
||||
<Property name="Friction" tooltip="Number PhysicalProperties.Friction%1Friction" />
|
||||
<Property name="Elasticity" tooltip="Number PhysicalProperties.Elasticity%1Elasticity" />
|
||||
<Property name="FrictionWeight" tooltip="Number PhysicalProperties.FrictionWeight%1FrictionWeight determines how dominant the object's friction is when interacting with other objects. Default = 1"/>
|
||||
<Property name="ElasticityWeight" tooltip="Number PhysicalProperties.ElasticityWeight%1ElasticityWeight determins how dominant the object's elasticity is when interacting with other objects. Default = 1"/>
|
||||
</ItemStruct>
|
||||
</StudioAutocomplete>
|
||||
BIN
client/studio/resources/icon.ico
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
client/studio/resources/images/DeveloperProduct.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/studio/resources/images/MaterialPalette.PNG
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
client/studio/resources/images/RecordToggle.png
Normal file
|
After Width: | Height: | Size: 976 B |
BIN
client/studio/resources/images/RobloxStudio.ico
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
client/studio/resources/images/RobloxStudio.png
Normal file
|
After Width: | Height: | Size: 83 KiB |
BIN
client/studio/resources/images/RobloxStudioSplash.png
Normal file
|
After Width: | Height: | Size: 210 KiB |
BIN
client/studio/resources/images/TASKSCHEDULERIMAGES.bmp
Normal file
|
After Width: | Height: | Size: 630 B |
BIN
client/studio/resources/images/TA_BasicObjects.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/images/TA_Diagnostics.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/images/TA_Explorer.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/images/TA_HTML_Toolbox.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/studio/resources/images/TA_ObjectBrowser.png
Normal file
|
After Width: | Height: | Size: 582 B |
BIN
client/studio/resources/images/TA_Output.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
client/studio/resources/images/TA_Properties.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/images/TA_Start.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/studio/resources/images/TA_Task-Scheduler.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/images/ToolbarButtonDropDownGlyph.png
Normal file
|
After Width: | Height: | Size: 206 B |
BIN
client/studio/resources/images/ToolboxIcon.png
Normal file
|
After Width: | Height: | Size: 771 B |
BIN
client/studio/resources/images/advT_gridOff_old.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/images/advT_gridToOneFifth_old.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/images/advT_gridToOne_old.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/studio/resources/images/advT_jointManual_old.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/images/advT_resizeGrid_old.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
client/studio/resources/images/advT_rotateGrid_old.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/images/c_lookAtObject_old.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/images/c_panLeft_old.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/images/c_panRight_old.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/images/c_tiltDownCamera_old.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/images/c_tiltUpCamera_old.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
client/studio/resources/images/c_zoomExtent_old.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/studio/resources/images/c_zoomIn_old.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
client/studio/resources/images/c_zoomOut_old.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
client/studio/resources/images/checkmark.png
Normal file
|
After Width: | Height: | Size: 971 B |
BIN
client/studio/resources/images/cursor_PrimaryPart.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/studio/resources/images/downArrow.png
Normal file
|
After Width: | Height: | Size: 176 B |
BIN
client/studio/resources/images/home_button.png
Normal file
|
After Width: | Height: | Size: 734 B |
BIN
client/studio/resources/images/icons/16x16/CollisionGroups.png
Normal file
|
After Width: | Height: | Size: 608 B |
BIN
client/studio/resources/images/icons/16x16/Drag
Normal file
|
After Width: | Height: | Size: 999 B |
BIN
client/studio/resources/images/icons/16x16/Drag.png
Normal file
|
After Width: | Height: | Size: 999 B |
BIN
client/studio/resources/images/icons/16x16/Duplicate.png
Normal file
|
After Width: | Height: | Size: 392 B |
BIN
client/studio/resources/images/icons/16x16/Group.png
Normal file
|
After Width: | Height: | Size: 476 B |
BIN
client/studio/resources/images/icons/16x16/PackageLink.png
Normal file
|
After Width: | Height: | Size: 447 B |
BIN
client/studio/resources/images/icons/16x16/Record-icon.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
BIN
client/studio/resources/images/icons/16x16/Ungroup.png
Normal file
|
After Width: | Height: | Size: 365 B |
|
After Width: | Height: | Size: 746 B |
|
After Width: | Height: | Size: 321 B |
|
After Width: | Height: | Size: 767 B |
|
After Width: | Height: | Size: 728 B |
|
After Width: | Height: | Size: 764 B |
|
After Width: | Height: | Size: 313 B |
|
After Width: | Height: | Size: 791 B |
|
After Width: | Height: | Size: 743 B |
|
After Width: | Height: | Size: 790 B |
BIN
client/studio/resources/images/icons/16x16/attach_tab.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
client/studio/resources/images/icons/16x16/axis_widget.png
Normal file
|
After Width: | Height: | Size: 674 B |
BIN
client/studio/resources/images/icons/16x16/badges.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
client/studio/resources/images/icons/16x16/bomb.png
Normal file
|
After Width: | Height: | Size: 871 B |
BIN
client/studio/resources/images/icons/16x16/breakpointDelete.png
Normal file
|
After Width: | Height: | Size: 729 B |
BIN
client/studio/resources/images/icons/16x16/breakpoints.png
Normal file
|
After Width: | Height: | Size: 682 B |
BIN
client/studio/resources/images/icons/16x16/bug.png
Normal file
|
After Width: | Height: | Size: 774 B |
BIN
client/studio/resources/images/icons/16x16/bug_error.png
Normal file
|
After Width: | Height: | Size: 841 B |
BIN
client/studio/resources/images/icons/16x16/bug_go_to.png
Normal file
|
After Width: | Height: | Size: 831 B |
BIN
client/studio/resources/images/icons/16x16/callStack.png
Normal file
|
After Width: | Height: | Size: 524 B |
BIN
client/studio/resources/images/icons/16x16/collapse_all.png
Normal file
|
After Width: | Height: | Size: 493 B |
BIN
client/studio/resources/images/icons/16x16/collapse_gray_16.png
Normal file
|
After Width: | Height: | Size: 269 B |
BIN
client/studio/resources/images/icons/16x16/color-picker.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
client/studio/resources/images/icons/16x16/computer_16.png
Normal file
|
After Width: | Height: | Size: 605 B |
BIN
client/studio/resources/images/icons/16x16/connector-surface.png
Normal file
|
After Width: | Height: | Size: 1015 B |
|
After Width: | Height: | Size: 1.0 KiB |
BIN
client/studio/resources/images/icons/16x16/copy_clipboard_16.png
Normal file
|
After Width: | Height: | Size: 478 B |
|
After Width: | Height: | Size: 432 B |