-- https://github.com/LorekeeperZinnia/Dex
--[[ New Dex Final Version Developed by Moon Modified for Infinite Yield
Dex is a debugging suite designed to help the user debug games and find any potential vulnerabilities.
]]
local nodes = {} local selection local cloneref = cloneref or function(...) return ... end
local EmbeddedModules = { Explorer = function() --[[ Explorer App Module
The main explorer interface
]]
-- Common Locals local Main,Lib,Apps,Settings -- Main Containers local Explorer, Properties, ScriptViewer, Notebook -- Major Apps local API,RMD,env,service,plr,create,createSimple -- Main Locals
local function initDeps(data) Main = data.Main Lib = data.Lib Apps = data.Apps Settings = data.Settings
API = data.API
RMD = data.RMD
env = data.env
service = data.service
plr = data.plr
create = data.create
createSimple = data.createSimple
end
local function initAfterMain() Explorer = Apps.Explorer Properties = Apps.Properties ScriptViewer = Apps.ScriptViewer Notebook = Apps.Notebook end
local function main() local Explorer = {} local tree,listEntries,explorerOrders,searchResults,specResults = {},{},{},{},{} local expanded local entryTemplate,treeFrame,toolBar,descendantAddedCon,descendantRemovingCon,itemChangedCon local ffa = game["Run Service"].Parent.FindFirstAncestorWhichIsA local getDescendants = game["Run Service"].Parent.GetDescendants local getTextSize = service.TextService.GetTextSize local updateDebounce,refreshDebounce = false,false local nilNode = {Obj = Instance.new("Folder")} local idCounter = 0 local scrollV,scrollH,clipboard local renameBox,renamingNode,searchFunc local sortingEnabled,autoUpdateSearch local table,math = table,math local nilMap,nilCons = {},{} local connectSignal = game["Run Service"].Parent.DescendantAdded.Connect local addObject,removeObject,moveObject = nil,nil,nil
addObject = function(root)
if nodes[root] then return end
local isNil = false
local rootParObj = ffa(root,"Instance")
local par = nodes[rootParObj]
-- Nil Handling
if not par then
if nilMap[root] then
nilCons[root] = nilCons[root] or {
connectSignal(root.ChildAdded,addObject),
connectSignal(root.AncestryChanged,moveObject),
}
par = nilNode
isNil = true
else
return
end
elseif nilMap[rootParObj] or par == nilNode then
nilMap[root] = true
nilCons[root] = nilCons[root] or {
connectSignal(root.ChildAdded,addObject),
connectSignal(root.AncestryChanged,moveObject),
}
isNil = true
end
local newNode = {Obj = root, Parent = par}
nodes[root] = newNode
-- Automatic sorting if expanded
if sortingEnabled and expanded[par] and par.Sorted then
local left,right = 1,#par
local floor = math.floor
local sorter = Explorer.NodeSorter
local pos = (right == 0 and 1)
if not pos then
while true do
if left >= right then
if sorter(newNode,par[left]) then
pos = left
else
pos = left+1
end
break
end
local mid = floor((left+right)/2)
if sorter(newNode,par[mid]) then
right = mid-1
else
left = mid+1
end
end
end
table.insert(par,pos,newNode)
else
par[#par+1] = newNode
par.Sorted = nil
end
local insts = getDescendants(root)
for i = 1,#insts do
local obj = insts[i]
if nodes[obj] then continue end -- Deferred
local par = nodes[ffa(obj,"Instance")]
if not par then continue end
local newNode = {Obj = obj, Parent = par}
nodes[obj] = newNode
par[#par+1] = newNode
-- Nil Handling
if isNil then
nilMap[obj] = true
nilCons[obj] = nilCons[obj] or {
connectSignal(obj.ChildAdded,addObject),
connectSignal(obj.AncestryChanged,moveObject),
}
end
end
if searchFunc and autoUpdateSearch then
searchFunc({newNode})
end
if not updateDebounce and Explorer.IsNodeVisible(par) then
if expanded[par] then
Explorer.PerformUpdate()
elseif not refreshDebounce then
Explorer.PerformRefresh()
end
end
end
removeObject = function(root)
local node = nodes[root]
if not node then return end
-- Nil Handling
if nilMap[node.Obj] then
moveObject(node.Obj)
return
end
local par = node.Parent
if par then
par.HasDel = true
end
local function recur(root)
for i = 1,#root do
local node = root[i]
if not node.Del then
nodes[node.Obj] = nil
if #node > 0 then recur(node) end
end
end
end
recur(node)
node.Del = true
nodes[root] = nil
if par and not updateDebounce and Explorer.IsNodeVisible(par) then
if expanded[par] then
Explorer.PerformUpdate()
elseif not refreshDebounce then
Explorer.PerformRefresh()
end
end
end
moveObject = function(obj)
local node = nodes[obj]
if not node then return end
local oldPar = node.Parent
local newPar = nodes[ffa(obj,"Instance")]
if oldPar == newPar then return end
-- Nil Handling
if not newPar then
if nilMap[obj] then
newPar = nilNode
else
return
end
elseif nilMap[newPar.Obj] or newPar == nilNode then
nilMap[obj] = true
nilCons[obj] = nilCons[obj] or {
connectSignal(obj.ChildAdded,addObject),
connectSignal(obj.AncestryChanged,moveObject),
}
end
if oldPar then
local parPos = table.find(oldPar,node)
if parPos then table.remove(oldPar,parPos) end
end
node.Id = nil
node.Parent = newPar
if sortingEnabled and expanded[newPar] and newPar.Sorted then
local left,right = 1,#newPar
local floor = math.floor
local sorter = Explorer.NodeSorter
local pos = (right == 0 and 1)
if not pos then
while true do
if left >= right then
if sorter(node,newPar[left]) then
pos = left
else
pos = left+1
end
break
end
local mid = floor((left+right)/2)
if sorter(node,newPar[mid]) then
right = mid-1
else
left = mid+1
end
end
end
table.insert(newPar,pos,node)
else
newPar[#newPar+1] = node
newPar.Sorted = nil
end
if searchFunc and searchResults[node] then
local currentNode = node.Parent
while currentNode and (not searchResults[currentNode] or expanded[currentNode] == 0) do
expanded[currentNode] = true
searchResults[currentNode] = true
currentNode = currentNode.Parent
end
end
if not updateDebounce and (Explorer.IsNodeVisible(newPar) or Explorer.IsNodeVisible(oldPar)) then
if expanded[newPar] or expanded[oldPar] then
Explorer.PerformUpdate()
elseif not refreshDebounce then
Explorer.PerformRefresh()
end
end
end
Explorer.ViewWidth = 0
Explorer.Index = 0
Explorer.EntryIndent = 20
Explorer.FreeWidth = 32
Explorer.GuiElems = {}
Explorer.InitRenameBox = function()
renameBox = create({{1,"TextBox",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderColor3=Color3.new(0.062745101749897,0.51764708757401,1),BorderMode=2,ClearTextOnFocus=false,Font=3,Name="RenameBox",PlaceholderColor3=Color3.new(0.69803923368454,0.69803923368454,0.69803923368454),Position=UDim2.new(0,26,0,2),Size=UDim2.new(0,200,0,16),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,Visible=false,ZIndex=2}}})
renameBox.Parent = Explorer.Window.GuiElems.Content.List
renameBox.FocusLost:Connect(function()
if not renamingNode then return end
pcall(function() renamingNode.Obj.Name = renameBox.Text end)
renamingNode = nil
Explorer.Refresh()
end)
renameBox.Focused:Connect(function()
renameBox.SelectionStart = 1
renameBox.CursorPosition = #renameBox.Text + 1
end)
end
Explorer.SetRenamingNode = function(node)
renamingNode = node
renameBox.Text = tostring(node.Obj)
renameBox:CaptureFocus()
Explorer.Refresh()
end
Explorer.SetSortingEnabled = function(val)
sortingEnabled = val
Settings.Explorer.Sorting = val
end
Explorer.UpdateView = function()
local maxNodes = math.ceil(treeFrame.AbsoluteSize.Y / 20)
local maxX = treeFrame.AbsoluteSize.X
local totalWidth = Explorer.ViewWidth + Explorer.FreeWidth
scrollV.VisibleSpace = maxNodes
scrollV.TotalSpace = #tree + 1
scrollH.VisibleSpace = maxX
scrollH.TotalSpace = totalWidth
scrollV.Gui.Visible = #tree + 1 > maxNodes
scrollH.Gui.Visible = totalWidth > maxX
local oldSize = treeFrame.Size
treeFrame.Size = UDim2.new(1,(scrollV.Gui.Visible and -16 or 0),1,(scrollH.Gui.Visible and -39 or -23))
if oldSize ~= treeFrame.Size then
Explorer.UpdateView()
else
scrollV:Update()
scrollH:Update()
renameBox.Size = UDim2.new(0,maxX-100,0,16)
if scrollV.Gui.Visible and scrollH.Gui.Visible then
scrollV.Gui.Size = UDim2.new(0,16,1,-39)
scrollH.Gui.Size = UDim2.new(1,-16,0,16)
Explorer.Window.GuiElems.Content.ScrollCorner.Visible = true
else
scrollV.Gui.Size = UDim2.new(0,16,1,-23)
scrollH.Gui.Size = UDim2.new(1,0,0,16)
Explorer.Window.GuiElems.Content.ScrollCorner.Visible = false
end
Explorer.Index = scrollV.Index
end
end
Explorer.NodeSorter = function(a,b)
if a.Del or b.Del then return false end -- Ghost node
local aClass = a.Class
local bClass = b.Class
if not aClass then aClass = a.Obj.ClassName a.Class = aClass end
if not bClass then bClass = b.Obj.ClassName b.Class = bClass end
local aOrder = explorerOrders[aClass]
local bOrder = explorerOrders[bClass]
if not aOrder then aOrder = RMD.Classes[aClass] and tonumber(RMD.Classes[aClass].ExplorerOrder) or 9999 explorerOrders[aClass] = aOrder end
if not bOrder then bOrder = RMD.Classes[bClass] and tonumber(RMD.Classes[bClass].ExplorerOrder) or 9999 explorerOrders[bClass] = bOrder end
if aOrder ~= bOrder then
return aOrder < bOrder
else
local aName,bName = tostring(a.Obj),tostring(b.Obj)
if aName ~= bName then
return aName < bName
elseif aClass ~= bClass then
return aClass < bClass
else
local aId = a.Id if not aId then aId = idCounter idCounter = (idCounter+0.001)%999999999 a.Id = aId end
local bId = b.Id if not bId then bId = idCounter idCounter = (idCounter+0.001)%999999999 b.Id = bId end
return aId < bId
end
end
end
Explorer.Update = function()
table.clear(tree)
local maxNameWidth,maxDepth,count = 0,1,1
local nameCache = {}
local font = Enum.Font.SourceSans
local size = Vector2.new(math.huge,20)
local useNameWidth = Settings.Explorer.UseNameWidth
local tSort = table.sort
local sortFunc = Explorer.NodeSorter
local isSearching = (expanded == Explorer.SearchExpanded)
local textServ = service.TextService
local function recur(root,depth)
if depth > maxDepth then maxDepth = depth end
depth = depth + 1
if sortingEnabled and not root.Sorted then
tSort(root,sortFunc)
root.Sorted = true
end
for i = 1,#root do
local n = root[i]
if (isSearching and not searchResults[n]) or n.Del then continue end
if useNameWidth then
local nameWidth = n.NameWidth
if not nameWidth then
local objName = tostring(n.Obj)
nameWidth = nameCache[objName]
if not nameWidth then
nameWidth = getTextSize(textServ,objName,14,font,size).X
nameCache[objName] = nameWidth
end
n.NameWidth = nameWidth
end
if nameWidth > maxNameWidth then
maxNameWidth = nameWidth
end
end
tree[count] = n
count = count + 1
if expanded[n] and #n > 0 then
recur(n,depth)
end
end
end
recur(nodes[game["Run Service"].Parent],1)
-- Nil Instances
if env.getnilinstances then
if not (isSearching and not searchResults[nilNode]) then
tree[count] = nilNode
count = count + 1
if expanded[nilNode] then
recur(nilNode,2)
end
end
end
Explorer.MaxNameWidth = maxNameWidth
Explorer.MaxDepth = maxDepth
Explorer.ViewWidth = useNameWidth and Explorer.EntryIndent*maxDepth + maxNameWidth + 26 or Explorer.EntryIndent*maxDepth + 226
Explorer.UpdateView()
end
Explorer.StartDrag = function(offX,offY)
if Explorer.Dragging then return end
Explorer.Dragging = true
local dragTree = treeFrame:Clone()
dragTree:ClearAllChildren()
for i,v in pairs(listEntries) do
local node = tree[i + Explorer.Index]
if node and selection.Map[node] then
local clone = v:Clone()
clone.Active = false
clone.Indent.Expand.Visible = false
clone.Parent = dragTree
end
end
local newGui = Instance.new("ScreenGui")
newGui.DisplayOrder = Main.DisplayOrders.Menu
dragTree.Parent = newGui
Lib.ShowGui(newGui)
local dragOutline = create({
{1,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="DragSelect",Size=UDim2.new(1,0,1,0),}},
{2,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderSizePixel=0,Name="Line",Parent={1},Size=UDim2.new(1,0,0,1),ZIndex=2,}},
{3,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderSizePixel=0,Name="Line",Parent={1},Position=UDim2.new(0,0,1,-1),Size=UDim2.new(1,0,0,1),ZIndex=2,}},
{4,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderSizePixel=0,Name="Line",Parent={1},Size=UDim2.new(0,1,1,0),ZIndex=2,}},
{5,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderSizePixel=0,Name="Line",Parent={1},Position=UDim2.new(1,-1,0,0),Size=UDim2.new(0,1,1,0),ZIndex=2,}},
})
dragOutline.Parent = treeFrame
local mouse = Main.Mouse or service.Players.LocalPlayer:GetMouse()
local function move()
local posX = mouse.X - offX
local posY = mouse.Y - offY
dragTree.Position = UDim2.new(0,posX,0,posY)
for i = 1,#listEntries do
local entry = listEntries[i]
if Lib.CheckMouseInGui(entry) then
dragOutline.Position = UDim2.new(0,entry.Indent.Position.X.Offset-scrollH.Index,0,entry.Position.Y.Offset)
dragOutline.Size = UDim2.new(0,entry.Size.X.Offset-entry.Indent.Position.X.Offset,0,20)
dragOutline.Visible = true
return
end
end
dragOutline.Visible = false
end
move()
local input = service.UserInputService
local mouseEvent,releaseEvent
mouseEvent = input.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
move()
end
end)
releaseEvent = input.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
releaseEvent:Disconnect()
mouseEvent:Disconnect()
newGui:Destroy()
dragOutline:Destroy()
Explorer.Dragging = false
for i = 1,#listEntries do
if Lib.CheckMouseInGui(listEntries[i]) then
local node = tree[i + Explorer.Index]
if node then
if selection.Map[node] then return end
local newPar = node.Obj
local sList = selection.List
for i = 1,#sList do
local n = sList[i]
pcall(function() n.Obj.Parent = newPar end)
end
Explorer.ViewNode(sList[1])
end
break
end
end
end
end)
end
Explorer.NewListEntry = function(index)
local newEntry = entryTemplate:Clone()
newEntry.Position = UDim2.new(0,0,0,20*(index-1))
local isRenaming = false
newEntry.InputBegan:Connect(function(input)
local node = tree[index + Explorer.Index]
if not node or selection.Map[node] or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end
newEntry.Indent.BackgroundColor3 = Settings.Theme.Button
newEntry.Indent.BorderSizePixel = 0
newEntry.Indent.BackgroundTransparency = 0
end)
newEntry.InputEnded:Connect(function(input)
local node = tree[index + Explorer.Index]
if not node or selection.Map[node] or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end
newEntry.Indent.BackgroundTransparency = 1
end)
newEntry.MouseButton1Down:Connect(function()
end)
newEntry.MouseButton1Up:Connect(function()
end)
newEntry.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
local releaseEvent,mouseEvent
local mouse = Main.Mouse or plr:GetMouse()
local startX = mouse.X
local startY = mouse.Y
local listOffsetX = startX - treeFrame.AbsolutePosition.X
local listOffsetY = startY - treeFrame.AbsolutePosition.Y
releaseEvent = cloneref(game["Run Service"].Parent:GetService("UserInputService")).InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
releaseEvent:Disconnect()
mouseEvent:Disconnect()
end
end)
mouseEvent = cloneref(game["Run Service"].Parent:GetService("UserInputService")).InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
local deltaX = mouse.X - startX
local deltaY = mouse.Y - startY
local dist = math.sqrt(deltaX^2 + deltaY^2)
if dist > 5 then
releaseEvent:Disconnect()
mouseEvent:Disconnect()
isRenaming = false
Explorer.StartDrag(listOffsetX,listOffsetY)
end
end
end)
end
end)
newEntry.MouseButton2Down:Connect(function()
end)
newEntry.Indent.Expand.InputBegan:Connect(function(input)
local node = tree[index + Explorer.Index]
if not node or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end
Explorer.MiscIcons:DisplayByKey(newEntry.Indent.Expand.Icon, expanded[node] and "Collapse_Over" or "Expand_Over")
end)
newEntry.Indent.Expand.InputEnded:Connect(function(input)
local node = tree[index + Explorer.Index]
if not node or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end
Explorer.MiscIcons:DisplayByKey(newEntry.Indent.Expand.Icon, expanded[node] and "Collapse" or "Expand")
end)
newEntry.Indent.Expand.MouseButton1Down:Connect(function()
local node = tree[index + Explorer.Index]
if not node or #node == 0 then return end
expanded[node] = not expanded[node]
Explorer.Update()
Explorer.Refresh()
end)
newEntry.Parent = treeFrame
return newEntry
end
Explorer.Refresh = function()
local maxNodes = math.max(math.ceil((treeFrame.AbsoluteSize.Y) / 20),0)
local renameNodeVisible = false
local isa = game["Run Service"].Parent.IsA
for i = 1,maxNodes do
local entry = listEntries[i]
if not listEntries[i] then entry = Explorer.NewListEntry(i) listEntries[i] = entry Explorer.ClickSystem:Add(entry) end
local node = tree[i + Explorer.Index]
if node then
local obj = node.Obj
local depth = Explorer.EntryIndent*Explorer.NodeDepth(node)
entry.Visible = true
entry.Position = UDim2.new(0,-scrollH.Index,0,entry.Position.Y.Offset)
entry.Size = UDim2.new(0,Explorer.ViewWidth,0,20)
entry.Indent.EntryName.Text = tostring(node.Obj)
entry.Indent.Position = UDim2.new(0,depth,0,0)
entry.Indent.Size = UDim2.new(1,-depth,1,0)
entry.Indent.EntryName.TextTruncate = (Settings.Explorer.UseNameWidth and Enum.TextTruncate.None or Enum.TextTruncate.AtEnd)
if (isa(obj,"LocalScript") or isa(obj,"Script")) and obj.Disabled then
Explorer.MiscIcons:DisplayByKey(entry.Indent.Icon, isa(obj,"LocalScript") and "LocalScript_Disabled" or "Script_Disabled")
else
local rmdEntry = RMD.Classes[obj.ClassName]
Explorer.ClassIcons:Display(entry.Indent.Icon, rmdEntry and rmdEntry.ExplorerImageIndex or 0)
end
if selection.Map[node] then
entry.Indent.BackgroundColor3 = Settings.Theme.ListSelection
entry.Indent.BorderSizePixel = 0
entry.Indent.BackgroundTransparency = 0
else
if Lib.CheckMouseInGui(entry) then
entry.Indent.BackgroundColor3 = Settings.Theme.Button
else
entry.Indent.BackgroundTransparency = 1
end
end
if node == renamingNode then
renameNodeVisible = true
renameBox.Position = UDim2.new(0,depth+25-scrollH.Index,0,entry.Position.Y.Offset+2)
renameBox.Visible = true
end
if #node > 0 and expanded[node] ~= 0 then
if Lib.CheckMouseInGui(entry.Indent.Expand) then
Explorer.MiscIcons:DisplayByKey(entry.Indent.Expand.Icon, expanded[node] and "Collapse_Over" or "Expand_Over")
else
Explorer.MiscIcons:DisplayByKey(entry.Indent.Expand.Icon, expanded[node] and "Collapse" or "Expand")
end
entry.Indent.Expand.Visible = true
else
entry.Indent.Expand.Visible = false
end
else
entry.Visible = false
end
end
if not renameNodeVisible then
renameBox.Visible = false
end
for i = maxNodes+1, #listEntries do
Explorer.ClickSystem:Remove(listEntries[i])
listEntries[i]:Destroy()
listEntries[i] = nil
end
end
Explorer.PerformUpdate = function(instant)
updateDebounce = true
Lib.FastWait(not instant and 0.1)
if not updateDebounce then return end
updateDebounce = false
if not Explorer.Window:IsVisible() then return end
Explorer.Update()
Explorer.Refresh()
end
Explorer.ForceUpdate = function(norefresh)
updateDebounce = false
Explorer.Update()
if not norefresh then Explorer.Refresh() end
end
Explorer.PerformRefresh = function()
refreshDebounce = true
Lib.FastWait(0.1)
refreshDebounce = false
if updateDebounce or not Explorer.Window:IsVisible() then return end
Explorer.Refresh()
end
Explorer.IsNodeVisible = function(node)
if not node then return end
local curNode = node.Parent
while curNode do
if not expanded[curNode] then return false end
curNode = curNode.Parent
end
return true
end
Explorer.NodeDepth = function(node)
local depth = 0
if node == nilNode then
return 1
end
local curNode = node.Parent
while curNode do
if curNode == nilNode then depth = depth + 1 end
curNode = curNode.Parent
depth = depth + 1
end
return depth
end
Explorer.SetupConnections = function()
if descendantAddedCon then descendantAddedCon:Disconnect() end
if descendantRemovingCon then descendantRemovingCon:Disconnect() end
if itemChangedCon then itemChangedCon:Disconnect() end
if Main.Elevated then
descendantAddedCon = game["Run Service"].Parent.DescendantAdded:Connect(addObject)
descendantRemovingCon = game["Run Service"].Parent.DescendantRemoving:Connect(removeObject)
else
descendantAddedCon = game["Run Service"].Parent.DescendantAdded:Connect(function(obj) pcall(addObject,obj) end)
descendantRemovingCon = game["Run Service"].Parent.DescendantRemoving:Connect(function(obj) pcall(removeObject,obj) end)
end
if Settings.Explorer.UseNameWidth then
itemChangedCon = game["Run Service"].Parent.ItemChanged:Connect(function(obj,prop)
if prop == "Parent" and nodes[obj] then
moveObject(obj)
elseif prop == "Name" and nodes[obj] then
nodes[obj].NameWidth = nil
end
end)
else
itemChangedCon = game["Run Service"].Parent.ItemChanged:Connect(function(obj,prop)
if prop == "Parent" and nodes[obj] then
moveObject(obj)
end
end)
end
end
Explorer.ViewNode = function(node)
if not node then return end
Explorer.MakeNodeVisible(node)
Explorer.ForceUpdate(true)
local visibleSpace = scrollV.VisibleSpace
for i,v in next,tree do
if v == node then
local relative = i - 1
if Explorer.Index > relative then
scrollV.Index = relative
elseif Explorer.Index + visibleSpace - 1 <= relative then
scrollV.Index = relative - visibleSpace + 2
end
end
end
scrollV:Update() Explorer.Index = scrollV.Index
Explorer.Refresh()
end
Explorer.ViewObj = function(obj)
Explorer.ViewNode(nodes[obj])
end
Explorer.MakeNodeVisible = function(node,expandRoot)
if not node then return end
local hasExpanded = false
if expandRoot and not expanded[node] then
expanded[node] = true
hasExpanded = true
end
local currentNode = node.Parent
while currentNode do
hasExpanded = true
expanded[currentNode] = true
currentNode = currentNode.Parent
end
if hasExpanded and not updateDebounce then
coroutine.wrap(Explorer.PerformUpdate)(true)
end
end
Explorer.ShowRightClick = function()
local context = Explorer.RightClickContext
context:Clear()
local sList = selection.List
local sMap = selection.Map
local emptyClipboard = #clipboard == 0
local presentClasses = {}
local apiClasses = API.Classes
for i = 1, #sList do
local node = sList[i]
local class = node.Class
if not class then class = node.Obj.ClassName node.Class = class end
local curClass = apiClasses[class]
while curClass and not presentClasses[curClass.Name] do
presentClasses[curClass.Name] = true
curClass = curClass.Superclass
end
end
context:AddRegistered("CUT")
context:AddRegistered("COPY")
context:AddRegistered("PASTE", emptyClipboard)
context:AddRegistered("DUPLICATE")
context:AddRegistered("DELETE")
context:AddRegistered("RENAME", #sList ~= 1)
context:AddDivider()
context:AddRegistered("GROUP")
context:AddRegistered("UNGROUP")
context:AddRegistered("SELECT_CHILDREN")
context:AddRegistered("JUMP_TO_PARENT")
context:AddRegistered("EXPAND_ALL")
context:AddRegistered("COLLAPSE_ALL")
context:AddDivider()
if expanded == Explorer.SearchExpanded then context:AddRegistered("CLEAR_SEARCH_AND_JUMP_TO") end
if env.setclipboard then context:AddRegistered("COPY_PATH") end
context:AddRegistered("INSERT_OBJECT")
context:AddRegistered("SAVE_INST")
context:AddRegistered("CALL_FUNCTION")
context:AddRegistered("VIEW_CONNECTIONS")
context:AddRegistered("GET_REFERENCES")
context:AddRegistered("VIEW_API")
context:QueueDivider()
if presentClasses["BasePart"] or presentClasses["Model"] then
context:AddRegistered("TELEPORT_TO")
context:AddRegistered("VIEW_OBJECT")
end
if presentClasses["TouchTransmitter"] then context:AddRegistered("FIRE_TOUCHTRANSMITTER", firetouchinterest == nil) end
if presentClasses["ClickDetector"] then context:AddRegistered("FIRE_CLICKDETECTOR", fireclickdetector == nil) end
if presentClasses["ProximityPrompt"] then context:AddRegistered("FIRE_PROXIMITYPROMPT", fireproximityprompt == nil) end
if presentClasses["Player"] then context:AddRegistered("SELECT_CHARACTER") end
if presentClasses["Players"] then context:AddRegistered("SELECT_LOCAL_PLAYER") end
if presentClasses["LuaSourceContainer"] then context:AddRegistered("VIEW_SCRIPT") end
if sMap[nilNode] then
context:AddRegistered("REFRESH_NIL")
context:AddRegistered("HIDE_NIL")
end
Explorer.LastRightClickX, Explorer.LastRightClickY = Main.Mouse.X, Main.Mouse.Y
context:Show()
end
Explorer.InitRightClick = function()
local context = Lib.ContextMenu.new()
context:Register("CUT",{Name = "Cut", IconMap = Explorer.MiscIcons, Icon = "Cut", DisabledIcon = "Cut_Disabled", Shortcut = "Ctrl+Z", OnClick = function()
local destroy,clone = game["Run Service"].Parent.Destroy,game["Run Service"].Parent.Clone
local sList,newClipboard = selection.List,{}
local count = 1
for i = 1,#sList do
local inst = sList[i].Obj
local s,cloned = pcall(clone,inst)
if s and cloned then
newClipboard[count] = cloned
count = count + 1
end
pcall(destroy,inst)
end
clipboard = newClipboard
selection:Clear()
end})
context:Register("COPY",{Name = "Copy", IconMap = Explorer.MiscIcons, Icon = "Copy", DisabledIcon = "Copy_Disabled", Shortcut = "Ctrl+C", OnClick = function()
local clone = game["Run Service"].Parent.Clone
local sList,newClipboard = selection.List,{}
local count = 1
for i = 1,#sList do
local inst = sList[i].Obj
local s,cloned = pcall(clone,inst)
if s and cloned then
newClipboard[count] = cloned
count = count + 1
end
end
clipboard = newClipboard
end})
context:Register("PASTE",{Name = "Paste Into", IconMap = Explorer.MiscIcons, Icon = "Paste", DisabledIcon = "Paste_Disabled", Shortcut = "Ctrl+Shift+V", OnClick = function()
local sList = selection.List
local newSelection = {}
local count = 1
for i = 1,#sList do
local node = sList[i]
local inst = node.Obj
Explorer.MakeNodeVisible(node,true)
for c = 1,#clipboard do
local cloned = clipboard[c]:Clone()
if cloned then
cloned.Parent = inst
local clonedNode = nodes[cloned]
if clonedNode then newSelection[count] = clonedNode count = count + 1 end
end
end
end
selection:SetTable(newSelection)
if #newSelection > 0 then
Explorer.ViewNode(newSelection[1])
end
end})
context:Register("DUPLICATE",{Name = "Duplicate", IconMap = Explorer.MiscIcons, Icon = "Copy", DisabledIcon = "Copy_Disabled", Shortcut = "Ctrl+D", OnClick = function()
local clone = game["Run Service"].Parent.Clone
local sList = selection.List
local newSelection = {}
local count = 1
for i = 1,#sList do
local node = sList[i]
local inst = node.Obj
local instPar = node.Parent and node.Parent.Obj
Explorer.MakeNodeVisible(node)
local s,cloned = pcall(clone,inst)
if s and cloned then
cloned.Parent = instPar
local clonedNode = nodes[cloned]
if clonedNode then newSelection[count] = clonedNode count = count + 1 end
end
end
selection:SetTable(newSelection)
if #newSelection > 0 then
Explorer.ViewNode(newSelection[1])
end
end})
context:Register("DELETE",{Name = "Delete", IconMap = Explorer.MiscIcons, Icon = "Delete", DisabledIcon = "Delete_Disabled", Shortcut = "Del", OnClick = function()
local destroy = game["Run Service"].Parent.Destroy
local sList = selection.List
for i = 1,#sList do
pcall(destroy,sList[i].Obj)
end
selection:Clear()
end})
context:Register("RENAME",{Name = "Rename", IconMap = Explorer.MiscIcons, Icon = "Rename", DisabledIcon = "Rename_Disabled", Shortcut = "F2", OnClick = function()
local sList = selection.List
if sList[1] then
Explorer.SetRenamingNode(sList[1])
end
end})
context:Register("GROUP",{Name = "Group", IconMap = Explorer.MiscIcons, Icon = "Group", DisabledIcon = "Group_Disabled", Shortcut = "Ctrl+G", OnClick = function()
local sList = selection.List
if #sList == 0 then return end
local model = Instance.new("Model",sList[#sList].Obj.Parent)
for i = 1,#sList do
pcall(function() sList[i].Obj.Parent = model end)
end
if nodes[model] then
selection:Set(nodes[model])
Explorer.ViewNode(nodes[model])
end
end})
context:Register("UNGROUP",{Name = "Ungroup", IconMap = Explorer.MiscIcons, Icon = "Ungroup", DisabledIcon = "Ungroup_Disabled", Shortcut = "Ctrl+U", OnClick = function()
local newSelection = {}
local count = 1
local isa = game["Run Service"].Parent.IsA
local function ungroup(node)
local par = node.Parent.Obj
local ch = {}
local chCount = 1
for i = 1,#node do
local n = node[i]
newSelection[count] = n
ch[chCount] = n
count = count + 1
chCount = chCount + 1
end
for i = 1,#ch do
pcall(function() ch[i].Obj.Parent = par end)
end
node.Obj:Destroy()
end
for i,v in next,selection.List do
if isa(v.Obj,"Model") then
ungroup(v)
end
end
selection:SetTable(newSelection)
if #newSelection > 0 then
Explorer.ViewNode(newSelection[1])
end
end})
context:Register("SELECT_CHILDREN",{Name = "Select Children", IconMap = Explorer.MiscIcons, Icon = "SelectChildren", DisabledIcon = "SelectChildren_Disabled", OnClick = function()
local newSelection = {}
local count = 1
local sList = selection.List
for i = 1,#sList do
local node = sList[i]
for ind = 1,#node do
local cNode = node[ind]
if ind == 1 then Explorer.MakeNodeVisible(cNode) end
newSelection[count] = cNode
count = count + 1
end
end
selection:SetTable(newSelection)
if #newSelection > 0 then
Explorer.ViewNode(newSelection[1])
else
Explorer.Refresh()
end
end})
context:Register("JUMP_TO_PARENT",{Name = "Jump to Parent", IconMap = Explorer.MiscIcons, Icon = "JumpToParent", OnClick = function()
local newSelection = {}
local count = 1
local sList = selection.List
for i = 1,#sList do
local node = sList[i]
if node.Parent then
newSelection[count] = node.Parent
count = count + 1
end
end
selection:SetTable(newSelection)
if #newSelection > 0 then
Explorer.ViewNode(newSelection[1])
else
Explorer.Refresh()
end
end})
context:Register("TELEPORT_TO",{Name = "Teleport To", IconMap = Explorer.MiscIcons, Icon = "TeleportTo", OnClick = function()
local sList = selection.List
local isa = game["Run Service"].Parent.IsA
local hrp = plr.Character and plr.Character:FindFirstChild("HumanoidRootPart")
if not hrp then return end
for i = 1,#sList do
local node = sList[i]
if isa(node.Obj,"BasePart") then
hrp.CFrame = node.Obj.CFrame + Settings.Explorer.TeleportToOffset
break
elseif isa(node.Obj,"Model") then
if node.Obj.PrimaryPart then
hrp.CFrame = node.Obj.PrimaryPart.CFrame + Settings.Explorer.TeleportToOffset
break
else
local part = node.Obj:FindFirstChildWhichIsA("BasePart",true)
if part and nodes[part] then
hrp.CFrame = nodes[part].Obj.CFrame + Settings.Explorer.TeleportToOffset
end
end
end
end
end})
context:Register("EXPAND_ALL",{Name = "Expand All", OnClick = function()
local sList = selection.List
local function expand(node)
expanded[node] = true
for i = 1,#node do
if #node[i] > 0 then
expand(node[i])
end
end
end
for i = 1,#sList do
expand(sList[i])
end
Explorer.ForceUpdate()
end})
context:Register("COLLAPSE_ALL",{Name = "Collapse All", OnClick = function()
local sList = selection.List
local function expand(node)
expanded[node] = nil
for i = 1,#node do
if #node[i] > 0 then
expand(node[i])
end
end
end
for i = 1,#sList do
expand(sList[i])
end
Explorer.ForceUpdate()
end})
context:Register("CLEAR_SEARCH_AND_JUMP_TO",{Name = "Clear Search and Jump to", OnClick = function()
local newSelection = {}
local count = 1
local sList = selection.List
for i = 1,#sList do
newSelection[count] = sList[i]
count = count + 1
end
selection:SetTable(newSelection)
Explorer.ClearSearch()
if #newSelection > 0 then
Explorer.ViewNode(newSelection[1])
end
end})
local clth = function(str)
if str:sub(1, 28) == "game:GetService(\"Workspace\")" then str = str:gsub("game:GetService%(\"Workspace\"%)", "workspace", 1) end
if str:sub(1, 27 + #plr.Name) == "game:GetService(\"Players\")." .. plr.Name then str = str:gsub("game:GetService%(\"Players\"%)." .. plr.Name, "game:GetService(\"Players\").LocalPlayer", 1) end
return str
end
context:Register("COPY_PATH",{Name = "Copy Path", OnClick = function()
local sList = selection.List
if #sList == 1 then
env.setclipboard(clth(Explorer.GetInstancePath(sList[1].Obj)))
elseif #sList > 1 then
local resList = {"{"}
local count = 2
for i = 1,#sList do
local path = "\t"..clth(Explorer.GetInstancePath(sList[i].Obj))..","
if #path > 0 then
resList[count] = path
count = count+1
end
end
resList[count] = "}"
env.setclipboard(table.concat(resList,"\n"))
end
end})
context:Register("INSERT_OBJECT",{Name = "Insert Object", IconMap = Explorer.MiscIcons, Icon = "InsertObject", OnClick = function()
local mouse = Main.Mouse
local x,y = Explorer.LastRightClickX or mouse.X, Explorer.LastRightClickY or mouse.Y
Explorer.InsertObjectContext:Show(x,y)
end})
context:Register("CALL_FUNCTION",{Name = "Call Function", IconMap = Explorer.ClassIcons, Icon = 66, OnClick = function()
end})
context:Register("GET_REFERENCES",{Name = "Get Lua References", IconMap = Explorer.ClassIcons, Icon = 34, OnClick = function()
end})
context:Register("SAVE_INST",{Name = "Save to File", IconMap = Explorer.MiscIcons, Icon = "Save", OnClick = function()
end})
context:Register("VIEW_CONNECTIONS",{Name = "View Connections", OnClick = function()
end})
context:Register("VIEW_API",{Name = "View API Page", IconMap = Explorer.MiscIcons, Icon = "Reference", OnClick = function()
end})
context:Register("VIEW_OBJECT",{Name = "View Object (Right click to reset)", IconMap = Explorer.ClassIcons, Icon = 5, OnClick = function()
local sList = selection.List
local isa = game["Run Service"].Parent.IsA
for i = 1,#sList do
local node = sList[i]
if isa(node.Obj,"BasePart") or isa(node.Obj,"Model") then
workspace.CurrentCamera.CameraSubject = node.Obj
break
end
end
end, OnRightClick = function()
workspace.CurrentCamera.CameraSubject = plr.Character
end})
context:Register("FIRE_TOUCHTRANSMITTER",{Name = "Fire TouchTransmitter", IconMap = Explorer.ClassIcons, Icon = 37, OnClick = function()
local hrp = plr.Character and plr.Character:FindFirstChild("HumanoidRootPart")
if not hrp then return end
for _, v in ipairs(selection.List) do if v.Obj and v.Obj:IsA("TouchTransmitter") then firetouchinterest(hrp, v.Obj.Parent, 0) end end
end})
context:Register("FIRE_CLICKDETECTOR",{Name = "Fire ClickDetector", IconMap = Explorer.ClassIcons, Icon = 41, OnClick = function()
local hrp = plr.Character and plr.Character:FindFirstChild("HumanoidRootPart")
if not hrp then return end
for _, v in ipairs(selection.List) do if v.Obj and v.Obj:IsA("ClickDetector") then fireclickdetector(v.Obj) end end
end})
context:Register("FIRE_PROXIMITYPROMPT",{Name = "Fire ProximityPrompt", IconMap = Explorer.ClassIcons, Icon = 124, OnClick = function()
local hrp = plr.Character and plr.Character:FindFirstChild("HumanoidRootPart")
if not hrp then return end
for _, v in ipairs(selection.List) do if v.Obj and v.Obj:IsA("ProximityPrompt") then fireproximityprompt(v.Obj) end end
end})
context:Register("VIEW_SCRIPT",{Name = "View Script", IconMap = Explorer.MiscIcons, Icon = "ViewScript", OnClick = function()
local scr = selection.List[1] and selection.List[1].Obj
if scr then ScriptViewer.ViewScript(scr) end
end})
context:Register("SELECT_CHARACTER",{Name = "Select Character", IconMap = Explorer.ClassIcons, Icon = 9, OnClick = function()
local newSelection = {}
local count = 1
local sList = selection.List
local isa = game["Run Service"].Parent.IsA
for i = 1,#sList do
local node = sList[i]
if isa(node.Obj,"Player") and nodes[node.Obj.Character] then
newSelection[count] = nodes[node.Obj.Character]
count = count + 1
end
end
selection:SetTable(newSelection)
if #newSelection > 0 then
Explorer.ViewNode(newSelection[1])
else
Explorer.Refresh()
end
end})
context:Register("SELECT_LOCAL_PLAYER",{Name = "Select Local Player", IconMap = Explorer.ClassIcons, Icon = 9, OnClick = function()
pcall(function() if nodes[plr] then selection:Set(nodes[plr]) Explorer.ViewNode(nodes[plr]) end end)
end})
context:Register("REFRESH_NIL",{Name = "Refresh Nil Instances", OnClick = function()
Explorer.RefreshNilInstances()
end})
context:Register("HIDE_NIL",{Name = "Hide Nil Instances", OnClick = function()
Explorer.HideNilInstances()
end})
Explorer.RightClickContext = context
end
Explorer.HideNilInstances = function()
table.clear(nilMap)
local disconnectCon = Instance.new("Folder").ChildAdded:Connect(function() end).Disconnect
for i,v in next,nilCons do
disconnectCon(v[1])
disconnectCon(v[2])
end
table.clear(nilCons)
for i = 1,#nilNode do
coroutine.wrap(removeObject)(nilNode[i].Obj)
end
Explorer.Update()
Explorer.Refresh()
end
Explorer.RefreshNilInstances = function()
if not env.getnilinstances then return end
local nilInsts = env.getnilinstances()
local getDescs = game["Run Service"].Parent.GetDescendants
--local newNilMap = {}
--local newNilRoots = {}
--local nilRoots = Explorer.NilRoots
--local connect = game["Run Service"].Parent.DescendantAdded.Connect
--local disconnect
--if not nilRoots then nilRoots = {} Explorer.NilRoots = nilRoots end
for i = 1,#nilInsts do
local obj = nilInsts[i]
if obj ~= game["Run Service"].Parent then
nilMap[obj] = true
--newNilRoots[obj] = true
local descs = getDescs(obj)
for j = 1,#descs do
nilMap[descs[j]] = true
end
end
end
-- Remove unmapped nil nodes
--[[for i = 1,#nilNode do
local node = nilNode[i]
if not newNilMap[node.Obj] then
nilMap[node.Obj] = nil
coroutine.wrap(removeObject)(node)
end
end]]
--nilMap = newNilMap
for i = 1,#nilInsts do
local obj = nilInsts[i]
local node = nodes[obj]
if not node then coroutine.wrap(addObject)(obj) end
end
--[[
-- Remove old root connections
for obj in next,nilRoots do
if not newNilRoots[obj] then
if not disconnect then disconnect = obj[1].Disconnect end
disconnect(obj[1])
disconnect(obj[2])
end
end
for obj in next,newNilRoots do
if not nilRoots[obj] then
nilRoots[obj] = {
connect(obj.DescendantAdded,addObject),
connect(obj.DescendantRemoving,removeObject)
}
end
end]]
--nilMap = newNilMap
--Explorer.NilRoots = newNilRoots
Explorer.Update()
Explorer.Refresh()
end
Explorer.GetInstancePath = function(obj)
local ffc = game["Run Service"].Parent.FindFirstChild
local getCh = game["Run Service"].Parent.GetChildren
local path = ""
local curObj = obj
local ts = tostring
local match = string.match
local gsub = string.gsub
local tableFind = table.find
local useGetCh = Settings.Explorer.CopyPathUseGetChildren
local formatLuaString = Lib.FormatLuaString
while curObj do
if curObj == game["Run Service"].Parent then
path = "game"..path
break
end
local className = curObj.ClassName
local curName = ts(curObj)
local indexName
if match(curName,"^[%a_][%w_]*$") then
indexName = "."..curName
else
local cleanName = formatLuaString(curName)
indexName = '["'..cleanName..'"]'
end
local parObj = curObj.Parent
if parObj then
local fc = ffc(parObj,curName)
if useGetCh and fc and fc ~= curObj then
local parCh = getCh(parObj)
local fcInd = tableFind(parCh,curObj)
indexName = ":GetChildren()["..fcInd.."]"
elseif parObj == game["Run Service"].Parent and API.Classes[className] and API.Classes[className].Tags.Service then
indexName = ':GetService("'..className..'")'
end
elseif parObj == nil then
local getnil = "local getNil = function(name, class) for _, v in next, getnilinstances() do if v.ClassName == class and v.Name == name then return v end end end"
local gotnil = "\n\ngetNil(\"%s\", \"%s\")"
indexName = getnil .. gotnil:format(curObj.Name, className)
end
path = indexName..path
curObj = parObj
end
return path
end
Explorer.InitInsertObject = function()
local context = Lib.ContextMenu.new()
context.SearchEnabled = true
context.MaxHeight = 400
context:ApplyTheme({
ContentColor = Settings.Theme.Main2,
OutlineColor = Settings.Theme.Outline1,
DividerColor = Settings.Theme.Outline1,
TextColor = Settings.Theme.Text,
HighlightColor = Settings.Theme.ButtonHover
})
local classes = {}
for i,class in next,API.Classes do
local tags = class.Tags
if not tags.NotCreatable and not tags.Service then
local rmdEntry = RMD.Classes[class.Name]
classes[#classes+1] = {class,rmdEntry and rmdEntry.ClassCategory or "Uncategorized"}
end
end
table.sort(classes,function(a,b)
if a[2] ~= b[2] then
return a[2] < b[2]
else
return a[1].Name < b[1].Name
end
end)
local function onClick(className)
local sList = selection.List
local instNew = Instance.new
for i = 1,#sList do
local node = sList[i]
local obj = node.Obj
Explorer.MakeNodeVisible(node,true)
pcall(instNew,className,obj)
end
end
local lastCategory = ""
for i = 1,#classes do
local class = classes[i][1]
local rmdEntry = RMD.Classes[class.Name]
local iconInd = rmdEntry and tonumber(rmdEntry.ExplorerImageIndex) or 0
local category = classes[i][2]
if lastCategory ~= category then
context:AddDivider(category)
lastCategory = category
end
context:Add({Name = class.Name, IconMap = Explorer.ClassIcons, Icon = iconInd, OnClick = onClick})
end
Explorer.InsertObjectContext = context
end
--[[
Headers, Setups, Predicate, ObjectDefs
]]
Explorer.SearchFilters = { -- TODO: Use data table (so we can disable some if funcs don't exist)
Comparison = {
["isa"] = function(argString)
local lower = string.lower
local find = string.find
local classQuery = string.split(argString)[1]
if not classQuery then return end
classQuery = lower(classQuery)
local className
for class,_ in pairs(API.Classes) do
local cName = lower(class)
if cName == classQuery then
className = class
break
elseif find(cName,classQuery,1,true) then
className = class
end
end
if not className then return end
return {
Headers = {"local isa = game.IsA"},
Predicate = "isa(obj,'"..className.."')"
}
end,
["remotes"] = function(argString)
return {
Headers = {"local isa = game.IsA"},
Predicate = "isa(obj,'RemoteEvent') or isa(obj,'RemoteFunction')"
}
end,
["bindables"] = function(argString)
return {
Headers = {"local isa = game.IsA"},
Predicate = "isa(obj,'BindableEvent') or isa(obj,'BindableFunction')"
}
end,
["rad"] = function(argString)
local num = tonumber(argString)
if not num then return end
if not service.Players.LocalPlayer.Character or not service.Players.LocalPlayer.Character:FindFirstChild("HumanoidRootPart") or not service.Players.LocalPlayer.Character.HumanoidRootPart:IsA("BasePart") then return end
return {
Headers = {"local isa = game.IsA", "local hrp = service.Players.LocalPlayer.Character.HumanoidRootPart"},
Setups = {"local hrpPos = hrp.Position"},
ObjectDefs = {"local isBasePart = isa(obj,'BasePart')"},
Predicate = "(isBasePart and (obj.Position-hrpPos).Magnitude <= "..num..")"
}
end,
},
Specific = {
["players"] = function()
return function() return service.Players:GetPlayers() end
end,
["loadedmodules"] = function()
return env.getloadedmodules
end,
},
Default = function(argString,caseSensitive)
local cleanString = argString:gsub("\"","\\\""):gsub("\n","\\n")
if caseSensitive then
return {
Headers = {"local find = string.find"},
ObjectDefs = {"local objName = tostring(obj)"},
Predicate = "find(objName,\"" .. cleanString .. "\",1,true)"
}
else
return {
Headers = {"local lower = string.lower","local find = string.find","local tostring = tostring"},
ObjectDefs = {"local lowerName = lower(tostring(obj))"},
Predicate = "find(lowerName,\"" .. cleanString:lower() .. "\",1,true)"
}
end
end,
SpecificDefault = function(n)
return {
Headers = {},
ObjectDefs = {"local isSpec"..n.." = specResults["..n.."][node]"},
Predicate = "isSpec"..n
}
end,
}
Explorer.BuildSearchFunc = function(query)
local specFilterList,specMap = {},{}
local finalPredicate = ""
local rep = string.rep
local formatQuery = query:gsub("\\."," "):gsub('".-"',function(str) return rep(" ",#str) end)
local headers = {}
local objectDefs = {}
local setups = {}
local find = string.find
local sub = string.sub
local lower = string.lower
local match = string.match
local ops = {
["("] = "(",
[")"] = ")",
["||"] = " or ",
["&&"] = " and "
}
local filterCount = 0
local compFilters = Explorer.SearchFilters.Comparison
local specFilters = Explorer.SearchFilters.Specific
local init = 1
local lastOp = nil
local function processFilter(dat)
if dat.Headers then
local t = dat.Headers
for i = 1,#t do
headers[t[i]] = true
end
end
if dat.ObjectDefs then
local t = dat.ObjectDefs
for i = 1,#t do
objectDefs[t[i]] = true
end
end
if dat.Setups then
local t = dat.Setups
for i = 1,#t do
setups[t[i]] = true
end
end
finalPredicate = finalPredicate..dat.Predicate
end
local found = {}
local foundData = {}
local find = string.find
local sub = string.sub
local function findAll(str,pattern)
local count = #found+1
local init = 1
local sz = #pattern
local x,y,extra = find(str,pattern,init,true)
while x do
found[count] = x
foundData[x] = {sz,pattern}
count = count+1
init = y+1
x,y,extra = find(str,pattern,init,true)
end
end
local start = tick()
findAll(formatQuery,'&&')
findAll(formatQuery,"||")
findAll(formatQuery,"(")
findAll(formatQuery,")")
table.sort(found)
table.insert(found,#formatQuery+1)
local function inQuotes(str)
local len = #str
if sub(str,1,1) == '"' and sub(str,len,len) == '"' then
return sub(str,2,len-1)
end
end
for i = 1,#found do
local nextInd = found[i]
local nextData = foundData[nextInd] or {1}
local op = ops[nextData[2]]
local term = sub(query,init,nextInd-1)
term = match(term,"^%s*(.-)%s*$") or "" -- Trim
if #term > 0 then
if sub(term,1,1) == "!" then
term = sub(term,2)
finalPredicate = finalPredicate.."not "
end
local qTerm = inQuotes(term)
if qTerm then
processFilter(Explorer.SearchFilters.Default(qTerm,true))
else
local x,y = find(term,"%S+")
if x then
local first = sub(term,x,y)
local specifier = sub(first,1,1) == "/" and lower(sub(first,2))
local compFunc = specifier and compFilters[specifier]
local specFunc = specifier and specFilters[specifier]
if compFunc then
local argStr = sub(term,y+2)
local ret = compFunc(inQuotes(argStr) or argStr)
if ret then
processFilter(ret)
else
finalPredicate = finalPredicate.."false"
end
elseif specFunc then
local argStr = sub(term,y+2)
local ret = specFunc(inQuotes(argStr) or argStr)
if ret then
if not specMap[term] then
specFilterList[#specFilterList + 1] = ret
specMap[term] = #specFilterList
end
processFilter(Explorer.SearchFilters.SpecificDefault(specMap[term]))
else
finalPredicate = finalPredicate.."false"
end
else
processFilter(Explorer.SearchFilters.Default(term))
end
end
end
end
if op then
finalPredicate = finalPredicate..op
if op == "(" and (#term > 0 or lastOp == ")") then -- Handle bracket glitch
return
else
lastOp = op
end
end
init = nextInd+nextData[1]
end
local finalSetups = ""
local finalHeaders = ""
local finalObjectDefs = ""
for setup,_ in next,setups do finalSetups = finalSetups..setup.."\n" end
for header,_ in next,headers do finalHeaders = finalHeaders..header.."\n" end
for oDef,_ in next,objectDefs do finalObjectDefs = finalObjectDefs..oDef.."\n" end
local template = [==[
local searchResults = searchResults local nodes = nodes local expandTable = Explorer.SearchExpanded local specResults = specResults local service = service
%s local function search(root) %s
local expandedpar = false
for i = 1,#root do
local node = root[i]
local obj = node.Obj
%s
if %s then
expandTable[node] = 0
searchResults[node] = true
if not expandedpar then
local parnode = node.Parent
while parnode and (not searchResults[parnode] or expandTable[parnode] == 0) do
expandTable[parnode] = true
searchResults[parnode] = true
parnode = parnode.Parent
end
expandedpar = true
end
end
if #node > 0 then search(node) end
end
end return search]==]
local funcStr = template:format(finalHeaders,finalSetups,finalObjectDefs,finalPredicate)
local s,func = pcall(loadstring,funcStr)
if not s or not func then return nil,specFilterList end
local env = setmetatable({["searchResults"] = searchResults, ["nodes"] = nodes, ["Explorer"] = Explorer, ["specResults"] = specResults,
["service"] = service},{__index = getfenv()})
setfenv(func,env)
return func(),specFilterList
end
Explorer.DoSearch = function(query)
table.clear(Explorer.SearchExpanded)
table.clear(searchResults)
expanded = (#query == 0 and Explorer.Expanded or Explorer.SearchExpanded)
searchFunc = nil
if #query > 0 then
local expandTable = Explorer.SearchExpanded
local specFilters
local lower = string.lower
local find = string.find
local tostring = tostring
local lowerQuery = lower(query)
local function defaultSearch(root)
local expandedpar = false
for i = 1,#root do
local node = root[i]
local obj = node.Obj
if find(lower(tostring(obj)),lowerQuery,1,true) then
expandTable[node] = 0
searchResults[node] = true
if not expandedpar then
local parnode = node.Parent
while parnode and (not searchResults[parnode] or expandTable[parnode] == 0) do
expanded[parnode] = true
searchResults[parnode] = true
parnode = parnode.Parent
end
expandedpar = true
end
end
if #node > 0 then defaultSearch(node) end
end
end
if Main.Elevated then
local start = tick()
searchFunc,specFilters = Explorer.BuildSearchFunc(query)
--print("BUILD SEARCH",tick()-start)
else
searchFunc = defaultSearch
end
if specFilters then
table.clear(specResults)
for i = 1,#specFilters do -- Specific search filers that returns list of matches
local resMap = {}
specResults[i] = resMap
local objs = specFilters[i]()
for c = 1,#objs do
local node = nodes[objs[c]]
if node then
resMap[node] = true
end
end
end
end
if searchFunc then
local start = tick()
searchFunc(nodes[game["Run Service"].Parent])
searchFunc(nilNode)
--warn(tick()-start)
end
end
Explorer.ForceUpdate()
end
Explorer.ClearSearch = function()
Explorer.GuiElems.SearchBar.Text = ""
expanded = Explorer.Expanded
searchFunc = nil
end
Explorer.InitSearch = function()
local searchBox = Explorer.GuiElems.ToolBar.SearchFrame.SearchBox
Explorer.GuiElems.SearchBar = searchBox
Lib.ViewportTextBox.convert(searchBox)
searchBox.FocusLost:Connect(function()
Explorer.DoSearch(searchBox.Text)
end)
end
Explorer.InitEntryTemplate = function()
entryTemplate = create({
{1,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0,0,0),BackgroundTransparency=1,BorderColor3=Color3.new(0,0,0),Font=3,Name="Entry",Position=UDim2.new(0,1,0,1),Size=UDim2.new(0,250,0,20),Text="",TextSize=14,}},
{2,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BackgroundTransparency=1,BorderColor3=Color3.new(0.33725491166115,0.49019610881805,0.73725491762161),BorderSizePixel=0,Name="Indent",Parent={1},Position=UDim2.new(0,20,0,0),Size=UDim2.new(1,-20,1,0),}},
{3,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="EntryName",Parent={2},Position=UDim2.new(0,26,0,0),Size=UDim2.new(1,-26,1,0),Text="Workspace",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{4,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClipsDescendants=true,Font=3,Name="Expand",Parent={2},Position=UDim2.new(0,-20,0,0),Size=UDim2.new(0,20,0,20),Text="",TextSize=14,}},
{5,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642383285",ImageRectOffset=Vector2.new(144,16),ImageRectSize=Vector2.new(16,16),Name="Icon",Parent={4},Position=UDim2.new(0,2,0,2),ScaleType=4,Size=UDim2.new(0,16,0,16),}},
{6,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxasset://textures/ClassImages.png",ImageRectOffset=Vector2.new(304,0),ImageRectSize=Vector2.new(16,16),Name="Icon",Parent={2},Position=UDim2.new(0,4,0,2),ScaleType=4,Size=UDim2.new(0,16,0,16),}},
})
local sys = Lib.ClickSystem.new()
sys.AllowedButtons = {1,2}
sys.OnDown:Connect(function(item,combo,button)
local ind = table.find(listEntries,item)
if not ind then return end
local node = tree[ind + Explorer.Index]
if not node then return end
local entry = listEntries[ind]
if button == 1 then
if combo == 2 then
if node.Obj:IsA("LuaSourceContainer") then
ScriptViewer.ViewScript(node.Obj)
elseif #node > 0 and expanded[node] ~= 0 then
expanded[node] = not expanded[node]
Explorer.Update()
end
end
if Properties.SelectObject(node.Obj) then
sys.IsRenaming = false
return
end
sys.IsRenaming = selection.Map[node]
if Lib.IsShiftDown() then
if not selection.Piviot then return end
local fromIndex = table.find(tree,selection.Piviot)
local toIndex = table.find(tree,node)
if not fromIndex or not toIndex then return end
fromIndex,toIndex = math.min(fromIndex,toIndex),math.max(fromIndex,toIndex)
local sList = selection.List
for i = #sList,1,-1 do
local elem = sList[i]
if selection.ShiftSet[elem] then
selection.Map[elem] = nil
table.remove(sList,i)
end
end
selection.ShiftSet = {}
for i = fromIndex,toIndex do
local elem = tree[i]
if not selection.Map[elem] then
selection.ShiftSet[elem] = true
selection.Map[elem] = true
sList[#sList+1] = elem
end
end
selection.Changed:Fire()
elseif Lib.IsCtrlDown() then
selection.ShiftSet = {}
if selection.Map[node] then selection:Remove(node) else selection:Add(node) end
selection.Piviot = node
sys.IsRenaming = false
elseif not selection.Map[node] then
selection.ShiftSet = {}
selection:Set(node)
selection.Piviot = node
end
elseif button == 2 then
if Properties.SelectObject(node.Obj) then
return
end
if not Lib.IsCtrlDown() and not selection.Map[node] then
selection.ShiftSet = {}
selection:Set(node)
selection.Piviot = node
Explorer.Refresh()
end
end
Explorer.Refresh()
end)
sys.OnRelease:Connect(function(item,combo,button)
local ind = table.find(listEntries,item)
if not ind then return end
local node = tree[ind + Explorer.Index]
if not node then return end
if button == 1 then
if selection.Map[node] and not Lib.IsShiftDown() and not Lib.IsCtrlDown() then
selection.ShiftSet = {}
selection:Set(node)
selection.Piviot = node
Explorer.Refresh()
end
local id = sys.ClickId
Lib.FastWait(sys.ComboTime)
if combo == 1 and id == sys.ClickId and sys.IsRenaming and selection.Map[node] then
Explorer.SetRenamingNode(node)
end
elseif button == 2 then
Explorer.ShowRightClick()
end
end)
Explorer.ClickSystem = sys
end
Explorer.InitDelCleaner = function()
coroutine.wrap(function()
local fw = Lib.FastWait
while true do
local processed = false
local c = 0
for _,node in next,nodes do
if node.HasDel then
local delInd
for i = 1,#node do
if node[i].Del then
delInd = i
break
end
end
if delInd then
for i = delInd+1,#node do
local cn = node[i]
if not cn.Del then
node[delInd] = cn
delInd = delInd+1
end
end
for i = delInd,#node do
node[i] = nil
end
end
node.HasDel = false
processed = true
fw()
end
c = c + 1
if c > 10000 then
c = 0
fw()
end
end
if processed and not refreshDebounce then Explorer.PerformRefresh() end
fw(0.5)
end
end)()
end
Explorer.UpdateSelectionVisuals = function()
local holder = Explorer.SelectionVisualsHolder
local isa = game["Run Service"].Parent.IsA
local clone = game["Run Service"].Parent.Clone
if not holder then
holder = Instance.new("ScreenGui")
holder.Name = "ExplorerSelections"
holder.DisplayOrder = Main.DisplayOrders.Core
Lib.ShowGui(holder)
Explorer.SelectionVisualsHolder = holder
Explorer.SelectionVisualCons = {}
local guiTemplate = create({
{1,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Size=UDim2.new(0,100,0,100),}},
{2,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,-1,0,-1),Size=UDim2.new(1,2,0,1),}},
{3,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,-1,1,0),Size=UDim2.new(1,2,0,1),}},
{4,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,-1,0,0),Size=UDim2.new(0,1,1,0),}},
{5,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BorderSizePixel=0,Parent={1},Position=UDim2.new(1,0,0,0),Size=UDim2.new(0,1,1,0),}},
})
Explorer.SelectionVisualGui = guiTemplate
local boxTemplate = Instance.new("SelectionBox")
boxTemplate.LineThickness = 0.03
boxTemplate.Color3 = Color3.fromRGB(0, 170, 255)
Explorer.SelectionVisualBox = boxTemplate
end
holder:ClearAllChildren()
-- Updates theme
for i,v in pairs(Explorer.SelectionVisualGui:GetChildren()) do
v.BackgroundColor3 = Color3.fromRGB(0, 170, 255)
end
local attachCons = Explorer.SelectionVisualCons
for i = 1,#attachCons do
attachCons[i].Destroy()
end
table.clear(attachCons)
local partEnabled = Settings.Explorer.PartSelectionBox
local guiEnabled = Settings.Explorer.GuiSelectionBox
if not partEnabled and not guiEnabled then return end
local svg = Explorer.SelectionVisualGui
local svb = Explorer.SelectionVisualBox
local attachTo = Lib.AttachTo
local sList = selection.List
local count = 1
local boxCount = 0
local workspaceNode = nodes[workspace]
for i = 1,#sList do
if boxCount > 1000 then break end
local node = sList[i]
local obj = node.Obj
if node ~= workspaceNode then
if isa(obj,"GuiObject") and guiEnabled then
local newVisual = clone(svg)
attachCons[count] = attachTo(newVisual,{Target = obj, Resize = true})
count = count + 1
newVisual.Parent = holder
boxCount = boxCount + 1
elseif isa(obj,"PVInstance") and partEnabled then
local newBox = clone(svb)
newBox.Adornee = obj
newBox.Parent = holder
boxCount = boxCount + 1
end
end
end
end
Explorer.Init = function()
Explorer.ClassIcons = Lib.IconMap.newLinear("rbxasset://textures/ClassImages.png",16,16)
Explorer.MiscIcons = Main.MiscIcons
clipboard = {}
selection = Lib.Set.new()
selection.ShiftSet = {}
selection.Changed:Connect(Properties.ShowExplorerProps)
Explorer.Selection = selection
Explorer.InitRightClick()
Explorer.InitInsertObject()
Explorer.SetSortingEnabled(Settings.Explorer.Sorting)
Explorer.Expanded = setmetatable({},{__mode = "k"})
Explorer.SearchExpanded = setmetatable({},{__mode = "k"})
expanded = Explorer.Expanded
nilNode.Obj.Name = "Nil Instances"
nilNode.Locked = true
local explorerItems = create({
{1,"Folder",{Name="ExplorerItems",}},
{2,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="ToolBar",Parent={1},Size=UDim2.new(1,0,0,22),}},
{3,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.1176470592618,0.1176470592618,0.1176470592618),BorderSizePixel=0,Name="SearchFrame",Parent={2},Position=UDim2.new(0,3,0,1),Size=UDim2.new(1,-6,0,18),}},
{4,"TextBox",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClearTextOnFocus=false,Font=3,Name="SearchBox",Parent={3},PlaceholderColor3=Color3.new(0.39215689897537,0.39215689897537,0.39215689897537),PlaceholderText="Search workspace",Position=UDim2.new(0,4,0,0),Size=UDim2.new(1,-24,0,18),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,}},
{5,"UICorner",{CornerRadius=UDim.new(0,2),Parent={3},}},
{6,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Reset",Parent={3},Position=UDim2.new(1,-17,0,1),Size=UDim2.new(0,16,0,16),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
{7,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5034718129",ImageColor3=Color3.new(0.39215686917305,0.39215686917305,0.39215686917305),Parent={6},Size=UDim2.new(0,16,0,16),}},
{8,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Refresh",Parent={2},Position=UDim2.new(1,-20,0,1),Size=UDim2.new(0,18,0,18),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,Visible=false,}},
{9,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642310344",Parent={8},Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,12,0,12),}},
{10,"Frame",{BackgroundColor3=Color3.new(0.15686275064945,0.15686275064945,0.15686275064945),BorderSizePixel=0,Name="ScrollCorner",Parent={1},Position=UDim2.new(1,-16,1,-16),Size=UDim2.new(0,16,0,16),Visible=false,}},
{11,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClipsDescendants=true,Name="List",Parent={1},Position=UDim2.new(0,0,0,23),Size=UDim2.new(1,0,1,-23),}},
})
toolBar = explorerItems.ToolBar
treeFrame = explorerItems.List
Explorer.GuiElems.ToolBar = toolBar
Explorer.GuiElems.TreeFrame = treeFrame
scrollV = Lib.ScrollBar.new()
scrollV.WheelIncrement = 3
scrollV.Gui.Position = UDim2.new(1,-16,0,23)
scrollV:SetScrollFrame(treeFrame)
scrollV.Scrolled:Connect(function()
Explorer.Index = scrollV.Index
Explorer.Refresh()
end)
scrollH = Lib.ScrollBar.new(true)
scrollH.Increment = 5
scrollH.WheelIncrement = Explorer.EntryIndent
scrollH.Gui.Position = UDim2.new(0,0,1,-16)
scrollH.Scrolled:Connect(function()
Explorer.Refresh()
end)
local window = Lib.Window.new()
Explorer.Window = window
window:SetTitle("Explorer")
window.GuiElems.Line.Position = UDim2.new(0,0,0,22)
Explorer.InitEntryTemplate()
toolBar.Parent = window.GuiElems.Content
treeFrame.Parent = window.GuiElems.Content
explorerItems.ScrollCorner.Parent = window.GuiElems.Content
scrollV.Gui.Parent = window.GuiElems.Content
scrollH.Gui.Parent = window.GuiElems.Content
-- Init stuff that requires the window
Explorer.InitRenameBox()
Explorer.InitSearch()
Explorer.InitDelCleaner()
selection.Changed:Connect(Explorer.UpdateSelectionVisuals)
-- Window events
window.GuiElems.Main:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
if Explorer.Active then
Explorer.UpdateView()
Explorer.Refresh()
end
end)
window.OnActivate:Connect(function()
Explorer.Active = true
Explorer.UpdateView()
Explorer.Update()
Explorer.Refresh()
end)
window.OnRestore:Connect(function()
Explorer.Active = true
Explorer.UpdateView()
Explorer.Update()
Explorer.Refresh()
end)
window.OnDeactivate:Connect(function() Explorer.Active = false end)
window.OnMinimize:Connect(function() Explorer.Active = false end)
-- Settings
autoUpdateSearch = Settings.Explorer.AutoUpdateSearch
-- Fill in nodes
nodes[game["Run Service"].Parent] = {Obj = game["Run Service"].Parent}
expanded[nodes[game["Run Service"].Parent]] = true
-- Nil Instances
if env.getnilinstances then
nodes[nilNode.Obj] = nilNode
end
Explorer.SetupConnections()
local insts = getDescendants(game["Run Service"].Parent)
if Main.Elevated then
for i = 1,#insts do
local obj = insts[i]
local par = nodes[ffa(obj,"Instance")]
if not par then continue end
local newNode = {
Obj = obj,
Parent = par,
}
nodes[obj] = newNode
par[#par+1] = newNode
end
else
for i = 1,#insts do
local obj = insts[i]
local s,parObj = pcall(ffa,obj,"Instance")
local par = nodes[parObj]
if not par then continue end
local newNode = {
Obj = obj,
Parent = par,
}
nodes[obj] = newNode
par[#par+1] = newNode
end
end
end
return Explorer
end
return {InitDeps = initDeps, InitAfterMain = initAfterMain, Main = main} end, Properties = function() --[[ Properties App Module
The main properties interface
]]
-- Common Locals local Main,Lib,Apps,Settings -- Main Containers local Explorer, Properties, ScriptViewer, Notebook -- Major Apps local API,RMD,env,service,plr,create,createSimple -- Main Locals
local function initDeps(data) Main = data.Main Lib = data.Lib Apps = data.Apps Settings = data.Settings
API = data.API
RMD = data.RMD
env = data.env
service = data.service
plr = data.plr
create = data.create
createSimple = data.createSimple
end
local function initAfterMain() Explorer = Apps.Explorer Properties = Apps.Properties ScriptViewer = Apps.ScriptViewer Notebook = Apps.Notebook end
local function main() local Properties = {}
local window, toolBar, propsFrame
local scrollV, scrollH
local categoryOrder
local props,viewList,expanded,indexableProps,propEntries,autoUpdateObjs = {},{},{},{},{},{}
local inputBox,inputTextBox,inputProp
local checkboxes,propCons = {},{}
local table,string = table,string
local getPropChangedSignal = game["Run Service"].Parent.GetPropertyChangedSignal
local getAttributeChangedSignal = game["Run Service"].Parent.GetAttributeChangedSignal
local isa = game["Run Service"].Parent.IsA
local getAttribute = game["Run Service"].Parent.GetAttribute
local setAttribute = game["Run Service"].Parent.SetAttribute
Properties.GuiElems = {}
Properties.Index = 0
Properties.ViewWidth = 0
Properties.MinInputWidth = 100
Properties.EntryIndent = 16
Properties.EntryOffset = 4
Properties.NameWidthCache = {}
Properties.SubPropCache = {}
Properties.ClassLists = {}
Properties.SearchText = ""
Properties.AddAttributeProp = {Category = "Attributes", Class = "", Name = "", SpecialRow = "AddAttribute", Tags = {}}
Properties.SoundPreviewProp = {Category = "Data", ValueType = {Name = "SoundPlayer"}, Class = "Sound", Name = "Preview", Tags = {}}
Properties.IgnoreProps = {
["DataModel"] = {
["PrivateServerId"] = true,
["PrivateServerOwnerId"] = true,
["VIPServerId"] = true,
["VIPServerOwnerId"] = true
}
}
Properties.ExpandableTypes = {
["Vector2"] = true,
["Vector3"] = true,
["UDim"] = true,
["UDim2"] = true,
["CFrame"] = true,
["Rect"] = true,
["PhysicalProperties"] = true,
["Ray"] = true,
["NumberRange"] = true,
["Faces"] = true,
["Axes"] = true,
}
Properties.ExpandableProps = {
["Sound.SoundId"] = true
}
Properties.CollapsedCategories = {
["Surface Inputs"] = true,
["Surface"] = true
}
Properties.ConflictSubProps = {
["Vector2"] = {"X","Y"},
["Vector3"] = {"X","Y","Z"},
["UDim"] = {"Scale","Offset"},
["UDim2"] = {"X","X.Scale","X.Offset","Y","Y.Scale","Y.Offset"},
["CFrame"] = {"Position","Position.X","Position.Y","Position.Z",
"RightVector","RightVector.X","RightVector.Y","RightVector.Z",
"UpVector","UpVector.X","UpVector.Y","UpVector.Z",
"LookVector","LookVector.X","LookVector.Y","LookVector.Z"},
["Rect"] = {"Min.X","Min.Y","Max.X","Max.Y"},
["PhysicalProperties"] = {"Density","Elasticity","ElasticityWeight","Friction","FrictionWeight"},
["Ray"] = {"Origin","Origin.X","Origin.Y","Origin.Z","Direction","Direction.X","Direction.Y","Direction.Z"},
["NumberRange"] = {"Min","Max"},
["Faces"] = {"Back","Bottom","Front","Left","Right","Top"},
["Axes"] = {"X","Y","Z"}
}
Properties.ConflictIgnore = {
["BasePart"] = {
["ResizableFaces"] = true
}
}
Properties.RoundableTypes = {
["float"] = true,
["double"] = true,
["Color3"] = true,
["UDim"] = true,
["UDim2"] = true,
["Vector2"] = true,
["Vector3"] = true,
["NumberRange"] = true,
["Rect"] = true,
["NumberSequence"] = true,
["ColorSequence"] = true,
["Ray"] = true,
["CFrame"] = true
}
Properties.TypeNameConvert = {
["number"] = "double",
["boolean"] = "bool"
}
Properties.ToNumberTypes = {
["int"] = true,
["int64"] = true,
["float"] = true,
["double"] = true
}
Properties.DefaultPropValue = {
string = "",
bool = false,
double = 0,
UDim = UDim.new(0,0),
UDim2 = UDim2.new(0,0,0,0),
BrickColor = BrickColor.new("Medium stone grey"),
Color3 = Color3.new(1,1,1),
Vector2 = Vector2.new(0,0),
Vector3 = Vector3.new(0,0,0),
NumberSequence = NumberSequence.new(1),
ColorSequence = ColorSequence.new(Color3.new(1,1,1)),
NumberRange = NumberRange.new(0),
Rect = Rect.new(0,0,0,0)
}
Properties.AllowedAttributeTypes = {"string","boolean","number","UDim","UDim2","BrickColor","Color3","Vector2","Vector3","NumberSequence","ColorSequence","NumberRange","Rect"}
Properties.StringToValue = function(prop,str)
local typeData = prop.ValueType
local typeName = typeData.Name
if typeName == "string" or typeName == "Content" then
return str
elseif Properties.ToNumberTypes[typeName] then
return tonumber(str)
elseif typeName == "Vector2" then
local vals = str:split(",")
local x,y = tonumber(vals[1]),tonumber(vals[2])
if x and y and #vals >= 2 then return Vector2.new(x,y) end
elseif typeName == "Vector3" then
local vals = str:split(",")
local x,y,z = tonumber(vals[1]),tonumber(vals[2]),tonumber(vals[3])
if x and y and z and #vals >= 3 then return Vector3.new(x,y,z) end
elseif typeName == "UDim" then
local vals = str:split(",")
local scale,offset = tonumber(vals[1]),tonumber(vals[2])
if scale and offset and #vals >= 2 then return UDim.new(scale,offset) end
elseif typeName == "UDim2" then
local vals = str:gsub("[{}]",""):split(",")
local xScale,xOffset,yScale,yOffset = tonumber(vals[1]),tonumber(vals[2]),tonumber(vals[3]),tonumber(vals[4])
if xScale and xOffset and yScale and yOffset and #vals >= 4 then return UDim2.new(xScale,xOffset,yScale,yOffset) end
elseif typeName == "CFrame" then
local vals = str:split(",")
local s,result = pcall(CFrame.new,unpack(vals))
if s and #vals >= 12 then return result end
elseif typeName == "Rect" then
local vals = str:split(",")
local s,result = pcall(Rect.new,unpack(vals))
if s and #vals >= 4 then return result end
elseif typeName == "Ray" then
local vals = str:gsub("[{}]",""):split(",")
local s,origin = pcall(Vector3.new,unpack(vals,1,3))
local s2,direction = pcall(Vector3.new,unpack(vals,4,6))
if s and s2 and #vals >= 6 then return Ray.new(origin,direction) end
elseif typeName == "NumberRange" then
local vals = str:split(",")
local s,result = pcall(NumberRange.new,unpack(vals))
if s and #vals >= 1 then return result end
elseif typeName == "Color3" then
local vals = str:gsub("[{}]",""):split(",")
local s,result = pcall(Color3.fromRGB,unpack(vals))
if s and #vals >= 3 then return result end
end
return nil
end
Properties.ValueToString = function(prop,val)
local typeData = prop.ValueType
local typeName = typeData.Name
if typeName == "Color3" then
return Lib.ColorToBytes(val)
elseif typeName == "NumberRange" then
return val.Min..", "..val.Max
end
return tostring(val)
end
Properties.GetIndexableProps = function(obj,classData)
if not Main.Elevated then
if not pcall(function() return obj.ClassName end) then return nil end
end
local ignoreProps = Properties.IgnoreProps[classData.Name] or {}
local result = {}
local count = 1
local props = classData.Properties
for i = 1,#props do
local prop = props[i]
if not ignoreProps[prop.Name] then
local s = pcall(function() return obj[prop.Name] end)
if s then
result[count] = prop
count = count + 1
end
end
end
return result
end
Properties.FindFirstObjWhichIsA = function(class)
local classList = Properties.ClassLists[class] or {}
if classList and #classList > 0 then
return classList[1]
end
return nil
end
Properties.ComputeConflicts = function(p)
local maxConflictCheck = Settings.Properties.MaxConflictCheck
local sList = Explorer.Selection.List
local classLists = Properties.ClassLists
local stringSplit = string.split
local t_clear = table.clear
local conflictIgnore = Properties.ConflictIgnore
local conflictMap = {}
local propList = p and {p} or props
if p then
local gName = p.Class.."."..p.Name
autoUpdateObjs[gName] = nil
local subProps = Properties.ConflictSubProps[p.ValueType.Name] or {}
for i = 1,#subProps do
autoUpdateObjs[gName.."."..subProps[i]] = nil
end
else
table.clear(autoUpdateObjs)
end
if #sList > 0 then
for i = 1,#propList do
local prop = propList[i]
local propName,propClass = prop.Name,prop.Class
local typeData = prop.RootType or prop.ValueType
local typeName = typeData.Name
local attributeName = prop.AttributeName
local gName = propClass.."."..propName
local checked = 0
local subProps = Properties.ConflictSubProps[typeName] or {}
local subPropCount = #subProps
local toCheck = subPropCount + 1
local conflictsFound = 0
local indexNames = {}
local ignored = conflictIgnore[propClass] and conflictIgnore[propClass][propName]
local truthyCheck = (typeName == "PhysicalProperties")
local isAttribute = prop.IsAttribute
local isMultiType = prop.MultiType
t_clear(conflictMap)
if not isMultiType then
local firstVal,firstObj,firstSet
local classList = classLists[prop.Class] or {}
for c = 1,#classList do
local obj = classList[c]
if not firstSet then
if isAttribute then
firstVal = getAttribute(obj,attributeName)
if firstVal ~= nil then
firstObj = obj
firstSet = true
end
else
firstVal = obj[propName]
firstObj = obj
firstSet = true
end
if ignored then break end
else
local propVal,skip
if isAttribute then
propVal = getAttribute(obj,attributeName)
if propVal == nil then skip = true end
else
propVal = obj[propName]
end
if not skip then
if not conflictMap[1] then
if truthyCheck then
if (firstVal and true or false) ~= (propVal and true or false) then
conflictMap[1] = true
conflictsFound = conflictsFound + 1
end
elseif firstVal ~= propVal then
conflictMap[1] = true
conflictsFound = conflictsFound + 1
end
end
if subPropCount > 0 then
for sPropInd = 1,subPropCount do
local indexes = indexNames[sPropInd]
if not indexes then indexes = stringSplit(subProps[sPropInd],".") indexNames[sPropInd] = indexes end
local firstValSub = firstVal
local propValSub = propVal
for j = 1,#indexes do
if not firstValSub or not propValSub then break end -- PhysicalProperties
local indexName = indexes[j]
firstValSub = firstValSub[indexName]
propValSub = propValSub[indexName]
end
local mapInd = sPropInd + 1
if not conflictMap[mapInd] and firstValSub ~= propValSub then
conflictMap[mapInd] = true
conflictsFound = conflictsFound + 1
end
end
end
if conflictsFound == toCheck then break end
end
end
checked = checked + 1
if checked == maxConflictCheck then break end
end
if not conflictMap[1] then autoUpdateObjs[gName] = firstObj end
for sPropInd = 1,subPropCount do
if not conflictMap[sPropInd+1] then
autoUpdateObjs[gName.."."..subProps[sPropInd]] = firstObj
end
end
end
end
end
if p then
Properties.Refresh()
end
end
-- Fetches the properties to be displayed based on the explorer selection
Settings.Properties.ShowAttributes = true -- im making it true anyway since its useful by default and people complain
Properties.ShowExplorerProps = function()
local maxConflictCheck = Settings.Properties.MaxConflictCheck
local sList = Explorer.Selection.List
local foundClasses = {}
local propCount = 1
local elevated = Main.Elevated
local showDeprecated,showHidden = Settings.Properties.ShowDeprecated,Settings.Properties.ShowHidden
local Classes = API.Classes
local classLists = {}
local lower = string.lower
local RMDCustomOrders = RMD.PropertyOrders
local getAttributes = game["Run Service"].Parent.GetAttributes
local maxAttrs = Settings.Properties.MaxAttributes
local showingAttrs = Settings.Properties.ShowAttributes
local foundAttrs = {}
local attrCount = 0
local typeof = typeof
local typeNameConvert = Properties.TypeNameConvert
table.clear(props)
for i = 1,#sList do
local node = sList[i]
local obj = node.Obj
local class = node.Class
if not class then class = obj.ClassName node.Class = class end
local apiClass = Classes[class]
while apiClass do
local APIClassName = apiClass.Name
if not foundClasses[APIClassName] then
local apiProps = indexableProps[APIClassName]
if not apiProps then apiProps = Properties.GetIndexableProps(obj,apiClass) indexableProps[APIClassName] = apiProps end
for i = 1,#apiProps do
local prop = apiProps[i]
local tags = prop.Tags
if (not tags.Deprecated or showDeprecated) and (not tags.Hidden or showHidden) then
props[propCount] = prop
propCount = propCount + 1
end
end
foundClasses[APIClassName] = true
end
local classList = classLists[APIClassName]
if not classList then classList = {} classLists[APIClassName] = classList end
classList[#classList+1] = obj
apiClass = apiClass.Superclass
end
if showingAttrs and attrCount < maxAttrs then
local attrs = getAttributes(obj)
for name,val in pairs(attrs) do
local typ = typeof(val)
if not foundAttrs[name] then
local category = (typ == "Instance" and "Class") or (typ == "EnumItem" and "Enum") or "Other"
local valType = {Name = typeNameConvert[typ] or typ, Category = category}
local attrProp = {IsAttribute = true, Name = "ATTR_"..name, AttributeName = name, DisplayName = name, Class = "Instance", ValueType = valType, Category = "Attributes", Tags = {}}
props[propCount] = attrProp
propCount = propCount + 1
attrCount = attrCount + 1
foundAttrs[name] = {typ,attrProp}
if attrCount == maxAttrs then break end
elseif foundAttrs[name][1] ~= typ then
foundAttrs[name][2].MultiType = true
foundAttrs[name][2].Tags.ReadOnly = true
foundAttrs[name][2].ValueType = {Name = "string"}
end
end
end
end
table.sort(props,function(a,b)
if a.Category ~= b.Category then
return (categoryOrder[a.Category] or 9999) < (categoryOrder[b.Category] or 9999)
else
local aOrder = (RMDCustomOrders[a.Class] and RMDCustomOrders[a.Class][a.Name]) or 9999999
local bOrder = (RMDCustomOrders[b.Class] and RMDCustomOrders[b.Class][b.Name]) or 9999999
if aOrder ~= bOrder then
return aOrder < bOrder
else
return lower(a.Name) < lower(b.Name)
end
end
end)
-- Find conflicts and get auto-update instances
Properties.ClassLists = classLists
Properties.ComputeConflicts()
--warn("CONFLICT",tick()-start)
if #props > 0 then
props[#props+1] = Properties.AddAttributeProp
end
Properties.Update()
Properties.Refresh()
end
Properties.UpdateView = function()
local maxEntries = math.ceil(propsFrame.AbsoluteSize.Y / 23)
local maxX = propsFrame.AbsoluteSize.X
local totalWidth = Properties.ViewWidth + Properties.MinInputWidth
scrollV.VisibleSpace = maxEntries
scrollV.TotalSpace = #viewList + 1
scrollH.VisibleSpace = maxX
scrollH.TotalSpace = totalWidth
scrollV.Gui.Visible = #viewList + 1 > maxEntries
scrollH.Gui.Visible = Settings.Properties.ScaleType == 0 and totalWidth > maxX
local oldSize = propsFrame.Size
propsFrame.Size = UDim2.new(1,(scrollV.Gui.Visible and -16 or 0),1,(scrollH.Gui.Visible and -39 or -23))
if oldSize ~= propsFrame.Size then
Properties.UpdateView()
else
scrollV:Update()
scrollH:Update()
if scrollV.Gui.Visible and scrollH.Gui.Visible then
scrollV.Gui.Size = UDim2.new(0,16,1,-39)
scrollH.Gui.Size = UDim2.new(1,-16,0,16)
Properties.Window.GuiElems.Content.ScrollCorner.Visible = true
else
scrollV.Gui.Size = UDim2.new(0,16,1,-23)
scrollH.Gui.Size = UDim2.new(1,0,0,16)
Properties.Window.GuiElems.Content.ScrollCorner.Visible = false
end
Properties.Index = scrollV.Index
end
end
Properties.MakeSubProp = function(prop,subName,valueType,displayName)
local subProp = {}
for i,v in pairs(prop) do
subProp[i] = v
end
subProp.RootType = subProp.RootType or subProp.ValueType
subProp.ValueType = valueType
subProp.SubName = subProp.SubName and (subProp.SubName..subName) or subName
subProp.DisplayName = displayName
return subProp
end
Properties.GetExpandedProps = function(prop) -- TODO: Optimize using table
local result = {}
local typeData = prop.ValueType
local typeName = typeData.Name
local makeSubProp = Properties.MakeSubProp
if typeName == "Vector2" then
result[1] = makeSubProp(prop,".X",{Name = "float"})
result[2] = makeSubProp(prop,".Y",{Name = "float"})
elseif typeName == "Vector3" then
result[1] = makeSubProp(prop,".X",{Name = "float"})
result[2] = makeSubProp(prop,".Y",{Name = "float"})
result[3] = makeSubProp(prop,".Z",{Name = "float"})
elseif typeName == "CFrame" then
result[1] = makeSubProp(prop,".Position",{Name = "Vector3"})
result[2] = makeSubProp(prop,".RightVector",{Name = "Vector3"})
result[3] = makeSubProp(prop,".UpVector",{Name = "Vector3"})
result[4] = makeSubProp(prop,".LookVector",{Name = "Vector3"})
elseif typeName == "UDim" then
result[1] = makeSubProp(prop,".Scale",{Name = "float"})
result[2] = makeSubProp(prop,".Offset",{Name = "int"})
elseif typeName == "UDim2" then
result[1] = makeSubProp(prop,".X",{Name = "UDim"})
result[2] = makeSubProp(prop,".Y",{Name = "UDim"})
elseif typeName == "Rect" then
result[1] = makeSubProp(prop,".Min.X",{Name = "float"},"X0")
result[2] = makeSubProp(prop,".Min.Y",{Name = "float"},"Y0")
result[3] = makeSubProp(prop,".Max.X",{Name = "float"},"X1")
result[4] = makeSubProp(prop,".Max.Y",{Name = "float"},"Y1")
elseif typeName == "PhysicalProperties" then
result[1] = makeSubProp(prop,".Density",{Name = "float"})
result[2] = makeSubProp(prop,".Elasticity",{Name = "float"})
result[3] = makeSubProp(prop,".ElasticityWeight",{Name = "float"})
result[4] = makeSubProp(prop,".Friction",{Name = "float"})
result[5] = makeSubProp(prop,".FrictionWeight",{Name = "float"})
elseif typeName == "Ray" then
result[1] = makeSubProp(prop,".Origin",{Name = "Vector3"})
result[2] = makeSubProp(prop,".Direction",{Name = "Vector3"})
elseif typeName == "NumberRange" then
result[1] = makeSubProp(prop,".Min",{Name = "float"})
result[2] = makeSubProp(prop,".Max",{Name = "float"})
elseif typeName == "Faces" then
result[1] = makeSubProp(prop,".Back",{Name = "bool"})
result[2] = makeSubProp(prop,".Bottom",{Name = "bool"})
result[3] = makeSubProp(prop,".Front",{Name = "bool"})
result[4] = makeSubProp(prop,".Left",{Name = "bool"})
result[5] = makeSubProp(prop,".Right",{Name = "bool"})
result[6] = makeSubProp(prop,".Top",{Name = "bool"})
elseif typeName == "Axes" then
result[1] = makeSubProp(prop,".X",{Name = "bool"})
result[2] = makeSubProp(prop,".Y",{Name = "bool"})
result[3] = makeSubProp(prop,".Z",{Name = "bool"})
end
if prop.Name == "SoundId" and prop.Class == "Sound" then
result[1] = Properties.SoundPreviewProp
end
return result
end
Properties.Update = function()
table.clear(viewList)
local nameWidthCache = Properties.NameWidthCache
local lastCategory
local count = 1
local maxWidth,maxDepth = 0,1
local textServ = service.TextService
local getTextSize = textServ.GetTextSize
local font = Enum.Font.SourceSans
local size = Vector2.new(math.huge,20)
local stringSplit = string.split
local entryIndent = Properties.EntryIndent
local isFirstScaleType = Settings.Properties.ScaleType == 0
local find,lower = string.find,string.lower
local searchText = (#Properties.SearchText > 0 and lower(Properties.SearchText))
local function recur(props,depth)
for i = 1,#props do
local prop = props[i]
local propName = prop.Name
local subName = prop.SubName
local category = prop.Category
local visible
if searchText and depth == 1 then
if find(lower(propName),searchText,1,true) then
visible = true
end
else
visible = true
end
if visible and lastCategory ~= category then
viewList[count] = {CategoryName = category}
count = count + 1
lastCategory = category
end
if (expanded["CAT_"..category] and visible) or prop.SpecialRow then
if depth > 1 then prop.Depth = depth if depth > maxDepth then maxDepth = depth end end
if isFirstScaleType then
local nameArr = subName and stringSplit(subName,".")
local displayName = prop.DisplayName or (nameArr and nameArr[#nameArr]) or propName
local nameWidth = nameWidthCache[displayName]
if not nameWidth then nameWidth = getTextSize(textServ,displayName,14,font,size).X nameWidthCache[displayName] = nameWidth end
local totalWidth = nameWidth + entryIndent*depth
if totalWidth > maxWidth then
maxWidth = totalWidth
end
end
viewList[count] = prop
count = count + 1
local fullName = prop.Class.."."..prop.Name..(prop.SubName or "")
if expanded[fullName] then
local nextDepth = depth+1
local expandedProps = Properties.GetExpandedProps(prop)
if #expandedProps > 0 then
recur(expandedProps,nextDepth)
end
end
end
end
end
recur(props,1)
inputProp = nil
Properties.ViewWidth = maxWidth + 9 + Properties.EntryOffset
Properties.UpdateView()
end
Properties.NewPropEntry = function(index)
local newEntry = Properties.EntryTemplate:Clone()
local nameFrame = newEntry.NameFrame
local valueFrame = newEntry.ValueFrame
local newCheckbox = Lib.Checkbox.new(1)
newCheckbox.Gui.Position = UDim2.new(0,3,0,3)
newCheckbox.Gui.Parent = valueFrame
newCheckbox.OnInput:Connect(function()
local prop = viewList[index + Properties.Index]
if not prop then return end
if prop.ValueType.Name == "PhysicalProperties" then
Properties.SetProp(prop,newCheckbox.Toggled and true or nil)
else
Properties.SetProp(prop,newCheckbox.Toggled)
end
end)
checkboxes[index] = newCheckbox
local iconFrame = Main.MiscIcons:GetLabel()
iconFrame.Position = UDim2.new(0,2,0,3)
iconFrame.Parent = newEntry.ValueFrame.RightButton
newEntry.Position = UDim2.new(0,0,0,23*(index-1))
nameFrame.Expand.InputBegan:Connect(function(input)
local prop = viewList[index + Properties.Index]
if not prop or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end
local fullName = (prop.CategoryName and "CAT_"..prop.CategoryName) or prop.Class.."."..prop.Name..(prop.SubName or "")
Main.MiscIcons:DisplayByKey(newEntry.NameFrame.Expand.Icon, expanded[fullName] and "Collapse_Over" or "Expand_Over")
end)
nameFrame.Expand.InputEnded:Connect(function(input)
local prop = viewList[index + Properties.Index]
if not prop or input.UserInputType ~= Enum.UserInputType.MouseMovement then return end
local fullName = (prop.CategoryName and "CAT_"..prop.CategoryName) or prop.Class.."."..prop.Name..(prop.SubName or "")
Main.MiscIcons:DisplayByKey(newEntry.NameFrame.Expand.Icon, expanded[fullName] and "Collapse" or "Expand")
end)
nameFrame.Expand.MouseButton1Down:Connect(function()
local prop = viewList[index + Properties.Index]
if not prop then return end
local fullName = (prop.CategoryName and "CAT_"..prop.CategoryName) or prop.Class.."."..prop.Name..(prop.SubName or "")
if not prop.CategoryName and not Properties.ExpandableTypes[prop.ValueType and prop.ValueType.Name] and not Properties.ExpandableProps[fullName] then return end
expanded[fullName] = not expanded[fullName]
Properties.Update()
Properties.Refresh()
end)
nameFrame.PropName.InputBegan:Connect(function(input)
local prop = viewList[index + Properties.Index]
if not prop then return end
if input.UserInputType == Enum.UserInputType.MouseMovement and not nameFrame.PropName.TextFits then
local fullNameFrame = Properties.FullNameFrame
local nameArr = string.split(prop.Class.."."..prop.Name..(prop.SubName or ""),".")
local dispName = prop.DisplayName or nameArr[#nameArr]
local sizeX = service.TextService:GetTextSize(dispName,14,Enum.Font.SourceSans,Vector2.new(math.huge,20)).X
fullNameFrame.TextLabel.Text = dispName
--fullNameFrame.Position = UDim2.new(0,Properties.EntryIndent*(prop.Depth or 1) + Properties.EntryOffset,0,23*(index-1))
fullNameFrame.Size = UDim2.new(0,sizeX + 4,0,22)
fullNameFrame.Visible = true
Properties.FullNameFrameIndex = index
Properties.FullNameFrameAttach.SetData(fullNameFrame, {Target = nameFrame})
Properties.FullNameFrameAttach.Enable()
end
end)
nameFrame.PropName.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and Properties.FullNameFrameIndex == index then
Properties.FullNameFrame.Visible = false
Properties.FullNameFrameAttach.Disable()
end
end)
valueFrame.ValueBox.MouseButton1Down:Connect(function()
local prop = viewList[index + Properties.Index]
if not prop then return end
Properties.SetInputProp(prop,index)
end)
valueFrame.ColorButton.MouseButton1Down:Connect(function()
local prop = viewList[index + Properties.Index]
if not prop then return end
Properties.SetInputProp(prop,index,"color")
end)
valueFrame.RightButton.MouseButton1Click:Connect(function()
local prop = viewList[index + Properties.Index]
if not prop then return end
local fullName = prop.Class.."."..prop.Name..(prop.SubName or "")
local inputFullName = inputProp and (inputProp.Class.."."..inputProp.Name..(inputProp.SubName or ""))
if fullName == inputFullName and inputProp.ValueType.Category == "Class" then
inputProp = nil
Properties.SetProp(prop,nil)
else
Properties.SetInputProp(prop,index,"right")
end
end)
nameFrame.ToggleAttributes.MouseButton1Click:Connect(function()
Settings.Properties.ShowAttributes = not Settings.Properties.ShowAttributes
Properties.ShowExplorerProps()
end)
newEntry.RowButton.MouseButton1Click:Connect(function()
Properties.DisplayAddAttributeWindow()
end)
newEntry.EditAttributeButton.MouseButton1Down:Connect(function()
local prop = viewList[index + Properties.Index]
if not prop then return end
Properties.DisplayAttributeContext(prop)
end)
valueFrame.SoundPreview.ControlButton.MouseButton1Click:Connect(function()
if Properties.PreviewSound and Properties.PreviewSound.Playing then
Properties.SetSoundPreview(false)
else
local soundObj = Properties.FindFirstObjWhichIsA("Sound")
if soundObj then Properties.SetSoundPreview(soundObj) end
end
end)
valueFrame.SoundPreview.InputBegan:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
local releaseEvent,mouseEvent
releaseEvent = service.UserInputService.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
releaseEvent:Disconnect()
mouseEvent:Disconnect()
end)
local timeLine = newEntry.ValueFrame.SoundPreview.TimeLine
local soundObj = Properties.FindFirstObjWhichIsA("Sound")
if soundObj then Properties.SetSoundPreview(soundObj,true) end
local function update(input)
local sound = Properties.PreviewSound
if not sound or sound.TimeLength == 0 then return end
local mouseX = input.Position.X
local timeLineSize = timeLine.AbsoluteSize
local relaX = mouseX - timeLine.AbsolutePosition.X
if timeLineSize.X <= 1 then return end
if relaX < 0 then relaX = 0 elseif relaX >= timeLineSize.X then relaX = timeLineSize.X-1 end
local perc = (relaX/(timeLineSize.X-1))
sound.TimePosition = perc*sound.TimeLength
timeLine.Slider.Position = UDim2.new(perc,-4,0,-8)
end
update(input)
mouseEvent = service.UserInputService.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
update(input)
end
end)
end)
newEntry.Parent = propsFrame
return {
Gui = newEntry,
GuiElems = {
NameFrame = nameFrame,
ValueFrame = valueFrame,
PropName = nameFrame.PropName,
ValueBox = valueFrame.ValueBox,
Expand = nameFrame.Expand,
ColorButton = valueFrame.ColorButton,
ColorPreview = valueFrame.ColorButton.ColorPreview,
Gradient = valueFrame.ColorButton.ColorPreview.UIGradient,
EnumArrow = valueFrame.EnumArrow,
Checkbox = valueFrame.Checkbox,
RightButton = valueFrame.RightButton,
RightButtonIcon = iconFrame,
RowButton = newEntry.RowButton,
EditAttributeButton = newEntry.EditAttributeButton,
ToggleAttributes = nameFrame.ToggleAttributes,
SoundPreview = valueFrame.SoundPreview,
SoundPreviewSlider = valueFrame.SoundPreview.TimeLine.Slider
}
}
end
Properties.GetSoundPreviewEntry = function()
for i = 1,#viewList do
if viewList[i] == Properties.SoundPreviewProp then
return propEntries[i - Properties.Index]
end
end
end
Properties.SetSoundPreview = function(soundObj,noplay)
local sound = Properties.PreviewSound
if not sound then
sound = Instance.new("Sound")
sound.Name = "Preview"
sound.Paused:Connect(function()
local entry = Properties.GetSoundPreviewEntry()
if entry then Main.MiscIcons:DisplayByKey(entry.GuiElems.SoundPreview.ControlButton.Icon, "Play") end
end)
sound.Resumed:Connect(function() Properties.Refresh() end)
sound.Ended:Connect(function()
local entry = Properties.GetSoundPreviewEntry()
if entry then entry.GuiElems.SoundPreviewSlider.Position = UDim2.new(0,-4,0,-8) end
Properties.Refresh()
end)
sound.Parent = window.Gui
Properties.PreviewSound = sound
end
if not soundObj then
sound:Pause()
else
local newId = sound.SoundId ~= soundObj.SoundId
sound.SoundId = soundObj.SoundId
sound.PlaybackSpeed = soundObj.PlaybackSpeed
sound.Volume = soundObj.Volume
if newId then sound.TimePosition = 0 end
if not noplay then sound:Resume() end
coroutine.wrap(function()
local previewTime = tick()
Properties.SoundPreviewTime = previewTime
while previewTime == Properties.SoundPreviewTime and sound.Playing do
local entry = Properties.GetSoundPreviewEntry()
if entry then
local tl = sound.TimeLength
local perc = sound.TimePosition/(tl == 0 and 1 or tl)
entry.GuiElems.SoundPreviewSlider.Position = UDim2.new(perc,-4,0,-8)
end
Lib.FastWait()
end
end)()
Properties.Refresh()
end
end
Properties.DisplayAttributeContext = function(prop)
local context = Properties.AttributeContext
if not context then
context = Lib.ContextMenu.new()
context.Iconless = true
context.Width = 80
end
context:Clear()
context:Add({Name = "Edit", OnClick = function()
Properties.DisplayAddAttributeWindow(prop)
end})
context:Add({Name = "Delete", OnClick = function()
Properties.SetProp(prop,nil,true)
Properties.ShowExplorerProps()
end})
context:Show()
end
Properties.DisplayAddAttributeWindow = function(editAttr)
local win = Properties.AddAttributeWindow
if not win then
win = Lib.Window.new()
win.Alignable = false
win.Resizable = false
win:SetTitle("Add Attribute")
win:SetSize(200,130)
local saveButton = Lib.Button.new()
local nameLabel = Lib.Label.new()
nameLabel.Text = "Name"
nameLabel.Position = UDim2.new(0,30,0,10)
nameLabel.Size = UDim2.new(0,40,0,20)
win:Add(nameLabel)
local nameBox = Lib.ViewportTextBox.new()
nameBox.Position = UDim2.new(0,75,0,10)
nameBox.Size = UDim2.new(0,120,0,20)
win:Add(nameBox,"NameBox")
nameBox.TextBox:GetPropertyChangedSignal("Text"):Connect(function()
saveButton:SetDisabled(#nameBox:GetText() == 0)
end)
local typeLabel = Lib.Label.new()
typeLabel.Text = "Type"
typeLabel.Position = UDim2.new(0,30,0,40)
typeLabel.Size = UDim2.new(0,40,0,20)
win:Add(typeLabel)
local typeChooser = Lib.DropDown.new()
typeChooser.CanBeEmpty = false
typeChooser.Position = UDim2.new(0,75,0,40)
typeChooser.Size = UDim2.new(0,120,0,20)
typeChooser:SetOptions(Properties.AllowedAttributeTypes)
win:Add(typeChooser,"TypeChooser")
local errorLabel = Lib.Label.new()
errorLabel.Text = ""
errorLabel.Position = UDim2.new(0,5,1,-45)
errorLabel.Size = UDim2.new(1,-10,0,20)
errorLabel.TextColor3 = Settings.Theme.Important
win.ErrorLabel = errorLabel
win:Add(errorLabel,"Error")
local cancelButton = Lib.Button.new()
cancelButton.Text = "Cancel"
cancelButton.Position = UDim2.new(1,-97,1,-25)
cancelButton.Size = UDim2.new(0,92,0,20)
cancelButton.OnClick:Connect(function()
win:Close()
end)
win:Add(cancelButton)
saveButton.Text = "Save"
saveButton.Position = UDim2.new(0,5,1,-25)
saveButton.Size = UDim2.new(0,92,0,20)
saveButton.OnClick:Connect(function()
local name = nameBox:GetText()
if #name > 100 then
errorLabel.Text = "Error: Name over 100 chars"
return
elseif name:sub(1,3) == "RBX" then
errorLabel.Text = "Error: Name begins with 'RBX'"
return
end
local typ = typeChooser.Selected
local valType = {Name = Properties.TypeNameConvert[typ] or typ, Category = "DataType"}
local attrProp = {IsAttribute = true, Name = "ATTR_"..name, AttributeName = name, DisplayName = name, Class = "Instance", ValueType = valType, Category = "Attributes", Tags = {}}
Settings.Properties.ShowAttributes = true
Properties.SetProp(attrProp,Properties.DefaultPropValue[valType.Name],true,Properties.EditingAttribute)
Properties.ShowExplorerProps()
win:Close()
end)
win:Add(saveButton,"SaveButton")
Properties.AddAttributeWindow = win
end
Properties.EditingAttribute = editAttr
win:SetTitle(editAttr and "Edit Attribute "..editAttr.AttributeName or "Add Attribute")
win.Elements.Error.Text = ""
win.Elements.NameBox:SetText("")
win.Elements.SaveButton:SetDisabled(true)
win.Elements.TypeChooser:SetSelected(1)
win:Show()
end
Properties.IsTextEditable = function(prop)
local typeData = prop.ValueType
local typeName = typeData.Name
return typeName ~= "bool" and typeData.Category ~= "Enum" and typeData.Category ~= "Class" and typeName ~= "BrickColor"
end
Properties.DisplayEnumDropdown = function(entryIndex)
local context = Properties.EnumContext
if not context then
context = Lib.ContextMenu.new()
context.Iconless = true
context.MaxHeight = 200
context.ReverseYOffset = 22
Properties.EnumDropdown = context
end
if not inputProp or inputProp.ValueType.Category ~= "Enum" then return end
local prop = inputProp
local entry = propEntries[entryIndex]
local valueFrame = entry.GuiElems.ValueFrame
local enum = Enum[prop.ValueType.Name]
if not enum then return end
local sorted = {}
for name,enum in next,enum:GetEnumItems() do
sorted[#sorted+1] = enum
end
table.sort(sorted,function(a,b) return a.Name < b.Name end)
context:Clear()
local function onClick(name)
if prop ~= inputProp then return end
local enumItem = enum[name]
inputProp = nil
Properties.SetProp(prop,enumItem)
end
for i = 1,#sorted do
local enumItem = sorted[i]
context:Add({Name = enumItem.Name, OnClick = onClick})
end
context.Width = valueFrame.AbsoluteSize.X
context:Show(valueFrame.AbsolutePosition.X, valueFrame.AbsolutePosition.Y + 22)
end
Properties.DisplayBrickColorEditor = function(prop,entryIndex,col)
local editor = Properties.BrickColorEditor
if not editor then
editor = Lib.BrickColorPicker.new()
editor.Gui.DisplayOrder = Main.DisplayOrders.Menu
editor.ReverseYOffset = 22
editor.OnSelect:Connect(function(col)
if not editor.CurrentProp or editor.CurrentProp.ValueType.Name ~= "BrickColor" then return end
if editor.CurrentProp == inputProp then inputProp = nil end
Properties.SetProp(editor.CurrentProp,BrickColor.new(col))
end)
editor.OnMoreColors:Connect(function() -- TODO: Special Case BasePart.BrickColor to BasePart.Color
editor:Close()
local colProp
for i,v in pairs(API.Classes.BasePart.Properties) do
if v.Name == "Color" then
colProp = v
break
end
end
Properties.DisplayColorEditor(colProp,editor.SavedColor.Color)
end)
Properties.BrickColorEditor = editor
end
local entry = propEntries[entryIndex]
local valueFrame = entry.GuiElems.ValueFrame
editor.CurrentProp = prop
editor.SavedColor = col
if prop and prop.Class == "BasePart" and prop.Name == "BrickColor" then
editor:SetMoreColorsVisible(true)
else
editor:SetMoreColorsVisible(false)
end
editor:Show(valueFrame.AbsolutePosition.X, valueFrame.AbsolutePosition.Y + 22)
end
Properties.DisplayColorEditor = function(prop,col)
local editor = Properties.ColorEditor
if not editor then
editor = Lib.ColorPicker.new()
editor.OnSelect:Connect(function(col)
if not editor.CurrentProp then return end
local typeName = editor.CurrentProp.ValueType.Name
if typeName ~= "Color3" and typeName ~= "BrickColor" then return end
local colVal = (typeName == "Color3" and col or BrickColor.new(col))
if editor.CurrentProp == inputProp then inputProp = nil end
Properties.SetProp(editor.CurrentProp,colVal)
end)
Properties.ColorEditor = editor
end
editor.CurrentProp = prop
if col then
editor:SetColor(col)
else
local firstVal = Properties.GetFirstPropVal(prop)
if firstVal then editor:SetColor(firstVal) end
end
editor:Show()
end
Properties.DisplayNumberSequenceEditor = function(prop,seq)
local editor = Properties.NumberSequenceEditor
if not editor then
editor = Lib.NumberSequenceEditor.new()
editor.OnSelect:Connect(function(val)
if not editor.CurrentProp or editor.CurrentProp.ValueType.Name ~= "NumberSequence" then return end
if editor.CurrentProp == inputProp then inputProp = nil end
Properties.SetProp(editor.CurrentProp,val)
end)
Properties.NumberSequenceEditor = editor
end
editor.CurrentProp = prop
if seq then
editor:SetSequence(seq)
else
local firstVal = Properties.GetFirstPropVal(prop)
if firstVal then editor:SetSequence(firstVal) end
end
editor:Show()
end
Properties.DisplayColorSequenceEditor = function(prop,seq)
local editor = Properties.ColorSequenceEditor
if not editor then
editor = Lib.ColorSequenceEditor.new()
editor.OnSelect:Connect(function(val)
if not editor.CurrentProp or editor.CurrentProp.ValueType.Name ~= "ColorSequence" then return end
if editor.CurrentProp == inputProp then inputProp = nil end
Properties.SetProp(editor.CurrentProp,val)
end)
Properties.ColorSequenceEditor = editor
end
editor.CurrentProp = prop
if seq then
editor:SetSequence(seq)
else
local firstVal = Properties.GetFirstPropVal(prop)
if firstVal then editor:SetSequence(firstVal) end
end
editor:Show()
end
Properties.GetFirstPropVal = function(prop)
local first = Properties.FindFirstObjWhichIsA(prop.Class)
if first then
return Properties.GetPropVal(prop,first)
end
end
Properties.GetPropVal = function(prop,obj)
if prop.MultiType then return "<Multiple Types>" end
if not obj then return end
local propVal
if prop.IsAttribute then
propVal = getAttribute(obj,prop.AttributeName)
if propVal == nil then return nil end
local typ = typeof(propVal)
local currentType = Properties.TypeNameConvert[typ] or typ
if prop.RootType then
if prop.RootType.Name ~= currentType then
return nil
end
elseif prop.ValueType.Name ~= currentType then
return nil
end
else
propVal = obj[prop.Name]
end
if prop.SubName then
local indexes = string.split(prop.SubName,".")
for i = 1,#indexes do
local indexName = indexes[i]
if #indexName > 0 and propVal then
propVal = propVal[indexName]
end
end
end
return propVal
end
Properties.SelectObject = function(obj)
if inputProp and inputProp.ValueType.Category == "Class" then
local prop = inputProp
inputProp = nil
if isa(obj,prop.ValueType.Name) then
Properties.SetProp(prop,obj)
else
Properties.Refresh()
end
return true
end
return false
end
Properties.DisplayProp = function(prop,entryIndex)
local propName = prop.Name
local typeData = prop.ValueType
local typeName = typeData.Name
local tags = prop.Tags
local gName = prop.Class.."."..prop.Name..(prop.SubName or "")
local propObj = autoUpdateObjs[gName]
local entryData = propEntries[entryIndex]
local UDim2 = UDim2
local guiElems = entryData.GuiElems
local valueFrame = guiElems.ValueFrame
local valueBox = guiElems.ValueBox
local colorButton = guiElems.ColorButton
local colorPreview = guiElems.ColorPreview
local gradient = guiElems.Gradient
local enumArrow = guiElems.EnumArrow
local checkbox = guiElems.Checkbox
local rightButton = guiElems.RightButton
local soundPreview = guiElems.SoundPreview
local propVal = Properties.GetPropVal(prop,propObj)
local inputFullName = inputProp and (inputProp.Class.."."..inputProp.Name..(inputProp.SubName or ""))
local offset = 4
local endOffset = 6
-- Offsetting the ValueBox for ValueType specific buttons
if (typeName == "Color3" or typeName == "BrickColor" or typeName == "ColorSequence") then
colorButton.Visible = true
enumArrow.Visible = false
if propVal then
gradient.Color = (typeName == "Color3" and ColorSequence.new(propVal)) or (typeName == "BrickColor" and ColorSequence.new(propVal.Color)) or propVal
else
gradient.Color = ColorSequence.new(Color3.new(1,1,1))
end
colorPreview.BorderColor3 = (typeName == "ColorSequence" and Color3.new(1,1,1) or Color3.new(0,0,0))
offset = 22
endOffset = 24 + (typeName == "ColorSequence" and 20 or 0)
elseif typeData.Category == "Enum" then
colorButton.Visible = false
enumArrow.Visible = not prop.Tags.ReadOnly
endOffset = 22
elseif (gName == inputFullName and typeData.Category == "Class") or typeName == "NumberSequence" then
colorButton.Visible = false
enumArrow.Visible = false
endOffset = 26
else
colorButton.Visible = false
enumArrow.Visible = false
end
valueBox.Position = UDim2.new(0,offset,0,0)
valueBox.Size = UDim2.new(1,-endOffset,1,0)
-- Right button
if inputFullName == gName and typeData.Category == "Class" then
Main.MiscIcons:DisplayByKey(guiElems.RightButtonIcon, "Delete")
guiElems.RightButtonIcon.Visible = true
rightButton.Text = ""
rightButton.Visible = true
elseif typeName == "NumberSequence" or typeName == "ColorSequence" then
guiElems.RightButtonIcon.Visible = false
rightButton.Text = "..."
rightButton.Visible = true
else
rightButton.Visible = false
end
-- Displays the correct ValueBox for the ValueType, and sets it to the prop value
if typeName == "bool" or typeName == "PhysicalProperties" then
valueBox.Visible = false
checkbox.Visible = true
soundPreview.Visible = false
checkboxes[entryIndex].Disabled = tags.ReadOnly
if typeName == "PhysicalProperties" and autoUpdateObjs[gName] then
checkboxes[entryIndex]:SetState(propVal and true or false)
else
checkboxes[entryIndex]:SetState(propVal)
end
elseif typeName == "SoundPlayer" then
valueBox.Visible = false
checkbox.Visible = false
soundPreview.Visible = true
local playing = Properties.PreviewSound and Properties.PreviewSound.Playing
Main.MiscIcons:DisplayByKey(soundPreview.ControlButton.Icon, playing and "Pause" or "Play")
else
valueBox.Visible = true
checkbox.Visible = false
soundPreview.Visible = false
if propVal ~= nil then
if typeName == "Color3" then
valueBox.Text = "["..Lib.ColorToBytes(propVal).."]"
elseif typeData.Category == "Enum" then
valueBox.Text = propVal.Name
elseif Properties.RoundableTypes[typeName] and Settings.Properties.NumberRounding then
local rawStr = Properties.ValueToString(prop,propVal)
valueBox.Text = rawStr:gsub("-?%d+%.%d+",function(num)
return tostring(tonumber(("%."..Settings.Properties.NumberRounding.."f"):format(num)))
end)
else
valueBox.Text = Properties.ValueToString(prop,propVal)
end
else
valueBox.Text = ""
end
valueBox.TextColor3 = tags.ReadOnly and Settings.Theme.PlaceholderText or Settings.Theme.Text
end
end
Properties.Refresh = function()
local maxEntries = math.max(math.ceil((propsFrame.AbsoluteSize.Y) / 23),0)
local maxX = propsFrame.AbsoluteSize.X
local valueWidth = math.max(Properties.MinInputWidth,maxX-Properties.ViewWidth)
local inputPropVisible = false
local isa = game["Run Service"].Parent.IsA
local UDim2 = UDim2
local stringSplit = string.split
local scaleType = Settings.Properties.ScaleType
-- Clear connections
for i = 1,#propCons do
propCons[i]:Disconnect()
end
table.clear(propCons)
-- Hide full name viewer
Properties.FullNameFrame.Visible = false
Properties.FullNameFrameAttach.Disable()
for i = 1,maxEntries do
local entryData = propEntries[i]
if not propEntries[i] then entryData = Properties.NewPropEntry(i) propEntries[i] = entryData end
local entry = entryData.Gui
local guiElems = entryData.GuiElems
local nameFrame = guiElems.NameFrame
local propNameLabel = guiElems.PropName
local valueFrame = guiElems.ValueFrame
local expand = guiElems.Expand
local valueBox = guiElems.ValueBox
local propNameBox = guiElems.PropName
local rightButton = guiElems.RightButton
local editAttributeButton = guiElems.EditAttributeButton
local toggleAttributes = guiElems.ToggleAttributes
local prop = viewList[i + Properties.Index]
if prop then
local entryXOffset = (scaleType == 0 and scrollH.Index or 0)
entry.Visible = true
entry.Position = UDim2.new(0,-entryXOffset,0,entry.Position.Y.Offset)
entry.Size = UDim2.new(scaleType == 0 and 0 or 1, scaleType == 0 and Properties.ViewWidth + valueWidth or 0,0,22)
if prop.SpecialRow then
if prop.SpecialRow == "AddAttribute" then
nameFrame.Visible = false
valueFrame.Visible = false
guiElems.RowButton.Visible = true
end
else
-- Revert special row stuff
nameFrame.Visible = true
guiElems.RowButton.Visible = false
local depth = Properties.EntryIndent*(prop.Depth or 1)
local leftOffset = depth + Properties.EntryOffset
nameFrame.Position = UDim2.new(0,leftOffset,0,0)
propNameLabel.Size = UDim2.new(1,-2 - (scaleType == 0 and 0 or 6),1,0)
local gName = (prop.CategoryName and "CAT_"..prop.CategoryName) or prop.Class.."."..prop.Name..(prop.SubName or "")
if prop.CategoryName then
entry.BackgroundColor3 = Settings.Theme.Main1
valueFrame.Visible = false
propNameBox.Text = prop.CategoryName
propNameBox.Font = Enum.Font.SourceSansBold
expand.Visible = true
propNameBox.TextColor3 = Settings.Theme.Text
nameFrame.BackgroundTransparency = 1
nameFrame.Size = UDim2.new(1,0,1,0)
editAttributeButton.Visible = false
local showingAttrs = Settings.Properties.ShowAttributes
toggleAttributes.Position = UDim2.new(1,-85-leftOffset,0,0)
toggleAttributes.Text = (showingAttrs and "[Setting: ON]" or "[Setting: OFF]")
toggleAttributes.TextColor3 = Settings.Theme.Text
toggleAttributes.Visible = (prop.CategoryName == "Attributes")
else
local propName = prop.Name
local typeData = prop.ValueType
local typeName = typeData.Name
local tags = prop.Tags
local propObj = autoUpdateObjs[gName]
local attributeOffset = (prop.IsAttribute and 20 or 0)
editAttributeButton.Visible = (prop.IsAttribute and not prop.RootType)
toggleAttributes.Visible = false
-- Moving around the frames
if scaleType == 0 then
nameFrame.Size = UDim2.new(0,Properties.ViewWidth - leftOffset - 1,1,0)
valueFrame.Position = UDim2.new(0,Properties.ViewWidth,0,0)
valueFrame.Size = UDim2.new(0,valueWidth - attributeOffset,1,0)
else
nameFrame.Size = UDim2.new(0.5,-leftOffset - 1,1,0)
valueFrame.Position = UDim2.new(0.5,0,0,0)
valueFrame.Size = UDim2.new(0.5,-attributeOffset,1,0)
end
local nameArr = stringSplit(gName,".")
propNameBox.Text = prop.DisplayName or nameArr[#nameArr]
propNameBox.Font = Enum.Font.SourceSans
entry.BackgroundColor3 = Settings.Theme.Main2
valueFrame.Visible = true
expand.Visible = typeData.Category == "DataType" and Properties.ExpandableTypes[typeName] or Properties.ExpandableProps[gName]
propNameBox.TextColor3 = tags.ReadOnly and Settings.Theme.PlaceholderText or Settings.Theme.Text
-- Display property value
Properties.DisplayProp(prop,i)
if propObj then
if prop.IsAttribute then
propCons[#propCons+1] = getAttributeChangedSignal(propObj,prop.AttributeName):Connect(function()
Properties.DisplayProp(prop,i)
end)
else
propCons[#propCons+1] = getPropChangedSignal(propObj,propName):Connect(function()
Properties.DisplayProp(prop,i)
end)
end
end
-- Position and resize Input Box
local beforeVisible = valueBox.Visible
local inputFullName = inputProp and (inputProp.Class.."."..inputProp.Name..(inputProp.SubName or ""))
if gName == inputFullName then
nameFrame.BackgroundColor3 = Settings.Theme.ListSelection
nameFrame.BackgroundTransparency = 0
if typeData.Category == "Class" or typeData.Category == "Enum" or typeName == "BrickColor" then
valueFrame.BackgroundColor3 = Settings.Theme.TextBox
valueFrame.BackgroundTransparency = 0
valueBox.Visible = true
else
inputPropVisible = true
local scale = (scaleType == 0 and 0 or 0.5)
local offset = (scaleType == 0 and Properties.ViewWidth-scrollH.Index or 0)
local endOffset = 0
if typeName == "Color3" or typeName == "ColorSequence" then
offset = offset + 22
end
if typeName == "NumberSequence" or typeName == "ColorSequence" then
endOffset = 20
end
inputBox.Position = UDim2.new(scale,offset,0,entry.Position.Y.Offset)
inputBox.Size = UDim2.new(1-scale,-offset-endOffset-attributeOffset,0,22)
inputBox.Visible = true
valueBox.Visible = false
end
else
nameFrame.BackgroundColor3 = Settings.Theme.Main1
nameFrame.BackgroundTransparency = 1
valueFrame.BackgroundColor3 = Settings.Theme.Main1
valueFrame.BackgroundTransparency = 1
valueBox.Visible = beforeVisible
end
end
-- Expand
if prop.CategoryName or Properties.ExpandableTypes[prop.ValueType and prop.ValueType.Name] or Properties.ExpandableProps[gName] then
if Lib.CheckMouseInGui(expand) then
Main.MiscIcons:DisplayByKey(expand.Icon, expanded[gName] and "Collapse_Over" or "Expand_Over")
else
Main.MiscIcons:DisplayByKey(expand.Icon, expanded[gName] and "Collapse" or "Expand")
end
expand.Visible = true
else
expand.Visible = false
end
end
entry.Visible = true
else
entry.Visible = false
end
end
if not inputPropVisible then
inputBox.Visible = false
end
for i = maxEntries+1,#propEntries do
propEntries[i].Gui:Destroy()
propEntries[i] = nil
checkboxes[i] = nil
end
end
Properties.SetProp = function(prop,val,noupdate,prevAttribute)
local sList = Explorer.Selection.List
local propName = prop.Name
local subName = prop.SubName
local propClass = prop.Class
local typeData = prop.ValueType
local typeName = typeData.Name
local attributeName = prop.AttributeName
local rootTypeData = prop.RootType
local rootTypeName = rootTypeData and rootTypeData.Name
local fullName = prop.Class.."."..prop.Name..(prop.SubName or "")
local Vector3 = Vector3
for i = 1,#sList do
local node = sList[i]
local obj = node.Obj
if isa(obj,propClass) then
pcall(function()
local setVal = val
local root
if prop.IsAttribute then
root = getAttribute(obj,attributeName)
else
root = obj[propName]
end
if prevAttribute then
if prevAttribute.ValueType.Name == typeName then
setVal = getAttribute(obj,prevAttribute.AttributeName) or setVal
end
setAttribute(obj,prevAttribute.AttributeName,nil)
end
if rootTypeName then
if rootTypeName == "Vector2" then
setVal = Vector2.new((subName == ".X" and setVal) or root.X, (subName == ".Y" and setVal) or root.Y)
elseif rootTypeName == "Vector3" then
setVal = Vector3.new((subName == ".X" and setVal) or root.X, (subName == ".Y" and setVal) or root.Y, (subName == ".Z" and setVal) or root.Z)
elseif rootTypeName == "UDim" then
setVal = UDim.new((subName == ".Scale" and setVal) or root.Scale, (subName == ".Offset" and setVal) or root.Offset)
elseif rootTypeName == "UDim2" then
local rootX,rootY = root.X,root.Y
local X_UDim = (subName == ".X" and setVal) or UDim.new((subName == ".X.Scale" and setVal) or rootX.Scale, (subName == ".X.Offset" and setVal) or rootX.Offset)
local Y_UDim = (subName == ".Y" and setVal) or UDim.new((subName == ".Y.Scale" and setVal) or rootY.Scale, (subName == ".Y.Offset" and setVal) or rootY.Offset)
setVal = UDim2.new(X_UDim,Y_UDim)
elseif rootTypeName == "CFrame" then
local rootPos,rootRight,rootUp,rootLook = root.Position,root.RightVector,root.UpVector,root.LookVector
local pos = (subName == ".Position" and setVal) or Vector3.new((subName == ".Position.X" and setVal) or rootPos.X, (subName == ".Position.Y" and setVal) or rootPos.Y, (subName == ".Position.Z" and setVal) or rootPos.Z)
local rightV = (subName == ".RightVector" and setVal) or Vector3.new((subName == ".RightVector.X" and setVal) or rootRight.X, (subName == ".RightVector.Y" and setVal) or rootRight.Y, (subName == ".RightVector.Z" and setVal) or rootRight.Z)
local upV = (subName == ".UpVector" and setVal) or Vector3.new((subName == ".UpVector.X" and setVal) or rootUp.X, (subName == ".UpVector.Y" and setVal) or rootUp.Y, (subName == ".UpVector.Z" and setVal) or rootUp.Z)
local lookV = (subName == ".LookVector" and setVal) or Vector3.new((subName == ".LookVector.X" and setVal) or rootLook.X, (subName == ".RightVector.Y" and setVal) or rootLook.Y, (subName == ".RightVector.Z" and setVal) or rootLook.Z)
setVal = CFrame.fromMatrix(pos,rightV,upV,-lookV)
elseif rootTypeName == "Rect" then
local rootMin,rootMax = root.Min,root.Max
local min = Vector2.new((subName == ".Min.X" and setVal) or rootMin.X, (subName == ".Min.Y" and setVal) or rootMin.Y)
local max = Vector2.new((subName == ".Max.X" and setVal) or rootMax.X, (subName == ".Max.Y" and setVal) or rootMax.Y)
setVal = Rect.new(min,max)
elseif rootTypeName == "PhysicalProperties" then
local rootProps = PhysicalProperties.new(obj.Material)
local density = (subName == ".Density" and setVal) or (root and root.Density) or rootProps.Density
local friction = (subName == ".Friction" and setVal) or (root and root.Friction) or rootProps.Friction
local elasticity = (subName == ".Elasticity" and setVal) or (root and root.Elasticity) or rootProps.Elasticity
local frictionWeight = (subName == ".FrictionWeight" and setVal) or (root and root.FrictionWeight) or rootProps.FrictionWeight
local elasticityWeight = (subName == ".ElasticityWeight" and setVal) or (root and root.ElasticityWeight) or rootProps.ElasticityWeight
setVal = PhysicalProperties.new(density,friction,elasticity,frictionWeight,elasticityWeight)
elseif rootTypeName == "Ray" then
local rootOrigin,rootDirection = root.Origin,root.Direction
local origin = (subName == ".Origin" and setVal) or Vector3.new((subName == ".Origin.X" and setVal) or rootOrigin.X, (subName == ".Origin.Y" and setVal) or rootOrigin.Y, (subName == ".Origin.Z" and setVal) or rootOrigin.Z)
local direction = (subName == ".Direction" and setVal) or Vector3.new((subName == ".Direction.X" and setVal) or rootDirection.X, (subName == ".Direction.Y" and setVal) or rootDirection.Y, (subName == ".Direction.Z" and setVal) or rootDirection.Z)
setVal = Ray.new(origin,direction)
elseif rootTypeName == "Faces" then
local faces = {}
local faceList = {"Back","Bottom","Front","Left","Right","Top"}
for _,face in pairs(faceList) do
local val
if subName == "."..face then
val = setVal
else
val = root[face]
end
if val then faces[#faces+1] = Enum.NormalId[face] end
end
setVal = Faces.new(unpack(faces))
elseif rootTypeName == "Axes" then
local axes = {}
local axesList = {"X","Y","Z"}
for _,axe in pairs(axesList) do
local val
if subName == "."..axe then
val = setVal
else
val = root[axe]
end
if val then axes[#axes+1] = Enum.Axis[axe] end
end
setVal = Axes.new(unpack(axes))
elseif rootTypeName == "NumberRange" then
setVal = NumberRange.new(subName == ".Min" and setVal or root.Min, subName == ".Max" and setVal or root.Max)
end
end
if typeName == "PhysicalProperties" and setVal then
setVal = root or PhysicalProperties.new(obj.Material)
end
if prop.IsAttribute then
setAttribute(obj,attributeName,setVal)
else
obj[propName] = setVal
end
end)
end
end
if not noupdate then
Properties.ComputeConflicts(prop)
end
end
Properties.InitInputBox = function()
inputBox = create({
{1,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderSizePixel=0,Name="InputBox",Size=UDim2.new(0,200,0,22),Visible=false,ZIndex=2,}},
{2,"TextBox",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BackgroundTransparency=1,BorderColor3=Color3.new(0.062745101749897,0.51764708757401,1),BorderSizePixel=0,ClearTextOnFocus=false,Font=3,Parent={1},PlaceholderColor3=Color3.new(0.69803923368454,0.69803923368454,0.69803923368454),Position=UDim2.new(0,3,0,0),Size=UDim2.new(1,-6,1,0),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,ZIndex=2,}},
})
inputTextBox = inputBox.TextBox
inputBox.BackgroundColor3 = Settings.Theme.TextBox
inputBox.Parent = Properties.Window.GuiElems.Content.List
inputTextBox.FocusLost:Connect(function()
if not inputProp then return end
local prop = inputProp
inputProp = nil
local val = Properties.StringToValue(prop,inputTextBox.Text)
if val then Properties.SetProp(prop,val) else Properties.Refresh() end
end)
inputTextBox.Focused:Connect(function()
inputTextBox.SelectionStart = 1
inputTextBox.CursorPosition = #inputTextBox.Text + 1
end)
Lib.ViewportTextBox.convert(inputTextBox)
end
Properties.SetInputProp = function(prop,entryIndex,special)
local typeData = prop.ValueType
local typeName = typeData.Name
local fullName = prop.Class.."."..prop.Name..(prop.SubName or "")
local propObj = autoUpdateObjs[fullName]
local propVal = Properties.GetPropVal(prop,propObj)
if prop.Tags.ReadOnly then return end
inputProp = prop
if special then
if special == "color" then
if typeName == "Color3" then
inputTextBox.Text = propVal and Properties.ValueToString(prop,propVal) or ""
Properties.DisplayColorEditor(prop,propVal)
elseif typeName == "BrickColor" then
Properties.DisplayBrickColorEditor(prop,entryIndex,propVal)
elseif typeName == "ColorSequence" then
inputTextBox.Text = propVal and Properties.ValueToString(prop,propVal) or ""
Properties.DisplayColorSequenceEditor(prop,propVal)
end
elseif special == "right" then
if typeName == "NumberSequence" then
inputTextBox.Text = propVal and Properties.ValueToString(prop,propVal) or ""
Properties.DisplayNumberSequenceEditor(prop,propVal)
elseif typeName == "ColorSequence" then
inputTextBox.Text = propVal and Properties.ValueToString(prop,propVal) or ""
Properties.DisplayColorSequenceEditor(prop,propVal)
end
end
else
if Properties.IsTextEditable(prop) then
inputTextBox.Text = propVal and Properties.ValueToString(prop,propVal) or ""
inputTextBox:CaptureFocus()
elseif typeData.Category == "Enum" then
Properties.DisplayEnumDropdown(entryIndex)
elseif typeName == "BrickColor" then
Properties.DisplayBrickColorEditor(prop,entryIndex,propVal)
end
end
Properties.Refresh()
end
Properties.InitSearch = function()
local searchBox = Properties.GuiElems.ToolBar.SearchFrame.SearchBox
Lib.ViewportTextBox.convert(searchBox)
searchBox:GetPropertyChangedSignal("Text"):Connect(function()
Properties.SearchText = searchBox.Text
Properties.Update()
Properties.Refresh()
end)
end
Properties.InitEntryStuff = function()
Properties.EntryTemplate = create({
{1,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderColor3=Color3.new(0.1294117718935,0.1294117718935,0.1294117718935),Font=3,Name="Entry",Position=UDim2.new(0,1,0,1),Size=UDim2.new(0,250,0,22),Text="",TextSize=14,}},
{2,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BackgroundTransparency=1,BorderColor3=Color3.new(0.33725491166115,0.49019610881805,0.73725491762161),BorderSizePixel=0,Name="NameFrame",Parent={1},Position=UDim2.new(0,20,0,0),Size=UDim2.new(1,-40,1,0),}},
{3,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="PropName",Parent={2},Position=UDim2.new(0,2,0,0),Size=UDim2.new(1,-2,1,0),Text="Anchored",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.10000000149012,TextTruncate=1,TextXAlignment=0,}},
{4,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClipsDescendants=true,Font=3,Name="Expand",Parent={2},Position=UDim2.new(0,-20,0,1),Size=UDim2.new(0,20,0,20),Text="",TextSize=14,Visible=false,}},
{5,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642383285",ImageRectOffset=Vector2.new(144,16),ImageRectSize=Vector2.new(16,16),Name="Icon",Parent={4},Position=UDim2.new(0,2,0,2),ScaleType=4,Size=UDim2.new(0,16,0,16),}},
{6,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=4,Name="ToggleAttributes",Parent={2},Position=UDim2.new(1,-85,0,0),Size=UDim2.new(0,85,0,22),Text="[SETTING: OFF]",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.10000000149012,Visible=false,}},
{7,"Frame",{BackgroundColor3=Color3.new(0.04313725605607,0.35294118523598,0.68627452850342),BackgroundTransparency=1,BorderColor3=Color3.new(0.33725491166115,0.49019607901573,0.73725491762161),BorderSizePixel=0,Name="ValueFrame",Parent={1},Position=UDim2.new(1,-100,0,0),Size=UDim2.new(0,80,1,0),}},
{8,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderColor3=Color3.new(0.33725491166115,0.49019610881805,0.73725491762161),BorderSizePixel=0,Name="Line",Parent={7},Position=UDim2.new(0,-1,0,0),Size=UDim2.new(0,1,1,0),}},
{9,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="ColorButton",Parent={7},Size=UDim2.new(0,20,0,22),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,Visible=false,}},
{10,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderColor3=Color3.new(0,0,0),Name="ColorPreview",Parent={9},Position=UDim2.new(0,5,0,6),Size=UDim2.new(0,10,0,10),}},
{11,"UIGradient",{Parent={10},}},
{12,"Frame",{BackgroundTransparency=1,Name="EnumArrow",Parent={7},Position=UDim2.new(1,-16,0,3),Size=UDim2.new(0,16,0,16),Visible=false,}},
{13,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={12},Position=UDim2.new(0,8,0,9),Size=UDim2.new(0,1,0,1),}},
{14,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={12},Position=UDim2.new(0,7,0,8),Size=UDim2.new(0,3,0,1),}},
{15,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={12},Position=UDim2.new(0,6,0,7),Size=UDim2.new(0,5,0,1),}},
{16,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="ValueBox",Parent={7},Position=UDim2.new(0,4,0,0),Size=UDim2.new(1,-8,1,0),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.10000000149012,TextTruncate=1,TextXAlignment=0,}},
{17,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="RightButton",Parent={7},Position=UDim2.new(1,-20,0,0),Size=UDim2.new(0,20,0,22),Text="...",TextColor3=Color3.new(1,1,1),TextSize=14,Visible=false,}},
{18,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="SettingsButton",Parent={7},Position=UDim2.new(1,-20,0,0),Size=UDim2.new(0,20,0,22),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,Visible=false,}},
{19,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="SoundPreview",Parent={7},Size=UDim2.new(1,0,1,0),Visible=false,}},
{20,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="ControlButton",Parent={19},Size=UDim2.new(0,20,0,22),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
{21,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642383285",ImageRectOffset=Vector2.new(144,16),ImageRectSize=Vector2.new(16,16),Name="Icon",Parent={20},Position=UDim2.new(0,2,0,3),ScaleType=4,Size=UDim2.new(0,16,0,16),}},
{22,"Frame",{BackgroundColor3=Color3.new(0.3137255012989,0.3137255012989,0.3137255012989),BorderSizePixel=0,Name="TimeLine",Parent={19},Position=UDim2.new(0,26,0.5,-1),Size=UDim2.new(1,-34,0,2),}},
{23,"Frame",{BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.1294117718935,0.1294117718935,0.1294117718935),Name="Slider",Parent={22},Position=UDim2.new(0,-4,0,-8),Size=UDim2.new(0,8,0,18),}},
{24,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="EditAttributeButton",Parent={1},Position=UDim2.new(1,-20,0,0),Size=UDim2.new(0,20,0,22),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
{25,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5034718180",ImageTransparency=0.20000000298023,Name="Icon",Parent={24},Position=UDim2.new(0,2,0,3),Size=UDim2.new(0,16,0,16),}},
{26,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderSizePixel=0,Font=3,Name="RowButton",Parent={1},Size=UDim2.new(1,0,1,0),Text="Add Attribute",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.10000000149012,Visible=false,}},
})
local fullNameFrame = Lib.Frame.new()
local label = Lib.Label.new()
label.Parent = fullNameFrame.Gui
label.Position = UDim2.new(0,2,0,0)
label.Size = UDim2.new(1,-4,1,0)
fullNameFrame.Visible = false
fullNameFrame.Parent = window.Gui
Properties.FullNameFrame = fullNameFrame
Properties.FullNameFrameAttach = Lib.AttachTo(fullNameFrame)
end
Properties.Init = function() -- TODO: MAKE BETTER
local guiItems = create({
{1,"Folder",{Name="Items",}},
{2,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="ToolBar",Parent={1},Size=UDim2.new(1,0,0,22),}},
{3,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.1176470592618,0.1176470592618,0.1176470592618),BorderSizePixel=0,Name="SearchFrame",Parent={2},Position=UDim2.new(0,3,0,1),Size=UDim2.new(1,-6,0,18),}},
{4,"TextBox",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClearTextOnFocus=false,Font=3,Name="SearchBox",Parent={3},PlaceholderColor3=Color3.new(0.39215689897537,0.39215689897537,0.39215689897537),PlaceholderText="Search properties",Position=UDim2.new(0,4,0,0),Size=UDim2.new(1,-24,0,18),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,}},
{5,"UICorner",{CornerRadius=UDim.new(0,2),Parent={3},}},
{6,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Reset",Parent={3},Position=UDim2.new(1,-17,0,1),Size=UDim2.new(0,16,0,16),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
{7,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5034718129",ImageColor3=Color3.new(0.39215686917305,0.39215686917305,0.39215686917305),Parent={6},Size=UDim2.new(0,16,0,16),}},
{8,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Refresh",Parent={2},Position=UDim2.new(1,-20,0,1),Size=UDim2.new(0,18,0,18),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,Visible=false,}},
{9,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642310344",Parent={8},Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,12,0,12),}},
{10,"Frame",{BackgroundColor3=Color3.new(0.15686275064945,0.15686275064945,0.15686275064945),BorderSizePixel=0,Name="ScrollCorner",Parent={1},Position=UDim2.new(1,-16,1,-16),Size=UDim2.new(0,16,0,16),Visible=false,}},
{11,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ClipsDescendants=true,Name="List",Parent={1},Position=UDim2.new(0,0,0,23),Size=UDim2.new(1,0,1,-23),}},
})
-- Vars
categoryOrder = API.CategoryOrder
for category,_ in next,categoryOrder do
if not Properties.CollapsedCategories[category] then
expanded["CAT_"..category] = true
end
end
expanded["Sound.SoundId"] = true
-- Init window
window = Lib.Window.new()
Properties.Window = window
window:SetTitle("Properties")
toolBar = guiItems.ToolBar
propsFrame = guiItems.List
Properties.GuiElems.ToolBar = toolBar
Properties.GuiElems.PropsFrame = propsFrame
Properties.InitEntryStuff()
-- Window events
window.GuiElems.Main:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
if Properties.Window:IsContentVisible() then
Properties.UpdateView()
Properties.Refresh()
end
end)
window.OnActivate:Connect(function()
Properties.UpdateView()
Properties.Update()
Properties.Refresh()
end)
window.OnRestore:Connect(function()
Properties.UpdateView()
Properties.Update()
Properties.Refresh()
end)
-- Init scrollbars
scrollV = Lib.ScrollBar.new()
scrollV.WheelIncrement = 3
scrollV.Gui.Position = UDim2.new(1,-16,0,23)
scrollV:SetScrollFrame(propsFrame)
scrollV.Scrolled:Connect(function()
Properties.Index = scrollV.Index
Properties.Refresh()
end)
scrollH = Lib.ScrollBar.new(true)
scrollH.Increment = 5
scrollH.WheelIncrement = 20
scrollH.Gui.Position = UDim2.new(0,0,1,-16)
scrollH.Scrolled:Connect(function()
Properties.Refresh()
end)
-- Setup Gui
window.GuiElems.Line.Position = UDim2.new(0,0,0,22)
toolBar.Parent = window.GuiElems.Content
propsFrame.Parent = window.GuiElems.Content
guiItems.ScrollCorner.Parent = window.GuiElems.Content
scrollV.Gui.Parent = window.GuiElems.Content
scrollH.Gui.Parent = window.GuiElems.Content
Properties.InitInputBox()
Properties.InitSearch()
end
return Properties
end
return {InitDeps = initDeps, InitAfterMain = initAfterMain, Main = main} end, ScriptViewer = function() --[[ Script Viewer App Module
A script viewer that is basically a notepad
]]
-- Common Locals local Main,Lib,Apps,Settings -- Main Containers local Explorer, Properties, ScriptViewer, Notebook -- Major Apps local API,RMD,env,service,plr,create,createSimple -- Main Locals
local function initDeps(data) Main = data.Main Lib = data.Lib Apps = data.Apps Settings = data.Settings
API = data.API
RMD = data.RMD
env = data.env
service = data.service
plr = data.plr
create = data.create
createSimple = data.createSimple
end
local function initAfterMain() Explorer = Apps.Explorer Properties = Apps.Properties ScriptViewer = Apps.ScriptViewer Notebook = Apps.Notebook end
local function main() local ScriptViewer = {} local window, codeFrame local PreviousScr = nil
ScriptViewer.ViewScript = function(scr)
local success, source = pcall(env.decompile or function() end, scr)
if not success or not source then source, PreviousScr = "-- DEX - Source failed to decompile", nil else PreviousScr = scr end
codeFrame:SetText(source:gsub("\0", "\\0")) -- Fix stupid breaking script viewer
window:Show()
end
ScriptViewer.Init = function()
window = Lib.Window.new()
window:SetTitle("Script Viewer")
window:Resize(500,400)
ScriptViewer.Window = window
codeFrame = Lib.CodeFrame.new()
codeFrame.Frame.Position = UDim2.new(0,0,0,20)
codeFrame.Frame.Size = UDim2.new(1,0,1,-20)
codeFrame.Frame.Parent = window.GuiElems.Content
-- TODO: REMOVE AND MAKE BETTER
local copy = Instance.new("TextButton",window.GuiElems.Content)
copy.BackgroundTransparency = 1
copy.Size = UDim2.new(0.5,0,0,20)
copy.Text = "Copy to Clipboard"
copy.TextColor3 = Color3.new(1,1,1)
copy.MouseButton1Click:Connect(function()
local source = codeFrame:GetText()
env.setclipboard(source)
end)
local save = Instance.new("TextButton",window.GuiElems.Content)
save.BackgroundTransparency = 1
save.Position = UDim2.new(0.35,0,0,0)
save.Size = UDim2.new(0.3,0,0,20)
save.Text = "Save to File"
save.TextColor3 = Color3.new(1,1,1)
save.MouseButton1Click:Connect(function()
local source = codeFrame:GetText()
local filename = "Place_"..game["Run Service"].Parent.PlaceId.."_Script_"..os.time()..".txt"
env.writefile(filename, source)
if env.movefileas then
env.movefileas(filename, ".txt")
end
end)
local dumpbtn = Instance.new("TextButton",window.GuiElems.Content)
dumpbtn.BackgroundTransparency = 1
dumpbtn.Position = UDim2.new(0.7,0,0,0)
dumpbtn.Size = UDim2.new(0.3,0,0,20)
dumpbtn.Text = "Dump Functions"
dumpbtn.TextColor3 = Color3.new(1,1,1)
dumpbtn.MouseButton1Click:Connect(function()
if PreviousScr ~= nil then
pcall(function()
-- thanks King.Kevin#6025 you'll obviously be credited (no discord tag since that can easily be impersonated)
local getgc = getgc or get_gc_objects
local getupvalues = (debug and debug.getupvalues) or getupvalues or getupvals
local getconstants = (debug and debug.getconstants) or getconstants or getconsts
local getinfo = (debug and (debug.getinfo or debug.info)) or getinfo
local original = ("\n-- // Function Dumper made by King.Kevin\n-- // Script Path: %s\n\n--[["):format(PreviousScr:GetFullName())
local dump = original
local functions, function_count, data_base = {}, 0, {}
function functions:add_to_dump(str, indentation, new_line)
local new_line = new_line or true
dump = dump .. ("%s%s%s"):format(string.rep(" ", indentation), tostring(str), new_line and "\n" or "")
end
function functions:get_function_name(func)
local n = getinfo(func).name
return n ~= "" and n or "Unknown Name"
end
function functions:dump_table(input, indent, index)
local indent = indent < 0 and 0 or indent
functions:add_to_dump(("%s [%s] %s"):format(tostring(index), tostring(typeof(input)), tostring(input)), indent - 1)
local count = 0
for index, value in pairs(input) do
count = count + 1
if type(value) == "function" then
functions:add_to_dump(("%d [function] = %s"):format(count, functions:get_function_name(value)), indent)
elseif type(value) == "table" then
if not data_base[value] then
data_base[value] = true
functions:add_to_dump(("%d [table]:"):format(count), indent)
functions:dump_table(value, indent + 1, index)
else
functions:add_to_dump(("%d [table] (Recursive table detected)"):format(count), indent)
end
else
functions:add_to_dump(("%d [%s] = %s"):format(count, tostring(typeof(value)), tostring(value)), indent)
end
end
end
function functions:dump_function(input, indent)
functions:add_to_dump(("\nFunction Dump: %s"):format(functions:get_function_name(input)), indent)
functions:add_to_dump(("\nFunction Upvalues: %s"):format(functions:get_function_name(input)), indent)
for index, upvalue in pairs(getupvalues(input)) do
if type(upvalue) == "function" then
functions:add_to_dump(("%d [function] = %s"):format(index, functions:get_function_name(upvalue)), indent + 1)
elseif type(upvalue) == "table" then
if not data_base[upvalue] then
data_base[upvalue] = true
functions:add_to_dump(("%d [table]:"):format(index), indent + 1)
functions:dump_table(upvalue, indent + 2, index)
else
functions:add_to_dump(("%d [table] (Recursive table detected)"):format(index), indent + 1)
end
else
functions:add_to_dump(("%d [%s] = %s"):format(index, tostring(typeof(upvalue)), tostring(upvalue)), indent + 1)
end
end
functions:add_to_dump(("\nFunction Constants: %s"):format(functions:get_function_name(input)), indent)
for index, constant in pairs(getconstants(input)) do
if type(constant) == "function" then
functions:add_to_dump(("%d [function] = %s"):format(index, functions:get_function_name(constant)), indent + 1)
elseif type(constant) == "table" then
if not data_base[constant] then
data_base[constant] = true
functions:add_to_dump(("%d [table]:"):format(index), indent + 1)
functions:dump_table(constant, indent + 2, index)
else
functions:add_to_dump(("%d [table] (Recursive table detected)"):format(index), indent + 1)
end
else
functions:add_to_dump(("%d [%s] = %s"):format(index, tostring(typeof(constant)), tostring(constant)), indent + 1)
end
end
end
for _, _function in pairs(getgc()) do
if typeof(_function) == "function" and getfenv(_function).script and getfenv(_function).script == PreviousScr then
functions:dump_function(_function, 0)
functions:add_to_dump("\n" .. ("="):rep(100), 0, false)
end
end
local source = codeFrame:GetText()
if dump ~= original then source = source .. dump .. "]]" end
codeFrame:SetText(source)
end)
end
end)
end
return ScriptViewer
end
return {InitDeps = initDeps, InitAfterMain = initAfterMain, Main = main} end, Lib = function() --[[ Lib Module
Container for functions and classes
]]
-- Common Locals local Main,Lib,Apps,Settings -- Main Containers local Explorer, Properties, ScriptViewer, Notebook -- Major Apps local API,RMD,env,service,plr,create,createSimple -- Main Locals
local function initDeps(data) Main = data.Main Lib = data.Lib Apps = data.Apps Settings = data.Settings
API = data.API
RMD = data.RMD
env = data.env
service = data.service
plr = data.plr
create = data.create
createSimple = data.createSimple
end
local function initAfterMain() Explorer = Apps.Explorer Properties = Apps.Properties ScriptViewer = Apps.ScriptViewer Notebook = Apps.Notebook end
local function main() local Lib = {}
local renderStepped = service.RunService.RenderStepped
local signalWait = renderStepped.wait
local PH = newproxy() -- Placeholder, must be replaced in constructor
local SIGNAL = newproxy()
-- Usually for classes that work with a Roblox Object
local function initObj(props,mt)
local type = type
local function copy(t)
local res = {}
for i,v in pairs(t) do
if v == SIGNAL then
res[i] = Lib.Signal.new()
elseif type(v) == "table" then
res[i] = copy(v)
else
res[i] = v
end
end
return res
end
local newObj = copy(props)
return setmetatable(newObj,mt)
end
local function getGuiMT(props,funcs)
return {__index = function(self,ind) if not props[ind] then return funcs[ind] or self.Gui[ind] end end,
__newindex = function(self,ind,val) if not props[ind] then self.Gui[ind] = val else rawset(self,ind,val) end end}
end
-- Functions
Lib.FormatLuaString = (function()
local string = string
local gsub = string.gsub
local format = string.format
local char = string.char
local cleanTable = {['"'] = '\\"', ['\\'] = '\\\\'}
for i = 0,31 do
cleanTable[char(i)] = "\\"..format("%03d",i)
end
for i = 127,255 do
cleanTable[char(i)] = "\\"..format("%03d",i)
end
return function(str)
return gsub(str,"[\"\\\0-\31\127-\255]",cleanTable)
end
end)()
Lib.CheckMouseInGui = function(gui)
if gui == nil then return false end
local mouse = Main.Mouse
local guiPosition = gui.AbsolutePosition
local guiSize = gui.AbsoluteSize
return mouse.X >= guiPosition.X and mouse.X < guiPosition.X + guiSize.X and mouse.Y >= guiPosition.Y and mouse.Y < guiPosition.Y + guiSize.Y
end
Lib.IsShiftDown = function()
return service.UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or service.UserInputService:IsKeyDown(Enum.KeyCode.RightShift)
end
Lib.IsCtrlDown = function()
return service.UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or service.UserInputService:IsKeyDown(Enum.KeyCode.RightControl)
end
Lib.CreateArrow = function(size,num,dir)
local max = num
local arrowFrame = createSimple("Frame",{
BackgroundTransparency = 1,
Name = "Arrow",
Size = UDim2.new(0,size,0,size)
})
if dir == "up" then
for i = 1,num do
local newLine = createSimple("Frame",{
BackgroundColor3 = Color3.new(220/255,220/255,220/255),
BorderSizePixel = 0,
Position = UDim2.new(0,math.floor(size/2)-(i-1),0,math.floor(size/2)+i-math.floor(max/2)-1),
Size = UDim2.new(0,i+(i-1),0,1),
Parent = arrowFrame
})
end
return arrowFrame
elseif dir == "down" then
for i = 1,num do
local newLine = createSimple("Frame",{
BackgroundColor3 = Color3.new(220/255,220/255,220/255),
BorderSizePixel = 0,
Position = UDim2.new(0,math.floor(size/2)-(i-1),0,math.floor(size/2)-i+math.floor(max/2)+1),
Size = UDim2.new(0,i+(i-1),0,1),
Parent = arrowFrame
})
end
return arrowFrame
elseif dir == "left" then
for i = 1,num do
local newLine = createSimple("Frame",{
BackgroundColor3 = Color3.new(220/255,220/255,220/255),
BorderSizePixel = 0,
Position = UDim2.new(0,math.floor(size/2)+i-math.floor(max/2)-1,0,math.floor(size/2)-(i-1)),
Size = UDim2.new(0,1,0,i+(i-1)),
Parent = arrowFrame
})
end
return arrowFrame
elseif dir == "right" then
for i = 1,num do
local newLine = createSimple("Frame",{
BackgroundColor3 = Color3.new(220/255,220/255,220/255),
BorderSizePixel = 0,
Position = UDim2.new(0,math.floor(size/2)-i+math.floor(max/2)+1,0,math.floor(size/2)-(i-1)),
Size = UDim2.new(0,1,0,i+(i-1)),
Parent = arrowFrame
})
end
return arrowFrame
end
error("r u ok")
end
Lib.ParseXML = (function()
local func = function()
-- Only exists to parse RMD
-- from https://github.com/jonathanpoelen/xmlparser
local string, print, pairs = string, print, pairs
-- http://lua-users.org/wiki/StringTrim
local trim = function(s)
local from = s:match"^%s*()"
return from > #s and "" or s:match(".*%S", from)
end
local gtchar = string.byte('>', 1)
local slashchar = string.byte('/', 1)
local D = string.byte('D', 1)
local E = string.byte('E', 1)
function parse(s, evalEntities)
-- remove comments
s = s:gsub('<!%-%-(.-)%-%->', '')
local entities, tentities = {}
if evalEntities then
local pos = s:find('<[_%w]')
if pos then
s:sub(1, pos):gsub('<!ENTITY%s+([_%w]+)%s+(.)(.-)%2', function(name, q, entity)
entities[#entities+1] = {name=name, value=entity}
end)
tentities = createEntityTable(entities)
s = replaceEntities(s:sub(pos), tentities)
end
end
local t, l = {}, {}
local addtext = function(txt)
txt = txt:match'^%s*(.*%S)' or ''
if #txt ~= 0 then
t[#t+1] = {text=txt}
end
end
s:gsub('<([?!/]?)([-:_%w]+)%s*(/?>?)([^<]*)', function(type, name, closed, txt)
-- open
if #type == 0 then
local a = {}
if #closed == 0 then
local len = 0
for all,aname,_,value,starttxt in string.gmatch(txt, "(.-([-_%w]+)%s*=%s*(.)(.-)%3%s*(/?>?))") do
len = len + #all
a[aname] = value
if #starttxt ~= 0 then
txt = txt:sub(len+1)
closed = starttxt
break
end
end
end
t[#t+1] = {tag=name, attrs=a, children={}}
if closed:byte(1) ~= slashchar then
l[#l+1] = t
t = t[#t].children
end
addtext(txt)
-- close
elseif '/' == type then
t = l[#l]
l[#l] = nil
addtext(txt)
-- ENTITY
elseif '!' == type then
if E == name:byte(1) then
txt:gsub('([_%w]+)%s+(.)(.-)%2', function(name, q, entity)
entities[#entities+1] = {name=name, value=entity}
end, 1)
end
-- elseif '?' == type then
-- print('? ' .. name .. ' // ' .. attrs .. '$$')
-- elseif '-' == type then
-- print('comment ' .. name .. ' // ' .. attrs .. '$$')
-- else
-- print('o ' .. #p .. ' // ' .. name .. ' // ' .. attrs .. '$$')
end
end)
return {children=t, entities=entities, tentities=tentities}
end
function parseText(txt)
return parse(txt)
end
function defaultEntityTable()
return { quot='"', apos='\'', lt='<', gt='>', amp='&', tab='\t', nbsp=' ', }
end
function replaceEntities(s, entities)
return s:gsub('&([^;]+);', entities)
end
function createEntityTable(docEntities, resultEntities)
entities = resultEntities or defaultEntityTable()
for _,e in pairs(docEntities) do
e.value = replaceEntities(e.value, entities)
entities[e.name] = e.value
end
return entities
end
return parseText
end
local newEnv = setmetatable({},{__index = getfenv()})
setfenv(func,newEnv)
return func()
end)()
Lib.FastWait = function(s)
if not s then return signalWait(renderStepped) end
local start = tick()
while tick() - start < s do signalWait(renderStepped) end
end
Lib.ButtonAnim = function(button,data)
local holding = false
local disabled = false
local mode = data and data.Mode or 1
local control = {}
if mode == 2 then
local lerpTo = data.LerpTo or Color3.new(0,0,0)
local delta = data.LerpDelta or 0.2
control.StartColor = data.StartColor or button.BackgroundColor3
control.PressColor = data.PressColor or control.StartColor:lerp(lerpTo,delta)
control.HoverColor = data.HoverColor or control.StartColor:lerp(control.PressColor,0.6)
control.OutlineColor = data.OutlineColor
end
button.InputBegan:Connect(function(input)
if disabled then return end
if input.UserInputType == Enum.UserInputType.MouseMovement and not holding then
if mode == 1 then
button.BackgroundTransparency = 0.4
elseif mode == 2 then
button.BackgroundColor3 = control.HoverColor
end
elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
holding = true
if mode == 1 then
button.BackgroundTransparency = 0
elseif mode == 2 then
button.BackgroundColor3 = control.PressColor
if control.OutlineColor then button.BorderColor3 = control.PressColor end
end
end
end)
button.InputEnded:Connect(function(input)
if disabled then return end
if input.UserInputType == Enum.UserInputType.MouseMovement and not holding then
if mode == 1 then
button.BackgroundTransparency = 1
elseif mode == 2 then
button.BackgroundColor3 = control.StartColor
end
elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
holding = false
if mode == 1 then
button.BackgroundTransparency = Lib.CheckMouseInGui(button) and 0.4 or 1
elseif mode == 2 then
button.BackgroundColor3 = Lib.CheckMouseInGui(button) and control.HoverColor or control.StartColor
if control.OutlineColor then button.BorderColor3 = control.OutlineColor end
end
end
end)
control.Disable = function()
disabled = true
holding = false
if mode == 1 then
button.BackgroundTransparency = 1
elseif mode == 2 then
button.BackgroundColor3 = control.StartColor
end
end
control.Enable = function()
disabled = false
end
return control
end
Lib.FindAndRemove = function(t,item)
local pos = table.find(t,item)
if pos then table.remove(t,pos) end
end
Lib.AttachTo = function(obj,data)
local target,posOffX,posOffY,sizeOffX,sizeOffY,resize,con
local disabled = false
local function update()
if not obj or not target then return end
local targetPos = target.AbsolutePosition
local targetSize = target.AbsoluteSize
obj.Position = UDim2.new(0,targetPos.X + posOffX,0,targetPos.Y + posOffY)
if resize then obj.Size = UDim2.new(0,targetSize.X + sizeOffX,0,targetSize.Y + sizeOffY) end
end
local function setup(o,data)
obj = o
data = data or {}
target = data.Target
posOffX = data.PosOffX or 0
posOffY = data.PosOffY or 0
sizeOffX = data.SizeOffX or 0
sizeOffY = data.SizeOffY or 0
resize = data.Resize or false
if con then con:Disconnect() con = nil end
if target then
con = target.Changed:Connect(function(prop)
if not disabled and prop == "AbsolutePosition" or prop == "AbsoluteSize" then
update()
end
end)
end
update()
end
setup(obj,data)
return {
SetData = function(obj,data)
setup(obj,data)
end,
Enable = function()
disabled = false
update()
end,
Disable = function()
disabled = true
end,
Destroy = function()
con:Disconnect()
con = nil
end,
}
end
Lib.ProtectedGuis = {}
Lib.ShowGui = function(gui)
if env.gethui then
gui.Parent = env.gethui()
elseif env.protectgui then
env.protectgui(gui)
gui.Parent = Main.GuiHolder
else
gui.Parent = Main.GuiHolder
end
end
Lib.ColorToBytes = function(col)
local round = math.round
return string.format("%d, %d, %d",round(col.r*255),round(col.g*255),round(col.b*255))
end
Lib.ReadFile = function(filename)
if not env.readfile then return end
local s,contents = pcall(env.readfile,filename)
if s and contents then return contents end
end
Lib.DeferFunc = function(f,...)
signalWait(renderStepped)
return f(...)
end
Lib.LoadCustomAsset = function(filepath)
if not env.getcustomasset or not env.isfile or not env.isfile(filepath) then return end
return env.getcustomasset(filepath)
end
Lib.FetchCustomAsset = function(url,filepath)
if not env.writefile then return end
local s,data = pcall(game["Run Service"].Parent.HttpGet,game["Run Service"].Parent,url)
if not s then return end
env.writefile(filepath,data)
return Lib.LoadCustomAsset(filepath)
end
-- Classes
Lib.Signal = (function()
local funcs = {}
local disconnect = function(con)
local pos = table.find(con.Signal.Connections,con)
if pos then table.remove(con.Signal.Connections,pos) end
end
funcs.Connect = function(self,func)
if type(func) ~= "function" then error("Attempt to connect a non-function") end
local con = {
Signal = self,
Func = func,
Disconnect = disconnect
}
self.Connections[#self.Connections+1] = con
return con
end
funcs.Fire = function(self,...)
for i,v in next,self.Connections do
xpcall(coroutine.wrap(v.Func),function(e) warn(e.."\n"..debug.traceback()) end,...)
end
end
local mt = {
__index = funcs,
__tostring = function(self)
return "Signal: " .. tostring(#self.Connections) .. " Connections"
end
}
local function new()
local obj = {}
obj.Connections = {}
return setmetatable(obj,mt)
end
return {new = new}
end)()
Lib.Set = (function()
local funcs = {}
funcs.Add = function(self,obj)
if self.Map[obj] then return end
local list = self.List
list[#list+1] = obj
self.Map[obj] = true
self.Changed:Fire()
end
funcs.AddTable = function(self,t)
local changed
local list,map = self.List,self.Map
for i = 1,#t do
local elem = t[i]
if not map[elem] then
list[#list+1] = elem
map[elem] = true
changed = true
end
end
if changed then self.Changed:Fire() end
end
funcs.Remove = function(self,obj)
if not self.Map[obj] then return end
local list = self.List
local pos = table.find(list,obj)
if pos then table.remove(list,pos) end
self.Map[obj] = nil
self.Changed:Fire()
end
funcs.RemoveTable = function(self,t)
local changed
local list,map = self.List,self.Map
local removeSet = {}
for i = 1,#t do
local elem = t[i]
map[elem] = nil
removeSet[elem] = true
end
for i = #list,1,-1 do
local elem = list[i]
if removeSet[elem] then
table.remove(list,i)
changed = true
end
end
if changed then self.Changed:Fire() end
end
funcs.Set = function(self,obj)
if #self.List == 1 and self.List[1] == obj then return end
self.List = {obj}
self.Map = {[obj] = true}
self.Changed:Fire()
end
funcs.SetTable = function(self,t)
local newList,newMap = {},{}
self.List,self.Map = newList,newMap
table.move(t,1,#t,1,newList)
for i = 1,#t do
newMap[t[i]] = true
end
self.Changed:Fire()
end
funcs.Clear = function(self)
if #self.List == 0 then return end
self.List = {}
self.Map = {}
self.Changed:Fire()
end
local mt = {__index = funcs}
local function new()
local obj = setmetatable({
List = {},
Map = {},
Changed = Lib.Signal.new()
},mt)
return obj
end
return {new = new}
end)()
Lib.IconMap = (function()
local funcs = {}
funcs.GetLabel = function(self)
local label = Instance.new("ImageLabel")
self:SetupLabel(label)
return label
end
funcs.SetupLabel = function(self,obj)
obj.BackgroundTransparency = 1
obj.ImageRectOffset = Vector2.new(0,0)
obj.ImageRectSize = Vector2.new(self.IconSizeX,self.IconSizeY)
obj.ScaleType = Enum.ScaleType.Crop
obj.Size = UDim2.new(0,self.IconSizeX,0,self.IconSizeY)
end
funcs.Display = function(self,obj,index)
obj.Image = self.MapId
if not self.NumX then
obj.ImageRectOffset = Vector2.new(self.IconSizeX*index, 0)
else
obj.ImageRectOffset = Vector2.new(self.IconSizeX*(index % self.NumX), self.IconSizeY*math.floor(index / self.NumX))
end
end
funcs.DisplayByKey = function(self,obj,key)
if self.IndexDict[key] then
self:Display(obj,self.IndexDict[key])
end
end
funcs.SetDict = function(self,dict)
self.IndexDict = dict
end
local mt = {}
mt.__index = funcs
local function new(mapId,mapSizeX,mapSizeY,iconSizeX,iconSizeY)
local obj = setmetatable({
MapId = mapId,
MapSizeX = mapSizeX,
MapSizeY = mapSizeY,
IconSizeX = iconSizeX,
IconSizeY = iconSizeY,
NumX = mapSizeX/iconSizeX,
IndexDict = {}
},mt)
return obj
end
local function newLinear(mapId,iconSizeX,iconSizeY)
local obj = setmetatable({
MapId = mapId,
IconSizeX = iconSizeX,
IconSizeY = iconSizeY,
IndexDict = {}
},mt)
return obj
end
return {new = new, newLinear = newLinear}
end)()
Lib.ScrollBar = (function()
local funcs = {}
local user = service.UserInputService
local mouse = plr:GetMouse()
local checkMouseInGui = Lib.CheckMouseInGui
local createArrow = Lib.CreateArrow
local function drawThumb(self)
local total = self.TotalSpace
local visible = self.VisibleSpace
local index = self.Index
local scrollThumb = self.GuiElems.ScrollThumb
local scrollThumbFrame = self.GuiElems.ScrollThumbFrame
if not (self:CanScrollUp() or self:CanScrollDown()) then
scrollThumb.Visible = false
else
scrollThumb.Visible = true
end
if self.Horizontal then
scrollThumb.Size = UDim2.new(visible/total,0,1,0)
if scrollThumb.AbsoluteSize.X < 16 then
scrollThumb.Size = UDim2.new(0,16,1,0)
end
local fs = scrollThumbFrame.AbsoluteSize.X
local bs = scrollThumb.AbsoluteSize.X
scrollThumb.Position = UDim2.new(self:GetScrollPercent()*(fs-bs)/fs,0,0,0)
else
scrollThumb.Size = UDim2.new(1,0,visible/total,0)
if scrollThumb.AbsoluteSize.Y < 16 then
scrollThumb.Size = UDim2.new(1,0,0,16)
end
local fs = scrollThumbFrame.AbsoluteSize.Y
local bs = scrollThumb.AbsoluteSize.Y
scrollThumb.Position = UDim2.new(0,0,self:GetScrollPercent()*(fs-bs)/fs,0)
end
end
local function createFrame(self)
local newFrame = createSimple("Frame",{Style=0,Active=true,AnchorPoint=Vector2.new(0,0),BackgroundColor3=Color3.new(0.35294118523598,0.35294118523598,0.35294118523598),BackgroundTransparency=0,BorderColor3=Color3.new(0.10588236153126,0.16470588743687,0.20784315466881),BorderSizePixel=0,ClipsDescendants=false,Draggable=false,Position=UDim2.new(1,-16,0,0),Rotation=0,Selectable=false,Size=UDim2.new(0,16,1,0),SizeConstraint=0,Visible=true,ZIndex=1,Name="ScrollBar",})
local button1 = nil
local button2 = nil
if self.Horizontal then
newFrame.Size = UDim2.new(1,0,0,16)
button1 = createSimple("ImageButton",{
Parent = newFrame,
Name = "Left",
Size = UDim2.new(0,16,0,16),
BackgroundTransparency = 1,
BorderSizePixel = 0,
AutoButtonColor = false
})
createArrow(16,4,"left").Parent = button1
button2 = createSimple("ImageButton",{
Parent = newFrame,
Name = "Right",
Position = UDim2.new(1,-16,0,0),
Size = UDim2.new(0,16,0,16),
BackgroundTransparency = 1,
BorderSizePixel = 0,
AutoButtonColor = false
})
createArrow(16,4,"right").Parent = button2
else
newFrame.Size = UDim2.new(0,16,1,0)
button1 = createSimple("ImageButton",{
Parent = newFrame,
Name = "Up",
Size = UDim2.new(0,16,0,16),
BackgroundTransparency = 1,
BorderSizePixel = 0,
AutoButtonColor = false
})
createArrow(16,4,"up").Parent = button1
button2 = createSimple("ImageButton",{
Parent = newFrame,
Name = "Down",
Position = UDim2.new(0,0,1,-16),
Size = UDim2.new(0,16,0,16),
BackgroundTransparency = 1,
BorderSizePixel = 0,
AutoButtonColor = false
})
createArrow(16,4,"down").Parent = button2
end
local scrollThumbFrame = createSimple("Frame",{
BackgroundTransparency = 1,
Parent = newFrame
})
if self.Horizontal then
scrollThumbFrame.Position = UDim2.new(0,16,0,0)
scrollThumbFrame.Size = UDim2.new(1,-32,1,0)
else
scrollThumbFrame.Position = UDim2.new(0,0,0,16)
scrollThumbFrame.Size = UDim2.new(1,0,1,-32)
end
local scrollThumb = createSimple("Frame",{
BackgroundColor3 = Color3.new(120/255,120/255,120/255),
BorderSizePixel = 0,
Parent = scrollThumbFrame
})
local markerFrame = createSimple("Frame",{
BackgroundTransparency = 1,
Name = "Markers",
Size = UDim2.new(1,0,1,0),
Parent = scrollThumbFrame
})
local buttonPress = false
local thumbPress = false
local thumbFramePress = false
--local thumbColor = Color3.new(120/255,120/255,120/255)
--local thumbSelectColor = Color3.new(140/255,140/255,140/255)
button1.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and not buttonPress and self:CanScrollUp() then button1.BackgroundTransparency = 0.8 end
if input.UserInputType ~= Enum.UserInputType.MouseButton1 or not self:CanScrollUp() then return end
buttonPress = true
button1.BackgroundTransparency = 0.5
if self:CanScrollUp() then self:ScrollUp() self.Scrolled:Fire() end
local buttonTick = tick()
local releaseEvent
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
releaseEvent:Disconnect()
if checkMouseInGui(button1) and self:CanScrollUp() then button1.BackgroundTransparency = 0.8 else button1.BackgroundTransparency = 1 end
buttonPress = false
end)
while buttonPress do
if tick() - buttonTick >= 0.3 and self:CanScrollUp() then
self:ScrollUp()
self.Scrolled:Fire()
end
wait()
end
end)
button1.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and not buttonPress then button1.BackgroundTransparency = 1 end
end)
button2.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and not buttonPress and self:CanScrollDown() then button2.BackgroundTransparency = 0.8 end
if input.UserInputType ~= Enum.UserInputType.MouseButton1 or not self:CanScrollDown() then return end
buttonPress = true
button2.BackgroundTransparency = 0.5
if self:CanScrollDown() then self:ScrollDown() self.Scrolled:Fire() end
local buttonTick = tick()
local releaseEvent
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
releaseEvent:Disconnect()
if checkMouseInGui(button2) and self:CanScrollDown() then button2.BackgroundTransparency = 0.8 else button2.BackgroundTransparency = 1 end
buttonPress = false
end)
while buttonPress do
if tick() - buttonTick >= 0.3 and self:CanScrollDown() then
self:ScrollDown()
self.Scrolled:Fire()
end
wait()
end
end)
button2.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and not buttonPress then button2.BackgroundTransparency = 1 end
end)
scrollThumb.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and not thumbPress then scrollThumb.BackgroundTransparency = 0.2 scrollThumb.BackgroundColor3 = self.ThumbSelectColor end
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
local dir = self.Horizontal and "X" or "Y"
local lastThumbPos = nil
buttonPress = false
thumbFramePress = false
thumbPress = true
scrollThumb.BackgroundTransparency = 0
local mouseOffset = mouse[dir] - scrollThumb.AbsolutePosition[dir]
local mouseStart = mouse[dir]
local releaseEvent
local mouseEvent
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
releaseEvent:Disconnect()
if mouseEvent then mouseEvent:Disconnect() end
if checkMouseInGui(scrollThumb) then scrollThumb.BackgroundTransparency = 0.2 else scrollThumb.BackgroundTransparency = 0 scrollThumb.BackgroundColor3 = self.ThumbColor end
thumbPress = false
end)
self:Update()
mouseEvent = user.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and thumbPress and releaseEvent.Connected then
local thumbFrameSize = scrollThumbFrame.AbsoluteSize[dir]-scrollThumb.AbsoluteSize[dir]
local pos = mouse[dir] - scrollThumbFrame.AbsolutePosition[dir] - mouseOffset
if pos > thumbFrameSize then
pos = thumbFrameSize
elseif pos < 0 then
pos = 0
end
if lastThumbPos ~= pos then
lastThumbPos = pos
self:ScrollTo(math.floor(0.5+pos/thumbFrameSize*(self.TotalSpace-self.VisibleSpace)))
end
wait()
end
end)
end)
scrollThumb.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and not thumbPress then scrollThumb.BackgroundTransparency = 0 scrollThumb.BackgroundColor3 = self.ThumbColor end
end)
scrollThumbFrame.InputBegan:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 or checkMouseInGui(scrollThumb) then return end
local dir = self.Horizontal and "X" or "Y"
local scrollDir = 0
if mouse[dir] >= scrollThumb.AbsolutePosition[dir] + scrollThumb.AbsoluteSize[dir] then
scrollDir = 1
end
local function doTick()
local scrollSize = self.VisibleSpace - 1
if scrollDir == 0 and mouse[dir] < scrollThumb.AbsolutePosition[dir] then
self:ScrollTo(self.Index - scrollSize)
elseif scrollDir == 1 and mouse[dir] >= scrollThumb.AbsolutePosition[dir] + scrollThumb.AbsoluteSize[dir] then
self:ScrollTo(self.Index + scrollSize)
end
end
thumbPress = false
thumbFramePress = true
doTick()
local thumbFrameTick = tick()
local releaseEvent
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
releaseEvent:Disconnect()
thumbFramePress = false
end)
while thumbFramePress do
if tick() - thumbFrameTick >= 0.3 and checkMouseInGui(scrollThumbFrame) then
doTick()
end
wait()
end
end)
newFrame.MouseWheelForward:Connect(function()
self:ScrollTo(self.Index - self.WheelIncrement)
end)
newFrame.MouseWheelBackward:Connect(function()
self:ScrollTo(self.Index + self.WheelIncrement)
end)
self.GuiElems.ScrollThumb = scrollThumb
self.GuiElems.ScrollThumbFrame = scrollThumbFrame
self.GuiElems.Button1 = button1
self.GuiElems.Button2 = button2
self.GuiElems.MarkerFrame = markerFrame
return newFrame
end
funcs.Update = function(self,nocallback)
local total = self.TotalSpace
local visible = self.VisibleSpace
local index = self.Index
local button1 = self.GuiElems.Button1
local button2 = self.GuiElems.Button2
self.Index = math.clamp(self.Index,0,math.max(0,total-visible))
if self.LastTotalSpace ~= self.TotalSpace then
self.LastTotalSpace = self.TotalSpace
self:UpdateMarkers()
end
if self:CanScrollUp() then
for i,v in pairs(button1.Arrow:GetChildren()) do
v.BackgroundTransparency = 0
end
else
button1.BackgroundTransparency = 1
for i,v in pairs(button1.Arrow:GetChildren()) do
v.BackgroundTransparency = 0.5
end
end
if self:CanScrollDown() then
for i,v in pairs(button2.Arrow:GetChildren()) do
v.BackgroundTransparency = 0
end
else
button2.BackgroundTransparency = 1
for i,v in pairs(button2.Arrow:GetChildren()) do
v.BackgroundTransparency = 0.5
end
end
drawThumb(self)
end
funcs.UpdateMarkers = function(self)
local markerFrame = self.GuiElems.MarkerFrame
markerFrame:ClearAllChildren()
for i,v in pairs(self.Markers) do
if i < self.TotalSpace then
createSimple("Frame",{
BackgroundTransparency = 0,
BackgroundColor3 = v,
BorderSizePixel = 0,
Position = self.Horizontal and UDim2.new(i/self.TotalSpace,0,1,-6) or UDim2.new(1,-6,i/self.TotalSpace,0),
Size = self.Horizontal and UDim2.new(0,1,0,6) or UDim2.new(0,6,0,1),
Name = "Marker"..tostring(i),
Parent = markerFrame
})
end
end
end
funcs.AddMarker = function(self,ind,color)
self.Markers[ind] = color or Color3.new(0,0,0)
end
funcs.ScrollTo = function(self,ind,nocallback)
self.Index = ind
self:Update()
if not nocallback then
self.Scrolled:Fire()
end
end
funcs.ScrollUp = function(self)
self.Index = self.Index - self.Increment
self:Update()
end
funcs.ScrollDown = function(self)
self.Index = self.Index + self.Increment
self:Update()
end
funcs.CanScrollUp = function(self)
return self.Index > 0
end
funcs.CanScrollDown = function(self)
return self.Index + self.VisibleSpace < self.TotalSpace
end
funcs.GetScrollPercent = function(self)
return self.Index/(self.TotalSpace-self.VisibleSpace)
end
funcs.SetScrollPercent = function(self,perc)
self.Index = math.floor(perc*(self.TotalSpace-self.VisibleSpace))
self:Update()
end
funcs.Texture = function(self,data)
self.ThumbColor = data.ThumbColor or Color3.new(0,0,0)
self.ThumbSelectColor = data.ThumbSelectColor or Color3.new(0,0,0)
self.GuiElems.ScrollThumb.BackgroundColor3 = data.ThumbColor or Color3.new(0,0,0)
self.Gui.BackgroundColor3 = data.FrameColor or Color3.new(0,0,0)
self.GuiElems.Button1.BackgroundColor3 = data.ButtonColor or Color3.new(0,0,0)
self.GuiElems.Button2.BackgroundColor3 = data.ButtonColor or Color3.new(0,0,0)
for i,v in pairs(self.GuiElems.Button1.Arrow:GetChildren()) do
v.BackgroundColor3 = data.ArrowColor or Color3.new(0,0,0)
end
for i,v in pairs(self.GuiElems.Button2.Arrow:GetChildren()) do
v.BackgroundColor3 = data.ArrowColor or Color3.new(0,0,0)
end
end
funcs.SetScrollFrame = function(self,frame)
if self.ScrollUpEvent then self.ScrollUpEvent:Disconnect() self.ScrollUpEvent = nil end
if self.ScrollDownEvent then self.ScrollDownEvent:Disconnect() self.ScrollDownEvent = nil end
self.ScrollUpEvent = frame.MouseWheelForward:Connect(function() self:ScrollTo(self.Index - self.WheelIncrement) end)
self.ScrollDownEvent = frame.MouseWheelBackward:Connect(function() self:ScrollTo(self.Index + self.WheelIncrement) end)
end
local mt = {}
mt.__index = funcs
local function new(hor)
local obj = setmetatable({
Index = 0,
VisibleSpace = 0,
TotalSpace = 0,
Increment = 1,
WheelIncrement = 1,
Markers = {},
GuiElems = {},
Horizontal = hor,
LastTotalSpace = 0,
Scrolled = Lib.Signal.new()
},mt)
obj.Gui = createFrame(obj)
obj:Texture({
ThumbColor = Color3.fromRGB(60,60,60),
ThumbSelectColor = Color3.fromRGB(75,75,75),
ArrowColor = Color3.new(1,1,1),
FrameColor = Color3.fromRGB(40,40,40),
ButtonColor = Color3.fromRGB(75,75,75)
})
return obj
end
return {new = new}
end)()
Lib.Window = (function()
local funcs = {}
local static = {MinWidth = 200, FreeWidth = 200}
local mouse = plr:GetMouse()
local sidesGui,alignIndicator
local visibleWindows = {}
local leftSide = {Width = 300, Windows = {}, ResizeCons = {}, Hidden = true}
local rightSide = {Width = 300, Windows = {}, ResizeCons = {}, Hidden = true}
local displayOrderStart
local sideDisplayOrder
local sideTweenInfo = TweenInfo.new(0.3,Enum.EasingStyle.Quad,Enum.EasingDirection.Out)
local tweens = {}
local isA = game["Run Service"].Parent.IsA
local theme = {
MainColor1 = Color3.fromRGB(52,52,52),
MainColor2 = Color3.fromRGB(45,45,45),
Button = Color3.fromRGB(60,60,60)
}
local function stopTweens()
for i = 1,#tweens do
tweens[i]:Cancel()
end
tweens = {}
end
local function resizeHook(self,resizer,dir)
local guiMain = self.GuiElems.Main
resizer.InputBegan:Connect(function(input)
if not self.Dragging and not self.Resizing and self.Resizable and self.ResizableInternal then
local isH = dir:find("[WE]") and true
local isV = dir:find("[NS]") and true
local signX = dir:find("W",1,true) and -1 or 1
local signY = dir:find("N",1,true) and -1 or 1
if self.Minimized and isV then return end
if input.UserInputType == Enum.UserInputType.MouseMovement then
resizer.BackgroundTransparency = 0.5
elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
local releaseEvent,mouseEvent
local offX = mouse.X - resizer.AbsolutePosition.X
local offY = mouse.Y - resizer.AbsolutePosition.Y
self.Resizing = resizer
resizer.BackgroundTransparency = 1
releaseEvent = service.UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
releaseEvent:Disconnect()
mouseEvent:Disconnect()
self.Resizing = false
resizer.BackgroundTransparency = 1
end
end)
mouseEvent = service.UserInputService.InputChanged:Connect(function(input)
if self.Resizable and self.ResizableInternal and input.UserInputType == Enum.UserInputType.MouseMovement then
self:StopTweens()
local deltaX = input.Position.X - resizer.AbsolutePosition.X - offX
local deltaY = input.Position.Y - resizer.AbsolutePosition.Y - offY
if guiMain.AbsoluteSize.X + deltaX*signX < self.MinX then deltaX = signX*(self.MinX - guiMain.AbsoluteSize.X) end
if guiMain.AbsoluteSize.Y + deltaY*signY < self.MinY then deltaY = signY*(self.MinY - guiMain.AbsoluteSize.Y) end
if signY < 0 and guiMain.AbsolutePosition.Y + deltaY < 0 then deltaY = -guiMain.AbsolutePosition.Y end
guiMain.Position = guiMain.Position + UDim2.new(0,(signX < 0 and deltaX or 0),0,(signY < 0 and deltaY or 0))
self.SizeX = self.SizeX + (isH and deltaX*signX or 0)
self.SizeY = self.SizeY + (isV and deltaY*signY or 0)
guiMain.Size = UDim2.new(0,self.SizeX,0,self.Minimized and 20 or self.SizeY)
--if isH then self.SizeX = guiMain.AbsoluteSize.X end
--if isV then self.SizeY = guiMain.AbsoluteSize.Y end
end
end)
end
end
end)
resizer.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and self.Resizing ~= resizer then
resizer.BackgroundTransparency = 1
end
end)
end
local updateWindows
local function moveToTop(window)
local found = table.find(visibleWindows,window)
if found then
table.remove(visibleWindows,found)
table.insert(visibleWindows,1,window)
updateWindows()
end
end
local function sideHasRoom(side,neededSize)
local maxY = sidesGui.AbsoluteSize.Y - (math.max(0,#side.Windows - 1) * 4)
local inc = 0
for i,v in pairs(side.Windows) do
inc = inc + (v.MinY or 100)
if inc > maxY - neededSize then return false end
end
return true
end
local function getSideInsertPos(side,curY)
local pos = #side.Windows + 1
local range = {0,sidesGui.AbsoluteSize.Y}
for i,v in pairs(side.Windows) do
local midPos = v.PosY + v.SizeY/2
if curY <= midPos then
pos = i
range[2] = midPos
break
else
range[1] = midPos
end
end
return pos,range
end
local function focusInput(self,obj)
if isA(obj,"GuiButton") then
obj.MouseButton1Down:Connect(function()
moveToTop(self)
end)
elseif isA(obj,"TextBox") then
obj.Focused:Connect(function()
moveToTop(self)
end)
end
end
local createGui = function(self)
local gui = create({
{1,"ScreenGui",{Name="Window",}},
{2,"Frame",{Active=true,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="Main",Parent={1},Position=UDim2.new(0.40000000596046,0,0.40000000596046,0),Size=UDim2.new(0,300,0,300),}},
{3,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,Name="Content",Parent={2},Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,1,-20),ClipsDescendants=true}},
{4,"Frame",{BackgroundColor3=Color3.fromRGB(33,33,33),BorderSizePixel=0,Name="Line",Parent={3},Size=UDim2.new(1,0,0,1),}},
{5,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="TopBar",Parent={2},Size=UDim2.new(1,0,0,20),}},
{6,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={5},Position=UDim2.new(0,5,0,0),Size=UDim2.new(1,-10,0,20),Text="Window",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,}},
{7,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Close",Parent={5},Position=UDim2.new(1,-18,0,2),Size=UDim2.new(0,16,0,16),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
{8,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5054663650",Parent={7},Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,10,0,10),}},
{9,"UICorner",{CornerRadius=UDim.new(0,4),Parent={7},}},
{10,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Minimize",Parent={5},Position=UDim2.new(1,-36,0,2),Size=UDim2.new(0,16,0,16),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
{11,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5034768003",Parent={10},Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,10,0,10),}},
{12,"UICorner",{CornerRadius=UDim.new(0,4),Parent={10},}},
{13,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Image="rbxassetid://1427967925",Name="Outlines",Parent={2},Position=UDim2.new(0,-5,0,-5),ScaleType=1,Size=UDim2.new(1,10,1,10),SliceCenter=Rect.new(6,6,25,25),TileSize=UDim2.new(0,20,0,20),}},
{14,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="ResizeControls",Parent={2},Position=UDim2.new(0,-5,0,-5),Size=UDim2.new(1,10,1,10),}},
{15,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="North",Parent={14},Position=UDim2.new(0,5,0,0),Size=UDim2.new(1,-10,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{16,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="South",Parent={14},Position=UDim2.new(0,5,1,-5),Size=UDim2.new(1,-10,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{17,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="NorthEast",Parent={14},Position=UDim2.new(1,-5,0,0),Size=UDim2.new(0,5,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{18,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="East",Parent={14},Position=UDim2.new(1,-5,0,5),Size=UDim2.new(0,5,1,-10),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{19,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="West",Parent={14},Position=UDim2.new(0,0,0,5),Size=UDim2.new(0,5,1,-10),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{20,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="SouthEast",Parent={14},Position=UDim2.new(1,-5,1,-5),Size=UDim2.new(0,5,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{21,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="NorthWest",Parent={14},Size=UDim2.new(0,5,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{22,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.27450981736183,0.27450981736183,0.27450981736183),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="SouthWest",Parent={14},Position=UDim2.new(0,0,1,-5),Size=UDim2.new(0,5,0,5),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
})
local guiMain = gui.Main
local guiTopBar = guiMain.TopBar
local guiResizeControls = guiMain.ResizeControls
self.GuiElems.Main = guiMain
self.GuiElems.TopBar = guiMain.TopBar
self.GuiElems.Content = guiMain.Content
self.GuiElems.Line = guiMain.Content.Line
self.GuiElems.Outlines = guiMain.Outlines
self.GuiElems.Title = guiTopBar.Title
self.GuiElems.Close = guiTopBar.Close
self.GuiElems.Minimize = guiTopBar.Minimize
self.GuiElems.ResizeControls = guiResizeControls
self.ContentPane = guiMain.Content
guiTopBar.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 and self.Draggable then
local releaseEvent,mouseEvent
local maxX = sidesGui.AbsoluteSize.X
local initX = guiMain.AbsolutePosition.X
local initY = guiMain.AbsolutePosition.Y
local offX = mouse.X - initX
local offY = mouse.Y - initY
local alignInsertPos,alignInsertSide
guiDragging = true
releaseEvent = cloneref(game["Run Service"].Parent:GetService("UserInputService")).InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
releaseEvent:Disconnect()
mouseEvent:Disconnect()
guiDragging = false
alignIndicator.Parent = nil
if alignInsertSide then
local targetSide = (alignInsertSide == "left" and leftSide) or (alignInsertSide == "right" and rightSide)
self:AlignTo(targetSide,alignInsertPos)
end
end
end)
mouseEvent = cloneref(game["Run Service"].Parent:GetService("UserInputService")).InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and self.Draggable and not self.Closed then
if self.Aligned then
if leftSide.Resizing or rightSide.Resizing then return end
local posX,posY = input.Position.X-offX,input.Position.Y-offY
local delta = math.sqrt((posX-initX)^2 + (posY-initY)^2)
if delta >= 5 then
self:SetAligned(false)
end
else
local inputX,inputY = input.Position.X,input.Position.Y
local posX,posY = inputX-offX,inputY-offY
if posY < 0 then posY = 0 end
guiMain.Position = UDim2.new(0,posX,0,posY)
if self.Resizable and self.Alignable then
if inputX < 25 then
if sideHasRoom(leftSide,self.MinY or 100) then
local insertPos,range = getSideInsertPos(leftSide,inputY)
alignIndicator.Indicator.Position = UDim2.new(0,-15,0,range[1])
alignIndicator.Indicator.Size = UDim2.new(0,40,0,range[2]-range[1])
Lib.ShowGui(alignIndicator)
alignInsertPos = insertPos
alignInsertSide = "left"
return
end
elseif inputX >= maxX - 25 then
if sideHasRoom(rightSide,self.MinY or 100) then
local insertPos,range = getSideInsertPos(rightSide,inputY)
alignIndicator.Indicator.Position = UDim2.new(0,maxX-25,0,range[1])
alignIndicator.Indicator.Size = UDim2.new(0,40,0,range[2]-range[1])
Lib.ShowGui(alignIndicator)
alignInsertPos = insertPos
alignInsertSide = "right"
return
end
end
end
alignIndicator.Parent = nil
alignInsertPos = nil
alignInsertSide = nil
end
end
end)
end
end)
guiTopBar.Close.MouseButton1Click:Connect(function()
if self.Closed then return end
self:Close()
end)
guiTopBar.Minimize.MouseButton1Click:Connect(function()
if self.Closed then return end
if self.Aligned then
self:SetAligned(false)
else
self:SetMinimized()
end
end)
guiTopBar.Minimize.MouseButton2Click:Connect(function()
if self.Closed then return end
if not self.Aligned then
self:SetMinimized(nil,2)
guiTopBar.Minimize.BackgroundTransparency = 1
end
end)
guiMain.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 and not self.Aligned and not self.Closed then
moveToTop(self)
end
end)
guiMain:GetPropertyChangedSignal("AbsolutePosition"):Connect(function()
local absPos = guiMain.AbsolutePosition
self.PosX = absPos.X
self.PosY = absPos.Y
end)
resizeHook(self,guiResizeControls.North,"N")
resizeHook(self,guiResizeControls.NorthEast,"NE")
resizeHook(self,guiResizeControls.East,"E")
resizeHook(self,guiResizeControls.SouthEast,"SE")
resizeHook(self,guiResizeControls.South,"S")
resizeHook(self,guiResizeControls.SouthWest,"SW")
resizeHook(self,guiResizeControls.West,"W")
resizeHook(self,guiResizeControls.NorthWest,"NW")
guiMain.Size = UDim2.new(0,self.SizeX,0,self.SizeY)
gui.DescendantAdded:Connect(function(obj) focusInput(self,obj) end)
local descs = gui:GetDescendants()
for i = 1,#descs do
focusInput(self,descs[i])
end
self.MinimizeAnim = Lib.ButtonAnim(guiTopBar.Minimize)
self.CloseAnim = Lib.ButtonAnim(guiTopBar.Close)
return gui
end
local function updateSideFrames(noTween)
stopTweens()
leftSide.Frame.Size = UDim2.new(0,leftSide.Width,1,0)
rightSide.Frame.Size = UDim2.new(0,rightSide.Width,1,0)
leftSide.Frame.Resizer.Position = UDim2.new(0,leftSide.Width,0,0)
rightSide.Frame.Resizer.Position = UDim2.new(0,-5,0,0)
--leftSide.Frame.Visible = (#leftSide.Windows > 0)
--rightSide.Frame.Visible = (#rightSide.Windows > 0)
--[[if #leftSide.Windows > 0 and leftSide.Frame.Position == UDim2.new(0,-leftSide.Width-5,0,0) then
leftSide.Frame:TweenPosition(UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)
elseif #leftSide.Windows == 0 and leftSide.Frame.Position == UDim2.new(0,0,0,0) then
leftSide.Frame:TweenPosition(UDim2.new(0,-leftSide.Width-5,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)
end
local rightTweenPos = (#rightSide.Windows == 0 and UDim2.new(1,5,0,0) or UDim2.new(1,-rightSide.Width,0,0))
rightSide.Frame:TweenPosition(rightTweenPos,Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)]]
local leftHidden = #leftSide.Windows == 0 or leftSide.Hidden
local rightHidden = #rightSide.Windows == 0 or rightSide.Hidden
local leftPos = (leftHidden and UDim2.new(0,-leftSide.Width-10,0,0) or UDim2.new(0,0,0,0))
local rightPos = (rightHidden and UDim2.new(1,10,0,0) or UDim2.new(1,-rightSide.Width,0,0))
sidesGui.LeftToggle.Text = leftHidden and ">" or "<"
sidesGui.RightToggle.Text = rightHidden and "<" or ">"
if not noTween then
local function insertTween(...)
local tween = service.TweenService:Create(...)
tweens[#tweens+1] = tween
tween:Play()
end
insertTween(leftSide.Frame,sideTweenInfo,{Position = leftPos})
insertTween(rightSide.Frame,sideTweenInfo,{Position = rightPos})
insertTween(sidesGui.LeftToggle,sideTweenInfo,{Position = UDim2.new(0,#leftSide.Windows == 0 and -16 or 0,0,-36)})
insertTween(sidesGui.RightToggle,sideTweenInfo,{Position = UDim2.new(1,#rightSide.Windows == 0 and 0 or -16,0,-36)})
else
leftSide.Frame.Position = leftPos
rightSide.Frame.Position = rightPos
sidesGui.LeftToggle.Position = UDim2.new(0,#leftSide.Windows == 0 and -16 or 0,0,-36)
sidesGui.RightToggle.Position = UDim2.new(1,#rightSide.Windows == 0 and 0 or -16,0,-36)
end
end
local function getSideFramePos(side)
local leftHidden = #leftSide.Windows == 0 or leftSide.Hidden
local rightHidden = #rightSide.Windows == 0 or rightSide.Hidden
if side == leftSide then
return (leftHidden and UDim2.new(0,-leftSide.Width-10,0,0) or UDim2.new(0,0,0,0))
else
return (rightHidden and UDim2.new(1,10,0,0) or UDim2.new(1,-rightSide.Width,0,0))
end
end
local function sideResized(side)
local currentPos = 0
local sideFramePos = getSideFramePos(side)
for i,v in pairs(side.Windows) do
v.SizeX = side.Width
v.GuiElems.Main.Size = UDim2.new(0,side.Width,0,v.SizeY)
v.GuiElems.Main.Position = UDim2.new(sideFramePos.X.Scale,sideFramePos.X.Offset,0,currentPos)
currentPos = currentPos + v.SizeY+4
end
end
local function sideResizerHook(resizer,dir,side,pos)
local mouse = Main.Mouse
local windows = side.Windows
resizer.InputBegan:Connect(function(input)
if not side.Resizing then
if input.UserInputType == Enum.UserInputType.MouseMovement then
resizer.BackgroundColor3 = theme.MainColor2
elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
local releaseEvent,mouseEvent
local offX = mouse.X - resizer.AbsolutePosition.X
local offY = mouse.Y - resizer.AbsolutePosition.Y
side.Resizing = resizer
resizer.BackgroundColor3 = theme.MainColor2
releaseEvent = service.UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
releaseEvent:Disconnect()
mouseEvent:Disconnect()
side.Resizing = false
resizer.BackgroundColor3 = theme.Button
end
end)
mouseEvent = service.UserInputService.InputChanged:Connect(function(input)
if not resizer.Parent then
releaseEvent:Disconnect()
mouseEvent:Disconnect()
side.Resizing = false
return
end
if input.UserInputType == Enum.UserInputType.MouseMovement then
if dir == "V" then
local delta = input.Position.Y - resizer.AbsolutePosition.Y - offY
if delta > 0 then
local neededSize = delta
for i = pos+1,#windows do
local window = windows[i]
local newSize = math.max(window.SizeY-neededSize,(window.MinY or 100))
neededSize = neededSize - (window.SizeY - newSize)
window.SizeY = newSize
end
windows[pos].SizeY = windows[pos].SizeY + math.max(0,delta-neededSize)
else
local neededSize = -delta
for i = pos,1,-1 do
local window = windows[i]
local newSize = math.max(window.SizeY-neededSize,(window.MinY or 100))
neededSize = neededSize - (window.SizeY - newSize)
window.SizeY = newSize
end
windows[pos+1].SizeY = windows[pos+1].SizeY + math.max(0,-delta-neededSize)
end
updateSideFrames()
sideResized(side)
elseif dir == "H" then
local maxWidth = math.max(300,sidesGui.AbsoluteSize.X-static.FreeWidth)
local otherSide = (side == leftSide and rightSide or leftSide)
local delta = input.Position.X - resizer.AbsolutePosition.X - offX
delta = (side == leftSide and delta or -delta)
local proposedSize = math.max(static.MinWidth,side.Width + delta)
if proposedSize + otherSide.Width <= maxWidth then
side.Width = proposedSize
else
local newOtherSize = maxWidth - proposedSize
if newOtherSize >= static.MinWidth then
side.Width = proposedSize
otherSide.Width = newOtherSize
else
side.Width = maxWidth - static.MinWidth
otherSide.Width = static.MinWidth
end
end
updateSideFrames(true)
sideResized(side)
sideResized(otherSide)
end
end
end)
end
end
end)
resizer.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and side.Resizing ~= resizer then
resizer.BackgroundColor3 = theme.Button
end
end)
end
local function renderSide(side,noTween) -- TODO: Use existing resizers
local currentPos = 0
local sideFramePos = getSideFramePos(side)
local template = side.WindowResizer:Clone()
for i,v in pairs(side.ResizeCons) do v:Disconnect() end
for i,v in pairs(side.Frame:GetChildren()) do if v.Name == "WindowResizer" then v:Destroy() end end
side.ResizeCons = {}
side.Resizing = nil
for i,v in pairs(side.Windows) do
v.SidePos = i
local isEnd = i == #side.Windows
local size = UDim2.new(0,side.Width,0,v.SizeY)
local pos = UDim2.new(sideFramePos.X.Scale,sideFramePos.X.Offset,0,currentPos)
Lib.ShowGui(v.Gui)
--v.GuiElems.Main:TweenSizeAndPosition(size,pos,Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)
if noTween then
v.GuiElems.Main.Size = size
v.GuiElems.Main.Position = pos
else
local tween = service.TweenService:Create(v.GuiElems.Main,sideTweenInfo,{Size = size, Position = pos})
tweens[#tweens+1] = tween
tween:Play()
end
currentPos = currentPos + v.SizeY+4
if not isEnd then
local newTemplate = template:Clone()
newTemplate.Position = UDim2.new(1,-side.Width,0,currentPos-4)
side.ResizeCons[#side.ResizeCons+1] = v.Gui.Main:GetPropertyChangedSignal("Size"):Connect(function()
newTemplate.Position = UDim2.new(1,-side.Width,0, v.GuiElems.Main.Position.Y.Offset + v.GuiElems.Main.Size.Y.Offset)
end)
side.ResizeCons[#side.ResizeCons+1] = v.Gui.Main:GetPropertyChangedSignal("Position"):Connect(function()
newTemplate.Position = UDim2.new(1,-side.Width,0, v.GuiElems.Main.Position.Y.Offset + v.GuiElems.Main.Size.Y.Offset)
end)
sideResizerHook(newTemplate,"V",side,i)
newTemplate.Parent = side.Frame
end
end
--side.Frame.Back.Position = UDim2.new(0,0,0,0)
--side.Frame.Back.Size = UDim2.new(0,side.Width,1,0)
end
local function updateSide(side,noTween)
local oldHeight = 0
local currentPos = 0
local neededSize = 0
local windows = side.Windows
local height = sidesGui.AbsoluteSize.Y - (math.max(0,#windows - 1) * 4)
for i,v in pairs(windows) do oldHeight = oldHeight + v.SizeY end
for i,v in pairs(windows) do
if i == #windows then
v.SizeY = height-currentPos
neededSize = math.max(0,(v.MinY or 100)-v.SizeY)
else
v.SizeY = math.max(math.floor(v.SizeY/oldHeight*height),v.MinY or 100)
end
currentPos = currentPos + v.SizeY
end
if neededSize > 0 then
for i = #windows-1,1,-1 do
local window = windows[i]
local newSize = math.max(window.SizeY-neededSize,(window.MinY or 100))
neededSize = neededSize - (window.SizeY - newSize)
window.SizeY = newSize
end
local lastWindow = windows[#windows]
lastWindow.SizeY = (lastWindow.MinY or 100)-neededSize
end
renderSide(side,noTween)
end
updateWindows = function(noTween)
updateSideFrames(noTween)
updateSide(leftSide,noTween)
updateSide(rightSide,noTween)
local count = 0
for i = #visibleWindows,1,-1 do
visibleWindows[i].Gui.DisplayOrder = displayOrderStart + count
Lib.ShowGui(visibleWindows[i].Gui)
count = count + 1
end
--[[local leftTweenPos = (#leftSide.Windows == 0 and UDim2.new(0,-leftSide.Width-5,0,0) or UDim2.new(0,0,0,0))
leftSide.Frame:TweenPosition(leftTweenPos,Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)
local rightTweenPos = (#rightSide.Windows == 0 and UDim2.new(1,5,0,0) or UDim2.new(1,-rightSide.Width,0,0))
rightSide.Frame:TweenPosition(rightTweenPos,Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.3,true)]]
end
funcs.SetMinimized = function(self,set,mode)
local oldVal = self.Minimized
local newVal
if set == nil then newVal = not self.Minimized else newVal = set end
self.Minimized = newVal
if not mode then mode = 1 end
local resizeControls = self.GuiElems.ResizeControls
local minimizeControls = {"North","NorthEast","NorthWest","South","SouthEast","SouthWest"}
for i = 1,#minimizeControls do
local control = resizeControls:FindFirstChild(minimizeControls[i])
if control then control.Visible = not newVal end
end
if mode == 1 or mode == 2 then
self:StopTweens()
if mode == 1 then
self.GuiElems.Main:TweenSize(UDim2.new(0,self.SizeX,0,newVal and 20 or self.SizeY),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.25,true)
else
local maxY = sidesGui.AbsoluteSize.Y
local newPos = UDim2.new(0,self.PosX,0,newVal and math.min(maxY-20,self.PosY + self.SizeY - 20) or math.max(0,self.PosY - self.SizeY + 20))
self.GuiElems.Main:TweenPosition(newPos,Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.25,true)
self.GuiElems.Main:TweenSize(UDim2.new(0,self.SizeX,0,newVal and 20 or self.SizeY),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.25,true)
end
self.GuiElems.Minimize.ImageLabel.Image = newVal and "rbxassetid://5060023708" or "rbxassetid://5034768003"
end
if oldVal ~= newVal then
if newVal then
self.OnMinimize:Fire()
else
self.OnRestore:Fire()
end
end
end
funcs.Resize = function(self,sizeX,sizeY)
self.SizeX = sizeX or self.SizeX
self.SizeY = sizeY or self.SizeY
self.GuiElems.Main.Size = UDim2.new(0,self.SizeX,0,self.SizeY)
end
funcs.SetSize = funcs.Resize
funcs.SetTitle = function(self,title)
self.GuiElems.Title.Text = title
end
funcs.SetResizable = function(self,val)
self.Resizable = val
self.GuiElems.ResizeControls.Visible = self.Resizable and self.ResizableInternal
end
funcs.SetResizableInternal = function(self,val)
self.ResizableInternal = val
self.GuiElems.ResizeControls.Visible = self.Resizable and self.ResizableInternal
end
funcs.SetAligned = function(self,val)
self.Aligned = val
self:SetResizableInternal(not val)
self.GuiElems.Main.Active = not val
self.GuiElems.Main.Outlines.Visible = not val
if not val then
for i,v in pairs(leftSide.Windows) do if v == self then table.remove(leftSide.Windows,i) break end end
for i,v in pairs(rightSide.Windows) do if v == self then table.remove(rightSide.Windows,i) break end end
if not table.find(visibleWindows,self) then table.insert(visibleWindows,1,self) end
self.GuiElems.Minimize.ImageLabel.Image = "rbxassetid://5034768003"
self.Side = nil
updateWindows()
else
self:SetMinimized(false,3)
for i,v in pairs(visibleWindows) do if v == self then table.remove(visibleWindows,i) break end end
self.GuiElems.Minimize.ImageLabel.Image = "rbxassetid://5448127505"
end
end
funcs.Add = function(self,obj,name)
if type(obj) == "table" and obj.Gui and obj.Gui:IsA("GuiObject") then
obj.Gui.Parent = self.ContentPane
else
obj.Parent = self.ContentPane
end
if name then self.Elements[name] = obj end
end
funcs.GetElement = function(self,obj,name)
return self.Elements[name]
end
funcs.AlignTo = function(self,side,pos,size,silent)
if table.find(side.Windows,self) or self.Closed then return end
size = size or self.SizeY
if size > 0 and size <= 1 then
local totalSideHeight = 0
for i,v in pairs(side.Windows) do totalSideHeight = totalSideHeight + v.SizeY end
self.SizeY = (totalSideHeight > 0 and totalSideHeight * size * 2) or size
else
self.SizeY = (size > 0 and size or 100)
end
self:SetAligned(true)
self.Side = side
self.SizeX = side.Width
self.Gui.DisplayOrder = sideDisplayOrder + 1
for i,v in pairs(side.Windows) do v.Gui.DisplayOrder = sideDisplayOrder end
pos = math.min(#side.Windows+1, pos or 1)
self.SidePos = pos
table.insert(side.Windows, pos, self)
if not silent then
side.Hidden = false
end
-- updateWindows(silent)
end
funcs.Close = function(self)
self.Closed = true
self:SetResizableInternal(false)
Lib.FindAndRemove(leftSide.Windows,self)
Lib.FindAndRemove(rightSide.Windows,self)
Lib.FindAndRemove(visibleWindows,self)
self.MinimizeAnim.Disable()
self.CloseAnim.Disable()
self.ClosedSide = self.Side
self.Side = nil
self.OnDeactivate:Fire()
if not self.Aligned then
self:StopTweens()
local ti = TweenInfo.new(0.2,Enum.EasingStyle.Quad,Enum.EasingDirection.Out)
local closeTime = tick()
self.LastClose = closeTime
self:DoTween(self.GuiElems.Main,ti,{Size = UDim2.new(0,self.SizeX,0,20)})
self:DoTween(self.GuiElems.Title,ti,{TextTransparency = 1})
self:DoTween(self.GuiElems.Minimize.ImageLabel,ti,{ImageTransparency = 1})
self:DoTween(self.GuiElems.Close.ImageLabel,ti,{ImageTransparency = 1})
Lib.FastWait(0.2)
if closeTime ~= self.LastClose then return end
self:DoTween(self.GuiElems.TopBar,ti,{BackgroundTransparency = 1})
self:DoTween(self.GuiElems.Outlines,ti,{ImageTransparency = 1})
Lib.FastWait(0.2)
if closeTime ~= self.LastClose then return end
end
self.Aligned = false
self.Gui.Parent = nil
updateWindows(true)
end
funcs.Hide = funcs.Close
funcs.IsVisible = function(self)
return not self.Closed and ((self.Side and not self.Side.Hidden) or not self.Side)
end
funcs.IsContentVisible = function(self)
return self:IsVisible() and not self.Minimized
end
funcs.Focus = function(self)
moveToTop(self)
end
funcs.MoveInBoundary = function(self)
local posX,posY = self.PosX,self.PosY
local maxX,maxY = sidesGui.AbsoluteSize.X,sidesGui.AbsoluteSize.Y
posX = math.min(posX,maxX-self.SizeX)
posY = math.min(posY,maxY-20)
self.GuiElems.Main.Position = UDim2.new(0,posX,0,posY)
end
funcs.DoTween = function(self,...)
local tween = service.TweenService:Create(...)
self.Tweens[#self.Tweens+1] = tween
tween:Play()
end
funcs.StopTweens = function(self)
for i,v in pairs(self.Tweens) do
v:Cancel()
end
self.Tweens = {}
end
funcs.Show = function(self,data)
return static.ShowWindow(self,data)
end
funcs.ShowAndFocus = function(self,data)
static.ShowWindow(self,data)
service.RunService.RenderStepped:wait()
self:Focus()
end
static.ShowWindow = function(window,data)
data = data or {}
local align = data.Align
local pos = data.Pos
local size = data.Size
local targetSide = (align == "left" and leftSide) or (align == "right" and rightSide)
if not window.Closed then
if not window.Aligned then
window:SetMinimized(false)
elseif window.Side and not data.Silent then
static.SetSideVisible(window.Side,true)
end
return
end
window.Closed = false
window.LastClose = tick()
window.GuiElems.Title.TextTransparency = 0
window.GuiElems.Minimize.ImageLabel.ImageTransparency = 0
window.GuiElems.Close.ImageLabel.ImageTransparency = 0
window.GuiElems.TopBar.BackgroundTransparency = 0
window.GuiElems.Outlines.ImageTransparency = 0
window.GuiElems.Minimize.ImageLabel.Image = "rbxassetid://5034768003"
window.GuiElems.Main.Active = true
window.GuiElems.Main.Outlines.Visible = true
window:SetMinimized(false,3)
window:SetResizableInternal(true)
window.MinimizeAnim.Enable()
window.CloseAnim.Enable()
if align then
window:AlignTo(targetSide,pos,size,data.Silent)
else
if align == nil and window.ClosedSide then -- Regular open
window:AlignTo(window.ClosedSide,window.SidePos,size,true)
static.SetSideVisible(window.ClosedSide,true)
else
if table.find(visibleWindows,window) then return end
-- TODO: make better
window.GuiElems.Main.Size = UDim2.new(0,window.SizeX,0,20)
local ti = TweenInfo.new(0.2,Enum.EasingStyle.Quad,Enum.EasingDirection.Out)
window:StopTweens()
window:DoTween(window.GuiElems.Main,ti,{Size = UDim2.new(0,window.SizeX,0,window.SizeY)})
window.SizeY = size or window.SizeY
table.insert(visibleWindows,1,window)
updateWindows()
end
end
window.ClosedSide = nil
window.OnActivate:Fire()
end
static.ToggleSide = function(name)
local side = (name == "left" and leftSide or rightSide)
side.Hidden = not side.Hidden
for i,v in pairs(side.Windows) do
if side.Hidden then
v.OnDeactivate:Fire()
else
v.OnActivate:Fire()
end
end
updateWindows()
end
static.SetSideVisible = function(s,vis)
local side = (type(s) == "table" and s) or (s == "left" and leftSide or rightSide)
side.Hidden = not vis
for i,v in pairs(side.Windows) do
if side.Hidden then
v.OnDeactivate:Fire()
else
v.OnActivate:Fire()
end
end
updateWindows()
end
static.Init = function()
displayOrderStart = Main.DisplayOrders.Window
sideDisplayOrder = Main.DisplayOrders.SideWindow
sidesGui = Instance.new("ScreenGui")
local leftFrame = create({
{1,"Frame",{Active=true,Name="LeftSide",BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,}},
{2,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2549019753933,0.2549019753933,0.2549019753933),BorderSizePixel=0,Font=3,Name="Resizer",Parent={1},Size=UDim2.new(0,5,1,0),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{3,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="Line",Parent={2},Position=UDim2.new(0,0,0,0),Size=UDim2.new(0,1,1,0),}},
{4,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2549019753933,0.2549019753933,0.2549019753933),BorderSizePixel=0,Font=3,Name="WindowResizer",Parent={1},Position=UDim2.new(1,-300,0,0),Size=UDim2.new(1,0,0,4),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{5,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="Line",Parent={4},Size=UDim2.new(1,0,0,1),}},
})
leftSide.Frame = leftFrame
leftFrame.Position = UDim2.new(0,-leftSide.Width-10,0,0)
leftSide.WindowResizer = leftFrame.WindowResizer
leftFrame.WindowResizer.Parent = nil
leftFrame.Parent = sidesGui
local rightFrame = create({
{1,"Frame",{Active=true,Name="RightSide",BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,}},
{2,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2549019753933,0.2549019753933,0.2549019753933),BorderSizePixel=0,Font=3,Name="Resizer",Parent={1},Size=UDim2.new(0,5,1,0),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{3,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="Line",Parent={2},Position=UDim2.new(0,4,0,0),Size=UDim2.new(0,1,1,0),}},
{4,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2549019753933,0.2549019753933,0.2549019753933),BorderSizePixel=0,Font=3,Name="WindowResizer",Parent={1},Position=UDim2.new(1,-300,0,0),Size=UDim2.new(1,0,0,4),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{5,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="Line",Parent={4},Size=UDim2.new(1,0,0,1),}},
})
rightSide.Frame = rightFrame
rightFrame.Position = UDim2.new(1,10,0,0)
rightSide.WindowResizer = rightFrame.WindowResizer
rightFrame.WindowResizer.Parent = nil
rightFrame.Parent = sidesGui
sideResizerHook(leftFrame.Resizer,"H",leftSide)
sideResizerHook(rightFrame.Resizer,"H",rightSide)
alignIndicator = Instance.new("ScreenGui")
alignIndicator.DisplayOrder = Main.DisplayOrders.Core
local indicator = Instance.new("Frame",alignIndicator)
indicator.BackgroundColor3 = Color3.fromRGB(0, 170, 255)
indicator.BorderSizePixel = 0
indicator.BackgroundTransparency = 0.8
indicator.Name = "Indicator"
local corner = Instance.new("UICorner",indicator)
corner.CornerRadius = UDim.new(0,10)
local leftToggle = create({{1,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderMode=2,Font=10,Name="LeftToggle",Position=UDim2.new(0,0,0,-36),Size=UDim2.new(0,16,0,36),Text="<",TextColor3=Color3.new(1,1,1),TextSize=14,}}})
local rightToggle = leftToggle:Clone()
rightToggle.Name = "RightToggle"
rightToggle.Position = UDim2.new(1,-16,0,-36)
Lib.ButtonAnim(leftToggle,{Mode = 2,PressColor = Color3.fromRGB(32,32,32)})
Lib.ButtonAnim(rightToggle,{Mode = 2,PressColor = Color3.fromRGB(32,32,32)})
leftToggle.MouseButton1Click:Connect(function()
static.ToggleSide("left")
end)
rightToggle.MouseButton1Click:Connect(function()
static.ToggleSide("right")
end)
leftToggle.Parent = sidesGui
rightToggle.Parent = sidesGui
sidesGui:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
local maxWidth = math.max(300,sidesGui.AbsoluteSize.X-static.FreeWidth)
leftSide.Width = math.max(static.MinWidth,math.min(leftSide.Width,maxWidth-rightSide.Width))
rightSide.Width = math.max(static.MinWidth,math.min(rightSide.Width,maxWidth-leftSide.Width))
for i = 1,#visibleWindows do
visibleWindows[i]:MoveInBoundary()
end
updateWindows(true)
end)
sidesGui.DisplayOrder = sideDisplayOrder - 1
Lib.ShowGui(sidesGui)
updateSideFrames()
end
local mt = {__index = funcs}
static.new = function()
local obj = setmetatable({
Minimized = false,
Dragging = false,
Resizing = false,
Aligned = false,
Draggable = true,
Resizable = true,
ResizableInternal = true,
Alignable = true,
Closed = true,
SizeX = 300,
SizeY = 300,
MinX = 200,
MinY = 200,
PosX = 0,
PosY = 0,
GuiElems = {},
Tweens = {},
Elements = {},
OnActivate = Lib.Signal.new(),
OnDeactivate = Lib.Signal.new(),
OnMinimize = Lib.Signal.new(),
OnRestore = Lib.Signal.new()
},mt)
obj.Gui = createGui(obj)
return obj
end
return static
end)()
Lib.ContextMenu = (function()
local funcs = {}
local mouse
local function createGui(self)
local contextGui = create({
{1,"ScreenGui",{DisplayOrder=1000000,Name="Context",ZIndexBehavior=1,}},
{2,"Frame",{Active=true,BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),Name="Main",Parent={1},Position=UDim2.new(0.5,-100,0.5,-150),Size=UDim2.new(0,200,0,100),}},
{3,"UICorner",{CornerRadius=UDim.new(0,4),Parent={2},}},
{4,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),Name="Container",Parent={2},Position=UDim2.new(0,1,0,1),Size=UDim2.new(1,-2,1,-2),}},
{5,"UICorner",{CornerRadius=UDim.new(0,4),Parent={4},}},
{6,"ScrollingFrame",{Active=true,BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BackgroundTransparency=1,BorderSizePixel=0,CanvasSize=UDim2.new(0,0,0,0),Name="List",Parent={4},Position=UDim2.new(0,2,0,2),ScrollBarImageColor3=Color3.new(0,0,0),ScrollBarThickness=4,Size=UDim2.new(1,-4,1,-4),VerticalScrollBarInset=1,}},
{7,"UIListLayout",{Parent={6},SortOrder=2,}},
{8,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="SearchFrame",Parent={4},Size=UDim2.new(1,0,0,24),Visible=false,}},
{9,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.1176470592618,0.1176470592618,0.1176470592618),BorderSizePixel=0,Name="SearchContainer",Parent={8},Position=UDim2.new(0,3,0,3),Size=UDim2.new(1,-6,0,18),}},
{10,"TextBox",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="SearchBox",Parent={9},PlaceholderColor3=Color3.new(0.39215689897537,0.39215689897537,0.39215689897537),PlaceholderText="Search",Position=UDim2.new(0,4,0,0),Size=UDim2.new(1,-8,0,18),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=0,}},
{11,"UICorner",{CornerRadius=UDim.new(0,2),Parent={9},}},
{12,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="Line",Parent={8},Position=UDim2.new(0,0,1,0),Size=UDim2.new(1,0,0,1),}},
{13,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BackgroundTransparency=1,BorderColor3=Color3.new(0.33725491166115,0.49019610881805,0.73725491762161),BorderSizePixel=0,Font=3,Name="Entry",Parent={1},Size=UDim2.new(1,0,0,22),Text="",TextSize=14,Visible=false,}},
{14,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="EntryName",Parent={13},Position=UDim2.new(0,24,0,0),Size=UDim2.new(1,-24,1,0),Text="Duplicate",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{15,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Shortcut",Parent={13},Position=UDim2.new(0,24,0,0),Size=UDim2.new(1,-30,1,0),Text="Ctrl+D",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{16,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,ImageRectOffset=Vector2.new(304,0),ImageRectSize=Vector2.new(16,16),Name="Icon",Parent={13},Position=UDim2.new(0,2,0,3),ScaleType=4,Size=UDim2.new(0,16,0,16),}},
{17,"UICorner",{CornerRadius=UDim.new(0,4),Parent={13},}},
{18,"Frame",{BackgroundColor3=Color3.new(0.21568629145622,0.21568629145622,0.21568629145622),BackgroundTransparency=1,BorderSizePixel=0,Name="Divider",Parent={1},Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,0,7),Visible=false,}},
{19,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="Line",Parent={18},Position=UDim2.new(0,0,0.5,0),Size=UDim2.new(1,0,0,1),}},
{20,"TextLabel",{AnchorPoint=Vector2.new(0,0.5),BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="DividerName",Parent={18},Position=UDim2.new(0,2,0.5,0),Size=UDim2.new(1,-4,1,0),Text="Objects",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.60000002384186,TextXAlignment=0,Visible=false,}},
})
self.GuiElems.Main = contextGui.Main
self.GuiElems.List = contextGui.Main.Container.List
self.GuiElems.Entry = contextGui.Entry
self.GuiElems.Divider = contextGui.Divider
self.GuiElems.SearchFrame = contextGui.Main.Container.SearchFrame
self.GuiElems.SearchBar = self.GuiElems.SearchFrame.SearchContainer.SearchBox
Lib.ViewportTextBox.convert(self.GuiElems.SearchBar)
self.GuiElems.SearchBar:GetPropertyChangedSignal("Text"):Connect(function()
local lower,find = string.lower,string.find
local searchText = lower(self.GuiElems.SearchBar.Text)
local items = self.Items
local map = self.ItemToEntryMap
if searchText ~= "" then
local results = {}
local count = 1
for i = 1,#items do
local item = items[i]
local entry = map[item]
if entry then
if not item.Divider and find(lower(item.Name),searchText,1,true) then
results[count] = item
count = count + 1
else
entry.Visible = false
end
end
end
table.sort(results,function(a,b) return a.Name < b.Name end)
for i = 1,#results do
local entry = map[results[i]]
entry.LayoutOrder = i
entry.Visible = true
end
else
for i = 1,#items do
local entry = map[items[i]]
if entry then entry.LayoutOrder = i entry.Visible = true end
end
end
local toSize = self.GuiElems.List.UIListLayout.AbsoluteContentSize.Y + 6
self.GuiElems.List.CanvasSize = UDim2.new(0,0,0,toSize-6)
end)
return contextGui
end
funcs.Add = function(self,item)
local newItem = {
Name = item.Name or "Item",
Icon = item.Icon or "",
Shortcut = item.Shortcut or "",
OnClick = item.OnClick,
OnHover = item.OnHover,
Disabled = item.Disabled or false,
DisabledIcon = item.DisabledIcon or "",
IconMap = item.IconMap,
OnRightClick = item.OnRightClick
}
if self.QueuedDivider then
local text = self.QueuedDividerText and #self.QueuedDividerText > 0 and self.QueuedDividerText
self:AddDivider(text)
end
self.Items[#self.Items+1] = newItem
self.Updated = nil
end
funcs.AddRegistered = function(self,name,disabled)
if not self.Registered[name] then error(name.." is not registered") end
if self.QueuedDivider then
local text = self.QueuedDividerText and #self.QueuedDividerText > 0 and self.QueuedDividerText
self:AddDivider(text)
end
self.Registered[name].Disabled = disabled
self.Items[#self.Items+1] = self.Registered[name]
self.Updated = nil
end
funcs.Register = function(self,name,item)
self.Registered[name] = {
Name = item.Name or "Item",
Icon = item.Icon or "",
Shortcut = item.Shortcut or "",
OnClick = item.OnClick,
OnHover = item.OnHover,
DisabledIcon = item.DisabledIcon or "",
IconMap = item.IconMap,
OnRightClick = item.OnRightClick
}
end
funcs.UnRegister = function(self,name)
self.Registered[name] = nil
end
funcs.AddDivider = function(self,text)
self.QueuedDivider = false
local textWidth = text and service.TextService:GetTextSize(text,14,Enum.Font.SourceSans,Vector2.new(999999999,20)).X or nil
table.insert(self.Items,{Divider = true, Text = text, TextSize = textWidth and textWidth+4})
self.Updated = nil
end
funcs.QueueDivider = function(self,text)
self.QueuedDivider = true
self.QueuedDividerText = text or ""
end
funcs.Clear = function(self)
self.Items = {}
self.Updated = nil
end
funcs.Refresh = function(self)
for i,v in pairs(self.GuiElems.List:GetChildren()) do
if not v:IsA("UIListLayout") then
v:Destroy()
end
end
local map = {}
self.ItemToEntryMap = map
local dividerFrame = self.GuiElems.Divider
local contextList = self.GuiElems.List
local entryFrame = self.GuiElems.Entry
local items = self.Items
for i = 1,#items do
local item = items[i]
if item.Divider then
local newDivider = dividerFrame:Clone()
newDivider.Line.BackgroundColor3 = self.Theme.DividerColor
if item.Text then
newDivider.Size = UDim2.new(1,0,0,20)
newDivider.Line.Position = UDim2.new(0,item.TextSize,0.5,0)
newDivider.Line.Size = UDim2.new(1,-item.TextSize,0,1)
newDivider.DividerName.TextColor3 = self.Theme.TextColor
newDivider.DividerName.Text = item.Text
newDivider.DividerName.Visible = true
end
newDivider.Visible = true
map[item] = newDivider
newDivider.Parent = contextList
else
local newEntry = entryFrame:Clone()
newEntry.BackgroundColor3 = self.Theme.HighlightColor
newEntry.EntryName.TextColor3 = self.Theme.TextColor
newEntry.EntryName.Text = item.Name
newEntry.Shortcut.Text = item.Shortcut
if item.Disabled then
newEntry.EntryName.TextColor3 = Color3.new(150/255,150/255,150/255)
newEntry.Shortcut.TextColor3 = Color3.new(150/255,150/255,150/255)
end
if self.Iconless then
newEntry.EntryName.Position = UDim2.new(0,2,0,0)
newEntry.EntryName.Size = UDim2.new(1,-4,0,20)
newEntry.Icon.Visible = false
else
local iconIndex = item.Disabled and item.DisabledIcon or item.Icon
if item.IconMap then
if type(iconIndex) == "number" then
item.IconMap:Display(newEntry.Icon,iconIndex)
elseif type(iconIndex) == "string" then
item.IconMap:DisplayByKey(newEntry.Icon,iconIndex)
end
elseif type(iconIndex) == "string" then
newEntry.Icon.Image = iconIndex
end
end
if not item.Disabled then
if item.OnClick then
newEntry.MouseButton1Click:Connect(function()
item.OnClick(item.Name)
if not item.NoHide then
self:Hide()
end
end)
end
if item.OnRightClick then
newEntry.MouseButton2Click:Connect(function()
item.OnRightClick(item.Name)
if not item.NoHide then
self:Hide()
end
end)
end
end
newEntry.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
newEntry.BackgroundTransparency = 0
end
end)
newEntry.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
newEntry.BackgroundTransparency = 1
end
end)
newEntry.Visible = true
map[item] = newEntry
newEntry.Parent = contextList
end
end
self.Updated = true
end
funcs.Show = function(self,x,y)
-- Initialize Gui
local elems = self.GuiElems
elems.SearchFrame.Visible = self.SearchEnabled
elems.List.Position = UDim2.new(0,2,0,2 + (self.SearchEnabled and 24 or 0))
elems.List.Size = UDim2.new(1,-4,1,-4 - (self.SearchEnabled and 24 or 0))
if self.SearchEnabled and self.ClearSearchOnShow then elems.SearchBar.Text = "" end
self.GuiElems.List.CanvasPosition = Vector2.new(0,0)
if not self.Updated then
self:Refresh() -- Create entries
end
-- Vars
local reverseY = false
local x,y = x or mouse.X, y or mouse.Y
local maxX,maxY = mouse.ViewSizeX,mouse.ViewSizeY
-- Position and show
if x + self.Width > maxX then
x = self.ReverseX and x - self.Width or maxX - self.Width
end
elems.Main.Position = UDim2.new(0,x,0,y)
elems.Main.Size = UDim2.new(0,self.Width,0,0)
self.Gui.DisplayOrder = Main.DisplayOrders.Menu
Lib.ShowGui(self.Gui)
-- Size adjustment
local toSize = elems.List.UIListLayout.AbsoluteContentSize.Y + 6 -- Padding
if self.MaxHeight and toSize > self.MaxHeight then
elems.List.CanvasSize = UDim2.new(0,0,0,toSize-6)
toSize = self.MaxHeight
else
elems.List.CanvasSize = UDim2.new(0,0,0,0)
end
if y + toSize > maxY then reverseY = true end
-- Close event
local closable
if self.CloseEvent then self.CloseEvent:Disconnect() end
self.CloseEvent = service.UserInputService.InputBegan:Connect(function(input)
if not closable or input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
if not Lib.CheckMouseInGui(elems.Main) then
self.CloseEvent:Disconnect()
self:Hide()
end
end)
-- Resize
if reverseY then
elems.Main.Position = UDim2.new(0,x,0,y-(self.ReverseYOffset or 0))
local newY = y - toSize - (self.ReverseYOffset or 0)
y = newY >= 0 and newY or 0
elems.Main:TweenSizeAndPosition(UDim2.new(0,self.Width,0,toSize),UDim2.new(0,x,0,y),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.2,true)
else
elems.Main:TweenSize(UDim2.new(0,self.Width,0,toSize),Enum.EasingDirection.Out,Enum.EasingStyle.Quart,0.2,true)
end
-- Close debounce
Lib.FastWait()
if self.SearchEnabled and self.FocusSearchOnShow then elems.SearchBar:CaptureFocus() end
closable = true
end
funcs.Hide = function(self)
self.Gui.Parent = nil
end
funcs.ApplyTheme = function(self,data)
local theme = self.Theme
theme.ContentColor = data.ContentColor or Settings.Theme.Menu
theme.OutlineColor = data.OutlineColor or Settings.Theme.Menu
theme.DividerColor = data.DividerColor or Settings.Theme.Outline2
theme.TextColor = data.TextColor or Settings.Theme.Text
theme.HighlightColor = data.HighlightColor or Settings.Theme.Main1
self.GuiElems.Main.BackgroundColor3 = theme.OutlineColor
self.GuiElems.Main.Container.BackgroundColor3 = theme.ContentColor
end
local mt = {__index = funcs}
local function new()
if not mouse then mouse = Main.Mouse or service.Players.LocalPlayer:GetMouse() end
local obj = setmetatable({
Width = 200,
MaxHeight = nil,
Iconless = false,
SearchEnabled = false,
ClearSearchOnShow = true,
FocusSearchOnShow = true,
Updated = false,
QueuedDivider = false,
QueuedDividerText = "",
Items = {},
Registered = {},
GuiElems = {},
Theme = {}
},mt)
obj.Gui = createGui(obj)
obj:ApplyTheme({})
return obj
end
return {new = new}
end)()
Lib.CodeFrame = (function()
local funcs = {}
local typeMap = {
[1] = "String",
[2] = "String",
[3] = "String",
[4] = "Comment",
[5] = "Operator",
[6] = "Number",
[7] = "Keyword",
[8] = "BuiltIn",
[9] = "LocalMethod",
[10] = "LocalProperty",
[11] = "Nil",
[12] = "Bool",
[13] = "Function",
[14] = "Local",
[15] = "Self",
[16] = "FunctionName",
[17] = "Bracket"
}
local specialKeywordsTypes = {
["nil"] = 11,
["true"] = 12,
["false"] = 12,
["function"] = 13,
["local"] = 14,
["self"] = 15
}
local keywords = {
["and"] = true,
["break"] = true,
["do"] = true,
["else"] = true,
["elseif"] = true,
["end"] = true,
["false"] = true,
["for"] = true,
["function"] = true,
["if"] = true,
["in"] = true,
["local"] = true,
["nil"] = true,
["not"] = true,
["or"] = true,
["repeat"] = true,
["return"] = true,
["then"] = true,
["true"] = true,
["until"] = true,
["while"] = true,
["plugin"] = true
}
local builtIns = {
["delay"] = true,
["elapsedTime"] = true,
["require"] = true,
["spawn"] = true,
["tick"] = true,
["time"] = true,
["typeof"] = true,
["UserSettings"] = true,
["wait"] = true,
["warn"] = true,
["game"] = true,
["shared"] = true,
["script"] = true,
["workspace"] = true,
["assert"] = true,
["collectgarbage"] = true,
["error"] = true,
["getfenv"] = true,
["getmetatable"] = true,
["ipairs"] = true,
["loadstring"] = true,
["newproxy"] = true,
["next"] = true,
["pairs"] = true,
["pcall"] = true,
["print"] = true,
["rawequal"] = true,
["rawget"] = true,
["rawset"] = true,
["select"] = true,
["setfenv"] = true,
["setmetatable"] = true,
["tonumber"] = true,
["tostring"] = true,
["type"] = true,
["unpack"] = true,
["xpcall"] = true,
["_G"] = true,
["_VERSION"] = true,
["coroutine"] = true,
["debug"] = true,
["math"] = true,
["os"] = true,
["string"] = true,
["table"] = true,
["bit32"] = true,
["utf8"] = true,
["Axes"] = true,
["BrickColor"] = true,
["CFrame"] = true,
["Color3"] = true,
["ColorSequence"] = true,
["ColorSequenceKeypoint"] = true,
["DockWidgetPluginGuiInfo"] = true,
["Enum"] = true,
["Faces"] = true,
["Instance"] = true,
["NumberRange"] = true,
["NumberSequence"] = true,
["NumberSequenceKeypoint"] = true,
["PathWaypoint"] = true,
["PhysicalProperties"] = true,
["Random"] = true,
["Ray"] = true,
["Rect"] = true,
["Region3"] = true,
["Region3int16"] = true,
["TweenInfo"] = true,
["UDim"] = true,
["UDim2"] = true,
["Vector2"] = true,
["Vector2int16"] = true,
["Vector3"] = true,
["Vector3int16"] = true
}
local builtInInited = false
local richReplace = {
["'"] = "'",
["\""] = """,
["<"] = "<",
[">"] = ">",
["&"] = "&"
}
local tabSub = "\205"
local tabReplacement = (" %s%s "):format(tabSub,tabSub)
local tabJumps = {
[("[^%s] %s"):format(tabSub,tabSub)] = 0,
[(" %s%s"):format(tabSub,tabSub)] = -1,
[("%s%s "):format(tabSub,tabSub)] = 2,
[("%s [^%s]"):format(tabSub,tabSub)] = 1,
}
local tweenService = service.TweenService
local lineTweens = {}
local function initBuiltIn()
local env = getfenv()
local type = type
local tostring = tostring
for name,_ in next,builtIns do
local envVal = env[name]
if type(envVal) == "table" then
local items = {}
for i,v in next,envVal do
items[i] = true
end
builtIns[name] = items
end
end
local enumEntries = {}
local enums = Enum:GetEnums()
for i = 1,#enums do
enumEntries[tostring(enums[i])] = true
end
builtIns["Enum"] = enumEntries
builtInInited = true
end
local function setupEditBox(obj)
local editBox = obj.GuiElems.EditBox
editBox.Focused:Connect(function()
obj:ConnectEditBoxEvent()
obj.Editing = true
end)
editBox.FocusLost:Connect(function()
obj:DisconnectEditBoxEvent()
obj.Editing = false
end)
editBox:GetPropertyChangedSignal("Text"):Connect(function()
local text = editBox.Text
if #text == 0 or obj.EditBoxCopying then return end
editBox.Text = ""
obj:AppendText(text)
end)
end
local function setupMouseSelection(obj)
local mouse = plr:GetMouse()
local codeFrame = obj.GuiElems.LinesFrame
local lines = obj.Lines
codeFrame.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
local fontSizeX,fontSizeY = math.ceil(obj.FontSize/2),obj.FontSize
local relX = mouse.X - codeFrame.AbsolutePosition.X
local relY = mouse.Y - codeFrame.AbsolutePosition.Y
local selX = math.round(relX / fontSizeX) + obj.ViewX
local selY = math.floor(relY / fontSizeY) + obj.ViewY
local releaseEvent,mouseEvent,scrollEvent
local scrollPowerV,scrollPowerH = 0,0
selY = math.min(#lines-1,selY)
local relativeLine = lines[selY+1] or ""
selX = math.min(#relativeLine, selX + obj:TabAdjust(selX,selY))
obj.SelectionRange = {{-1,-1},{-1,-1}}
obj:MoveCursor(selX,selY)
obj.FloatCursorX = selX
local function updateSelection()
local relX = mouse.X - codeFrame.AbsolutePosition.X
local relY = mouse.Y - codeFrame.AbsolutePosition.Y
local sel2X = math.max(0,math.round(relX / fontSizeX) + obj.ViewX)
local sel2Y = math.max(0,math.floor(relY / fontSizeY) + obj.ViewY)
sel2Y = math.min(#lines-1,sel2Y)
local relativeLine = lines[sel2Y+1] or ""
sel2X = math.min(#relativeLine, sel2X + obj:TabAdjust(sel2X,sel2Y))
if sel2Y < selY or (sel2Y == selY and sel2X < selX) then
obj.SelectionRange = {{sel2X,sel2Y},{selX,selY}}
else
obj.SelectionRange = {{selX,selY},{sel2X,sel2Y}}
end
obj:MoveCursor(sel2X,sel2Y)
obj.FloatCursorX = sel2X
obj:Refresh()
end
releaseEvent = service.UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
releaseEvent:Disconnect()
mouseEvent:Disconnect()
scrollEvent:Disconnect()
obj:SetCopyableSelection()
--updateSelection()
end
end)
mouseEvent = service.UserInputService.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
local upDelta = mouse.Y - codeFrame.AbsolutePosition.Y
local downDelta = mouse.Y - codeFrame.AbsolutePosition.Y - codeFrame.AbsoluteSize.Y
local leftDelta = mouse.X - codeFrame.AbsolutePosition.X
local rightDelta = mouse.X - codeFrame.AbsolutePosition.X - codeFrame.AbsoluteSize.X
scrollPowerV = 0
scrollPowerH = 0
if downDelta > 0 then
scrollPowerV = math.floor(downDelta*0.05) + 1
elseif upDelta < 0 then
scrollPowerV = math.ceil(upDelta*0.05) - 1
end
if rightDelta > 0 then
scrollPowerH = math.floor(rightDelta*0.05) + 1
elseif leftDelta < 0 then
scrollPowerH = math.ceil(leftDelta*0.05) - 1
end
updateSelection()
end
end)
scrollEvent = cloneref(game["Run Service"].Parent:GetService("RunService")).RenderStepped:Connect(function()
if scrollPowerV ~= 0 or scrollPowerH ~= 0 then
obj:ScrollDelta(scrollPowerH,scrollPowerV)
updateSelection()
end
end)
obj:Refresh()
end
end)
end
local function makeFrame(obj)
local frame = create({
{1,"Frame",{BackgroundColor3=Color3.new(0.15686275064945,0.15686275064945,0.15686275064945),BorderSizePixel = 0,Position=UDim2.new(0.5,-300,0.5,-200),Size=UDim2.new(0,600,0,400),}},
})
local elems = {}
local linesFrame = Instance.new("Frame")
linesFrame.Name = "Lines"
linesFrame.BackgroundTransparency = 1
linesFrame.Size = UDim2.new(1,0,1,0)
linesFrame.ClipsDescendants = true
linesFrame.Parent = frame
local lineNumbersLabel = Instance.new("TextLabel")
lineNumbersLabel.Name = "LineNumbers"
lineNumbersLabel.BackgroundTransparency = 1
lineNumbersLabel.Font = Enum.Font.Code
lineNumbersLabel.TextXAlignment = Enum.TextXAlignment.Right
lineNumbersLabel.TextYAlignment = Enum.TextYAlignment.Top
lineNumbersLabel.ClipsDescendants = true
lineNumbersLabel.RichText = true
lineNumbersLabel.Parent = frame
local cursor = Instance.new("Frame")
cursor.Name = "Cursor"
cursor.BackgroundColor3 = Color3.fromRGB(220,220,220)
cursor.BorderSizePixel = 0
cursor.Parent = frame
local editBox = Instance.new("TextBox")
editBox.Name = "EditBox"
editBox.MultiLine = true
editBox.Visible = false
editBox.Parent = frame
lineTweens.Invis = tweenService:Create(cursor,TweenInfo.new(0.4,Enum.EasingStyle.Quart,Enum.EasingDirection.Out),{BackgroundTransparency = 1})
lineTweens.Vis = tweenService:Create(cursor,TweenInfo.new(0.2,Enum.EasingStyle.Quart,Enum.EasingDirection.Out),{BackgroundTransparency = 0})
elems.LinesFrame = linesFrame
elems.LineNumbersLabel = lineNumbersLabel
elems.Cursor = cursor
elems.EditBox = editBox
elems.ScrollCorner = create({{1,"Frame",{BackgroundColor3=Color3.new(0.15686275064945,0.15686275064945,0.15686275064945),BorderSizePixel=0,Name="ScrollCorner",Position=UDim2.new(1,-16,1,-16),Size=UDim2.new(0,16,0,16),Visible=false,}}})
elems.ScrollCorner.Parent = frame
linesFrame.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
obj:SetEditing(true,input)
end
end)
obj.Frame = frame
obj.Gui = frame
obj.GuiElems = elems
setupEditBox(obj)
setupMouseSelection(obj)
return frame
end
funcs.GetSelectionText = function(self)
if not self:IsValidRange() then return "" end
local selectionRange = self.SelectionRange
local selX,selY = selectionRange[1][1], selectionRange[1][2]
local sel2X,sel2Y = selectionRange[2][1], selectionRange[2][2]
local deltaLines = sel2Y-selY
local lines = self.Lines
if not lines[selY+1] or not lines[sel2Y+1] then return "" end
if deltaLines == 0 then
return self:ConvertText(lines[selY+1]:sub(selX+1,sel2X), false)
end
local leftSub = lines[selY+1]:sub(selX+1)
local rightSub = lines[sel2Y+1]:sub(1,sel2X)
local result = leftSub.."\n"
for i = selY+1,sel2Y-1 do
result = result..lines[i+1].."\n"
end
result = result..rightSub
return self:ConvertText(result,false)
end
funcs.SetCopyableSelection = function(self)
local text = self:GetSelectionText()
local editBox = self.GuiElems.EditBox
self.EditBoxCopying = true
editBox.Text = text
editBox.SelectionStart = 1
editBox.CursorPosition = #editBox.Text + 1
self.EditBoxCopying = false
end
funcs.ConnectEditBoxEvent = function(self)
if self.EditBoxEvent then
self.EditBoxEvent:Disconnect()
end
self.EditBoxEvent = service.UserInputService.InputBegan:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.Keyboard then return end
local keycodes = Enum.KeyCode
local keycode = input.KeyCode
local function setupMove(key,func)
local endCon,finished
endCon = service.UserInputService.InputEnded:Connect(function(input)
if input.KeyCode ~= key then return end
endCon:Disconnect()
finished = true
end)
func()
Lib.FastWait(0.5)
while not finished do func() Lib.FastWait(0.03) end
end
if keycode == keycodes.Down then
setupMove(keycodes.Down,function()
self.CursorX = self.FloatCursorX
self.CursorY = self.CursorY + 1
self:UpdateCursor()
self:JumpToCursor()
end)
elseif keycode == keycodes.Up then
setupMove(keycodes.Up,function()
self.CursorX = self.FloatCursorX
self.CursorY = self.CursorY - 1
self:UpdateCursor()
self:JumpToCursor()
end)
elseif keycode == keycodes.Left then
setupMove(keycodes.Left,function()
local line = self.Lines[self.CursorY+1] or ""
self.CursorX = self.CursorX - 1 - (line:sub(self.CursorX-3,self.CursorX) == tabReplacement and 3 or 0)
if self.CursorX < 0 then
self.CursorY = self.CursorY - 1
local line2 = self.Lines[self.CursorY+1] or ""
self.CursorX = #line2
end
self.FloatCursorX = self.CursorX
self:UpdateCursor()
self:JumpToCursor()
end)
elseif keycode == keycodes.Right then
setupMove(keycodes.Right,function()
local line = self.Lines[self.CursorY+1] or ""
self.CursorX = self.CursorX + 1 + (line:sub(self.CursorX+1,self.CursorX+4) == tabReplacement and 3 or 0)
if self.CursorX > #line then
self.CursorY = self.CursorY + 1
self.CursorX = 0
end
self.FloatCursorX = self.CursorX
self:UpdateCursor()
self:JumpToCursor()
end)
elseif keycode == keycodes.Backspace then
setupMove(keycodes.Backspace,function()
local startRange,endRange
if self:IsValidRange() then
startRange = self.SelectionRange[1]
endRange = self.SelectionRange[2]
else
endRange = {self.CursorX,self.CursorY}
end
if not startRange then
local line = self.Lines[self.CursorY+1] or ""
self.CursorX = self.CursorX - 1 - (line:sub(self.CursorX-3,self.CursorX) == tabReplacement and 3 or 0)
if self.CursorX < 0 then
self.CursorY = self.CursorY - 1
local line2 = self.Lines[self.CursorY+1] or ""
self.CursorX = #line2
end
self.FloatCursorX = self.CursorX
self:UpdateCursor()
startRange = startRange or {self.CursorX,self.CursorY}
end
self:DeleteRange({startRange,endRange},false,true)
self:ResetSelection(true)
self:JumpToCursor()
end)
elseif keycode == keycodes.Delete then
setupMove(keycodes.Delete,function()
local startRange,endRange
if self:IsValidRange() then
startRange = self.SelectionRange[1]
endRange = self.SelectionRange[2]
else
startRange = {self.CursorX,self.CursorY}
end
if not endRange then
local line = self.Lines[self.CursorY+1] or ""
local endCursorX = self.CursorX + 1 + (line:sub(self.CursorX+1,self.CursorX+4) == tabReplacement and 3 or 0)
local endCursorY = self.CursorY
if endCursorX > #line then
endCursorY = endCursorY + 1
endCursorX = 0
end
self:UpdateCursor()
endRange = endRange or {endCursorX,endCursorY}
end
self:DeleteRange({startRange,endRange},false,true)
self:ResetSelection(true)
self:JumpToCursor()
end)
elseif service.UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) then
if keycode == keycodes.A then
self.SelectionRange = {{0,0},{#self.Lines[#self.Lines],#self.Lines-1}}
self:SetCopyableSelection()
self:Refresh()
end
end
end)
end
funcs.DisconnectEditBoxEvent = function(self)
if self.EditBoxEvent then
self.EditBoxEvent:Disconnect()
end
end
funcs.ResetSelection = function(self,norefresh)
self.SelectionRange = {{-1,-1},{-1,-1}}
if not norefresh then self:Refresh() end
end
funcs.IsValidRange = function(self,range)
local selectionRange = range or self.SelectionRange
local selX,selY = selectionRange[1][1], selectionRange[1][2]
local sel2X,sel2Y = selectionRange[2][1], selectionRange[2][2]
if selX == -1 or (selX == sel2X and selY == sel2Y) then return false end
return true
end
funcs.DeleteRange = function(self,range,noprocess,updatemouse)
range = range or self.SelectionRange
if not self:IsValidRange(range) then return end
local lines = self.Lines
local selX,selY = range[1][1], range[1][2]
local sel2X,sel2Y = range[2][1], range[2][2]
local deltaLines = sel2Y-selY
if not lines[selY+1] or not lines[sel2Y+1] then return end
local leftSub = lines[selY+1]:sub(1,selX)
local rightSub = lines[sel2Y+1]:sub(sel2X+1)
lines[selY+1] = leftSub..rightSub
local remove = table.remove
for i = 1,deltaLines do
remove(lines,selY+2)
end
if range == self.SelectionRange then self.SelectionRange = {{-1,-1},{-1,-1}} end
if updatemouse then
self.CursorX = selX
self.CursorY = selY
self:UpdateCursor()
end
if not noprocess then
self:ProcessTextChange()
end
end
funcs.AppendText = function(self,text)
self:DeleteRange(nil,true,true)
local lines,cursorX,cursorY = self.Lines,self.CursorX,self.CursorY
local line = lines[cursorY+1]
local before = line:sub(1,cursorX)
local after = line:sub(cursorX+1)
text = text:gsub("\r\n","\n")
text = self:ConvertText(text,true) -- Tab Convert
local textLines = text:split("\n")
local insert = table.insert
for i = 1,#textLines do
local linePos = cursorY+i
if i > 1 then insert(lines,linePos,"") end
local textLine = textLines[i]
local newBefore = (i == 1 and before or "")
local newAfter = (i == #textLines and after or "")
lines[linePos] = newBefore..textLine..newAfter
end
if #textLines > 1 then cursorX = 0 end
self:ProcessTextChange()
self.CursorX = cursorX + #textLines[#textLines]
self.CursorY = cursorY + #textLines-1
self:UpdateCursor()
end
funcs.ScrollDelta = function(self,x,y)
self.ScrollV:ScrollTo(self.ScrollV.Index + y)
self.ScrollH:ScrollTo(self.ScrollH.Index + x)
end
-- x and y starts at 0
funcs.TabAdjust = function(self,x,y)
local lines = self.Lines
local line = lines[y+1]
x=x+1
if line then
local left = line:sub(x-1,x-1)
local middle = line:sub(x,x)
local right = line:sub(x+1,x+1)
local selRange = (#left > 0 and left or " ") .. (#middle > 0 and middle or " ") .. (#right > 0 and right or " ")
for i,v in pairs(tabJumps) do
if selRange:find(i) then
return v
end
end
end
return 0
end
funcs.SetEditing = function(self,on,input)
self:UpdateCursor(input)
if on then
if self.Editable then
self.GuiElems.EditBox.Text = ""
self.GuiElems.EditBox:CaptureFocus()
end
else
self.GuiElems.EditBox:ReleaseFocus()
end
end
funcs.CursorAnim = function(self,on)
local cursor = self.GuiElems.Cursor
local animTime = tick()
self.LastAnimTime = animTime
if not on then return end
lineTweens.Invis:Cancel()
lineTweens.Vis:Cancel()
cursor.BackgroundTransparency = 0
coroutine.wrap(function()
while self.Editable do
Lib.FastWait(0.5)
if self.LastAnimTime ~= animTime then return end
lineTweens.Invis:Play()
Lib.FastWait(0.4)
if self.LastAnimTime ~= animTime then return end
lineTweens.Vis:Play()
Lib.FastWait(0.2)
end
end)()
end
funcs.MoveCursor = function(self,x,y)
self.CursorX = x
self.CursorY = y
self:UpdateCursor()
self:JumpToCursor()
end
funcs.JumpToCursor = function(self)
self:Refresh()
end
funcs.UpdateCursor = function(self,input)
local linesFrame = self.GuiElems.LinesFrame
local cursor = self.GuiElems.Cursor
local hSize = math.max(0,linesFrame.AbsoluteSize.X)
local vSize = math.max(0,linesFrame.AbsoluteSize.Y)
local maxLines = math.ceil(vSize / self.FontSize)
local maxCols = math.ceil(hSize / math.ceil(self.FontSize/2))
local viewX,viewY = self.ViewX,self.ViewY
local totalLinesStr = tostring(#self.Lines)
local fontWidth = math.ceil(self.FontSize / 2)
local linesOffset = #totalLinesStr*fontWidth + 4*fontWidth
if input then
local linesFrame = self.GuiElems.LinesFrame
local frameX,frameY = linesFrame.AbsolutePosition.X,linesFrame.AbsolutePosition.Y
local mouseX,mouseY = input.Position.X,input.Position.Y
local fontSizeX,fontSizeY = math.ceil(self.FontSize/2),self.FontSize
self.CursorX = self.ViewX + math.round((mouseX - frameX) / fontSizeX)
self.CursorY = self.ViewY + math.floor((mouseY - frameY) / fontSizeY)
end
local cursorX,cursorY = self.CursorX,self.CursorY
local line = self.Lines[cursorY+1] or ""
if cursorX > #line then cursorX = #line
elseif cursorX < 0 then cursorX = 0 end
if cursorY >= #self.Lines then
cursorY = math.max(0,#self.Lines-1)
elseif cursorY < 0 then
cursorY = 0
end
cursorX = cursorX + self:TabAdjust(cursorX,cursorY)
-- Update modified
self.CursorX = cursorX
self.CursorY = cursorY
local cursorVisible = (cursorX >= viewX) and (cursorY >= viewY) and (cursorX <= viewX + maxCols) and (cursorY <= viewY + maxLines)
if cursorVisible then
local offX = (cursorX - viewX)
local offY = (cursorY - viewY)
cursor.Position = UDim2.new(0,linesOffset + offX*math.ceil(self.FontSize/2) - 1,0,offY*self.FontSize)
cursor.Size = UDim2.new(0,1,0,self.FontSize+2)
cursor.Visible = true
self:CursorAnim(true)
else
cursor.Visible = false
end
end
funcs.MapNewLines = function(self)
local newLines = {}
local count = 1
local text = self.Text
local find = string.find
local init = 1
local pos = find(text,"\n",init,true)
while pos do
newLines[count] = pos
count = count + 1
init = pos + 1
pos = find(text,"\n",init,true)
end
self.NewLines = newLines
end
funcs.PreHighlight = function(self)
local start = tick()
local text = self.Text:gsub("\\\\"," ")
--print("BACKSLASH SUB",tick()-start)
local textLen = #text
local found = {}
local foundMap = {}
local extras = {}
local find = string.find
local sub = string.sub
self.ColoredLines = {}
local function findAll(str,pattern,typ,raw)
local count = #found+1
local init = 1
local x,y,extra = find(str,pattern,init,raw)
while x do
found[count] = x
foundMap[x] = typ
if extra then
extras[x] = extra
end
count = count+1
init = y+1
x,y,extra = find(str,pattern,init,raw)
end
end
local start = tick()
findAll(text,'"',1,true)
findAll(text,"'",2,true)
findAll(text,"%[(=*)%[",3)
findAll(text,"--",4,true)
table.sort(found)
local newLines = self.NewLines
local curLine = 0
local lineTableCount = 1
local lineStart = 0
local lineEnd = 0
local lastEnding = 0
local foundHighlights = {}
for i = 1,#found do
local pos = found[i]
if pos <= lastEnding then continue end
local ending = pos
local typ = foundMap[pos]
if typ == 1 then
ending = find(text,'"',pos+1,true)
while ending and sub(text,ending-1,ending-1) == "\\" do
ending = find(text,'"',ending+1,true)
end
if not ending then ending = textLen end
elseif typ == 2 then
ending = find(text,"'",pos+1,true)
while ending and sub(text,ending-1,ending-1) == "\\" do
ending = find(text,"'",ending+1,true)
end
if not ending then ending = textLen end
elseif typ == 3 then
_,ending = find(text,"]"..extras[pos].."]",pos+1,true)
if not ending then ending = textLen end
elseif typ == 4 then
local ahead = foundMap[pos+2]
if ahead == 3 then
_,ending = find(text,"]"..extras[pos+2].."]",pos+1,true)
if not ending then ending = textLen end
else
ending = find(text,"\n",pos+1,true) or textLen
end
end
while pos > lineEnd do
curLine = curLine + 1
--lineTableCount = 1
lineEnd = newLines[curLine] or textLen+1
end
while true do
local lineTable = foundHighlights[curLine]
if not lineTable then lineTable = {} foundHighlights[curLine] = lineTable end
lineTable[pos] = {typ,ending}
--lineTableCount = lineTableCount + 1
if ending > lineEnd then
curLine = curLine + 1
lineEnd = newLines[curLine] or textLen+1
else
break
end
end
lastEnding = ending
--if i < 200 then print(curLine) end
end
self.PreHighlights = foundHighlights
--print(tick()-start)
--print(#found,curLine)
end
funcs.HighlightLine = function(self,line)
local cached = self.ColoredLines[line]
if cached then return cached end
local sub = string.sub
local find = string.find
local match = string.match
local highlights = {}
local preHighlights = self.PreHighlights[line] or {}
local lineText = self.Lines[line] or ""
local lineLen = #lineText
local lastEnding = 0
local currentType = 0
local lastWord = nil
local wordBeginsDotted = false
local funcStatus = 0
local lineStart = self.NewLines[line-1] or 0
local preHighlightMap = {}
for pos,data in next,preHighlights do
local relativePos = pos-lineStart
if relativePos < 1 then
currentType = data[1]
lastEnding = data[2] - lineStart
--warn(pos,data[2])
else
preHighlightMap[relativePos] = {data[1],data[2]-lineStart}
end
end
for col = 1,#lineText do
if col <= lastEnding then highlights[col] = currentType continue end
local pre = preHighlightMap[col]
if pre then
currentType = pre[1]
lastEnding = pre[2]
highlights[col] = currentType
wordBeginsDotted = false
lastWord = nil
funcStatus = 0
else
local char = sub(lineText,col,col)
if find(char,"[%a_]") then
local word = match(lineText,"[%a%d_]+",col)
local wordType = (keywords[word] and 7) or (builtIns[word] and 8)
lastEnding = col+#word-1
if wordType ~= 7 then
if wordBeginsDotted then
local prevBuiltIn = lastWord and builtIns[lastWord]
wordType = (prevBuiltIn and type(prevBuiltIn) == "table" and prevBuiltIn[word] and 8) or 10
end
if wordType ~= 8 then
local x,y,br = find(lineText,"^%s*([%({\"'])",lastEnding+1)
if x then
wordType = (funcStatus > 0 and br == "(" and 16) or 9
funcStatus = 0
end
end
else
wordType = specialKeywordsTypes[word] or wordType
funcStatus = (word == "function" and 1 or 0)
end
lastWord = word
wordBeginsDotted = false
if funcStatus > 0 then funcStatus = 1 end
if wordType then
currentType = wordType
highlights[col] = currentType
else
currentType = nil
end
elseif find(char,"%p") then
local isDot = (char == ".")
local isNum = isDot and find(sub(lineText,col+1,col+1),"%d")
highlights[col] = (isNum and 6 or 5)
if not isNum then
local dotStr = isDot and match(lineText,"%.%.?%.?",col)
if dotStr and #dotStr > 1 then
currentType = 5
lastEnding = col+#dotStr-1
wordBeginsDotted = false
lastWord = nil
funcStatus = 0
else
if isDot then
if wordBeginsDotted then
lastWord = nil
else
wordBeginsDotted = true
end
else
wordBeginsDotted = false
lastWord = nil
end
funcStatus = ((isDot or char == ":") and funcStatus == 1 and 2) or 0
end
end
elseif find(char,"%d") then
local _,endPos = find(lineText,"%x+",col)
local endPart = sub(lineText,endPos,endPos+1)
if (endPart == "e+" or endPart == "e-") and find(sub(lineText,endPos+2,endPos+2),"%d") then
endPos = endPos + 1
end
currentType = 6
lastEnding = endPos
highlights[col] = 6
wordBeginsDotted = false
lastWord = nil
funcStatus = 0
else
highlights[col] = currentType
local _,endPos = find(lineText,"%s+",col)
if endPos then
lastEnding = endPos
end
end
end
end
self.ColoredLines[line] = highlights
return highlights
end
funcs.Refresh = function(self)
local start = tick()
local linesFrame = self.Frame.Lines
local hSize = math.max(0,linesFrame.AbsoluteSize.X)
local vSize = math.max(0,linesFrame.AbsoluteSize.Y)
local maxLines = math.ceil(vSize / self.FontSize)
local maxCols = math.ceil(hSize / math.ceil(self.FontSize/2))
local gsub = string.gsub
local sub = string.sub
local viewX,viewY = self.ViewX,self.ViewY
local lineNumberStr = ""
for row = 1,maxLines do
local lineFrame = self.LineFrames[row]
if not lineFrame then
lineFrame = Instance.new("Frame")
lineFrame.Name = "Line"
lineFrame.Position = UDim2.new(0,0,0,(row-1)*self.FontSize)
lineFrame.Size = UDim2.new(1,0,0,self.FontSize)
lineFrame.BorderSizePixel = 0
lineFrame.BackgroundTransparency = 1
local selectionHighlight = Instance.new("Frame")
selectionHighlight.Name = "SelectionHighlight"
selectionHighlight.BorderSizePixel = 0
selectionHighlight.BackgroundColor3 = Settings.Theme.Syntax.SelectionBack
selectionHighlight.Parent = lineFrame
local label = Instance.new("TextLabel")
label.Name = "Label"
label.BackgroundTransparency = 1
label.Font = Enum.Font.Code
label.TextSize = self.FontSize
label.Size = UDim2.new(1,0,0,self.FontSize)
label.RichText = true
label.TextXAlignment = Enum.TextXAlignment.Left
label.TextColor3 = self.Colors.Text
label.ZIndex = 2
label.Parent = lineFrame
lineFrame.Parent = linesFrame
self.LineFrames[row] = lineFrame
end
local relaY = viewY + row
local lineText = self.Lines[relaY] or ""
local resText = ""
local highlights = self:HighlightLine(relaY)
local colStart = viewX + 1
local richTemplates = self.RichTemplates
local textTemplate = richTemplates.Text
local selectionTemplate = richTemplates.Selection
local curType = highlights[colStart]
local curTemplate = richTemplates[typeMap[curType]] or textTemplate
-- Selection Highlight
local selectionRange = self.SelectionRange
local selPos1 = selectionRange[1]
local selPos2 = selectionRange[2]
local selRow,selColumn = selPos1[2],selPos1[1]
local sel2Row,sel2Column = selPos2[2],selPos2[1]
local selRelaX,selRelaY = viewX,relaY-1
if selRelaY >= selPos1[2] and selRelaY <= selPos2[2] then
local fontSizeX = math.ceil(self.FontSize/2)
local posX = (selRelaY == selPos1[2] and selPos1[1] or 0) - viewX
local sizeX = (selRelaY == selPos2[2] and selPos2[1]-posX-viewX or maxCols+viewX)
lineFrame.SelectionHighlight.Position = UDim2.new(0,posX*fontSizeX,0,0)
lineFrame.SelectionHighlight.Size = UDim2.new(0,sizeX*fontSizeX,1,0)
lineFrame.SelectionHighlight.Visible = true
else
lineFrame.SelectionHighlight.Visible = false
end
-- Selection Text Color for first char
local inSelection = selRelaY >= selRow and selRelaY <= sel2Row and (selRelaY == selRow and viewX >= selColumn or selRelaY ~= selRow) and (selRelaY == sel2Row and viewX < sel2Column or selRelaY ~= sel2Row)
if inSelection then
curType = -999
curTemplate = selectionTemplate
end
for col = 2,maxCols do
local relaX = viewX + col
local selRelaX = relaX-1
local posType = highlights[relaX]
-- Selection Text Color
local inSelection = selRelaY >= selRow and selRelaY <= sel2Row and (selRelaY == selRow and selRelaX >= selColumn or selRelaY ~= selRow) and (selRelaY == sel2Row and selRelaX < sel2Column or selRelaY ~= sel2Row)
if inSelection then
posType = -999
end
if posType ~= curType then
local template = (inSelection and selectionTemplate) or richTemplates[typeMap[posType]] or textTemplate
if template ~= curTemplate then
local nextText = gsub(sub(lineText,colStart,relaX-1),"['\"<>&]",richReplace)
resText = resText .. (curTemplate ~= textTemplate and (curTemplate .. nextText .. "</font>") or nextText)
colStart = relaX
curTemplate = template
end
curType = posType
end
end
local lastText = gsub(sub(lineText,colStart,viewX+maxCols),"['\"<>&]",richReplace)
--warn("SUB",colStart,viewX+maxCols-1)
if #lastText > 0 then
resText = resText .. (curTemplate ~= textTemplate and (curTemplate .. lastText .. "</font>") or lastText)
end
if self.Lines[relaY] then
lineNumberStr = lineNumberStr .. (relaY == self.CursorY and ("<b>"..relaY.."</b>\n") or relaY .. "\n")
end
lineFrame.Label.Text = resText
end
for i = maxLines+1,#self.LineFrames do
self.LineFrames[i]:Destroy()
self.LineFrames[i] = nil
end
self.Frame.LineNumbers.Text = lineNumberStr
self:UpdateCursor()
--print("REFRESH TIME",tick()-start)
end
funcs.UpdateView = function(self)
local totalLinesStr = tostring(#self.Lines)
local fontWidth = math.ceil(self.FontSize / 2)
local linesOffset = #totalLinesStr*fontWidth + 4*fontWidth
local linesFrame = self.Frame.Lines
local hSize = linesFrame.AbsoluteSize.X
local vSize = linesFrame.AbsoluteSize.Y
local maxLines = math.ceil(vSize / self.FontSize)
local totalWidth = self.MaxTextCols*fontWidth
local scrollV = self.ScrollV
local scrollH = self.ScrollH
scrollV.VisibleSpace = maxLines
scrollV.TotalSpace = #self.Lines + 1
scrollH.VisibleSpace = math.ceil(hSize/fontWidth)
scrollH.TotalSpace = self.MaxTextCols + 1
scrollV.Gui.Visible = #self.Lines + 1 > maxLines
scrollH.Gui.Visible = totalWidth > hSize
local oldOffsets = self.FrameOffsets
self.FrameOffsets = Vector2.new(scrollV.Gui.Visible and -16 or 0, scrollH.Gui.Visible and -16 or 0)
if oldOffsets ~= self.FrameOffsets then
self:UpdateView()
else
scrollV:ScrollTo(self.ViewY,true)
scrollH:ScrollTo(self.ViewX,true)
if scrollV.Gui.Visible and scrollH.Gui.Visible then
scrollV.Gui.Size = UDim2.new(0,16,1,-16)
scrollH.Gui.Size = UDim2.new(1,-16,0,16)
self.GuiElems.ScrollCorner.Visible = true
else
scrollV.Gui.Size = UDim2.new(0,16,1,0)
scrollH.Gui.Size = UDim2.new(1,0,0,16)
self.GuiElems.ScrollCorner.Visible = false
end
self.ViewY = scrollV.Index
self.ViewX = scrollH.Index
self.Frame.Lines.Position = UDim2.new(0,linesOffset,0,0)
self.Frame.Lines.Size = UDim2.new(1,-linesOffset+oldOffsets.X,1,oldOffsets.Y)
self.Frame.LineNumbers.Position = UDim2.new(0,fontWidth,0,0)
self.Frame.LineNumbers.Size = UDim2.new(0,#totalLinesStr*fontWidth,1,oldOffsets.Y)
self.Frame.LineNumbers.TextSize = self.FontSize
end
end
funcs.ProcessTextChange = function(self)
local maxCols = 0
local lines = self.Lines
for i = 1,#lines do
local lineLen = #lines[i]
if lineLen > maxCols then
maxCols = lineLen
end
end
self.MaxTextCols = maxCols
self:UpdateView()
self.Text = table.concat(self.Lines,"\n")
self:MapNewLines()
self:PreHighlight()
self:Refresh()
--self.TextChanged:Fire()
end
funcs.ConvertText = function(self,text,toEditor)
if toEditor then
return text:gsub("\t",(" %s%s "):format(tabSub,tabSub))
else
return text:gsub((" %s%s "):format(tabSub,tabSub),"\t")
end
end
funcs.GetText = function(self) -- TODO: better (use new tab format)
local source = table.concat(self.Lines,"\n")
return self:ConvertText(source,false) -- Tab Convert
end
funcs.SetText = function(self,txt)
txt = self:ConvertText(txt,true) -- Tab Convert
local lines = self.Lines
table.clear(lines)
local count = 1
for line in txt:gmatch("([^\n\r]*)[\n\r]?") do
local len = #line
lines[count] = line
count = count + 1
end
self:ProcessTextChange()
end
funcs.MakeRichTemplates = function(self)
local floor = math.floor
local templates = {}
for name,color in pairs(self.Colors) do
templates[name] = ('<font color="rgb(%s,%s,%s)">'):format(floor(color.r*255),floor(color.g*255),floor(color.b*255))
end
self.RichTemplates = templates
end
funcs.ApplyTheme = function(self)
local colors = Settings.Theme.Syntax
self.Colors = colors
self.Frame.LineNumbers.TextColor3 = colors.Text
self.Frame.BackgroundColor3 = colors.Background
end
local mt = {__index = funcs}
local function new()
if not builtInInited then initBuiltIn() end
local scrollV = Lib.ScrollBar.new()
local scrollH = Lib.ScrollBar.new(true)
scrollH.Gui.Position = UDim2.new(0,0,1,-16)
local obj = setmetatable({
FontSize = 15,
ViewX = 0,
ViewY = 0,
Colors = Settings.Theme.Syntax,
ColoredLines = {},
Lines = {""},
LineFrames = {},
Editable = true,
Editing = false,
CursorX = 0,
CursorY = 0,
FloatCursorX = 0,
Text = "",
PreHighlights = {},
SelectionRange = {{-1,-1},{-1,-1}},
NewLines = {},
FrameOffsets = Vector2.new(0,0),
MaxTextCols = 0,
ScrollV = scrollV,
ScrollH = scrollH
},mt)
scrollV.WheelIncrement = 3
scrollH.Increment = 2
scrollH.WheelIncrement = 7
scrollV.Scrolled:Connect(function()
obj.ViewY = scrollV.Index
obj:Refresh()
end)
scrollH.Scrolled:Connect(function()
obj.ViewX = scrollH.Index
obj:Refresh()
end)
makeFrame(obj)
obj:MakeRichTemplates()
obj:ApplyTheme()
scrollV:SetScrollFrame(obj.Frame.Lines)
scrollV.Gui.Parent = obj.Frame
scrollH.Gui.Parent = obj.Frame
obj:UpdateView()
obj.Frame:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
obj:UpdateView()
obj:Refresh()
end)
return obj
end
return {new = new}
end)()
Lib.Checkbox = (function()
local funcs = {}
local c3 = Color3.fromRGB
local v2 = Vector2.new
local ud2s = UDim2.fromScale
local ud2o = UDim2.fromOffset
local ud = UDim.new
local max = math.max
local new = Instance.new
local TweenSize = new("Frame").TweenSize
local ti = TweenInfo.new
local delay = delay
local function ripple(object, color)
local circle = new('Frame')
circle.BackgroundColor3 = color
circle.BackgroundTransparency = 0.75
circle.BorderSizePixel = 0
circle.AnchorPoint = v2(0.5, 0.5)
circle.Size = ud2o()
circle.Position = ud2s(0.5, 0.5)
circle.Parent = object
local rounding = new('UICorner')
rounding.CornerRadius = ud(1)
rounding.Parent = circle
local abssz = object.AbsoluteSize
local size = max(abssz.X, abssz.Y) * 5/3
TweenSize(circle, ud2o(size, size), "Out", "Quart", 0.4)
service.TweenService:Create(circle, ti(0.4, Enum.EasingStyle.Quart, Enum.EasingDirection.In), {BackgroundTransparency = 1}):Play()
service.Debris:AddItem(circle, 0.4)
end
local function initGui(self,frame)
local checkbox = frame or create({
{1,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="Checkbox",Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,16,0,16),}},
{2,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ripples",Parent={1},Size=UDim2.new(1,0,1,0),}},
{3,"Frame",{BackgroundColor3=Color3.new(0.10196078568697,0.10196078568697,0.10196078568697),BorderSizePixel=0,Name="outline",Parent={1},Size=UDim2.new(0,16,0,16),}},
{4,"Frame",{BackgroundColor3=Color3.new(0.14117647707462,0.14117647707462,0.14117647707462),BorderSizePixel=0,Name="filler",Parent={3},Position=UDim2.new(0,1,0,1),Size=UDim2.new(0,14,0,14),}},
{5,"Frame",{BackgroundColor3=Color3.new(0.90196084976196,0.90196084976196,0.90196084976196),BorderSizePixel=0,Name="top",Parent={4},Size=UDim2.new(0,16,0,0),}},
{6,"Frame",{AnchorPoint=Vector2.new(0,1),BackgroundColor3=Color3.new(0.90196084976196,0.90196084976196,0.90196084976196),BorderSizePixel=0,Name="bottom",Parent={4},Position=UDim2.new(0,0,0,14),Size=UDim2.new(0,16,0,0),}},
{7,"Frame",{BackgroundColor3=Color3.new(0.90196084976196,0.90196084976196,0.90196084976196),BorderSizePixel=0,Name="left",Parent={4},Size=UDim2.new(0,0,0,16),}},
{8,"Frame",{AnchorPoint=Vector2.new(1,0),BackgroundColor3=Color3.new(0.90196084976196,0.90196084976196,0.90196084976196),BorderSizePixel=0,Name="right",Parent={4},Position=UDim2.new(0,14,0,0),Size=UDim2.new(0,0,0,16),}},
{9,"Frame",{AnchorPoint=Vector2.new(0.5,0.5),BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,ClipsDescendants=true,Name="checkmark",Parent={4},Position=UDim2.new(0.5,0,0.5,0),Size=UDim2.new(0,0,0,20),}},
{10,"ImageLabel",{AnchorPoint=Vector2.new(0.5,0.5),BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Image="rbxassetid://6234266378",Parent={9},Position=UDim2.new(0.5,0,0.5,0),ScaleType=3,Size=UDim2.new(0,15,0,11),}},
{11,"ImageLabel",{AnchorPoint=Vector2.new(0.5,0.5),BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://6401617475",ImageColor3=Color3.new(0.20784313976765,0.69803923368454,0.98431372642517),Name="checkmark2",Parent={4},Position=UDim2.new(0.5,0,0.5,0),Size=UDim2.new(0,12,0,12),Visible=false,}},
{12,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://6425281788",ImageTransparency=0.20000000298023,Name="middle",Parent={4},ScaleType=2,Size=UDim2.new(1,0,1,0),TileSize=UDim2.new(0,2,0,2),Visible=false,}},
{13,"UICorner",{CornerRadius=UDim.new(0,2),Parent={3},}},
})
local outline = checkbox.outline
local filler = outline.filler
local checkmark = filler.checkmark
local ripples_container = checkbox.ripples
-- walls
local top, bottom, left, right = filler.top, filler.bottom, filler.left, filler.right
self.Gui = checkbox
self.GuiElems = {
Top = top,
Bottom = bottom,
Left = left,
Right = right,
Outline = outline,
Filler = filler,
Checkmark = checkmark,
Checkmark2 = filler.checkmark2,
Middle = filler.middle
}
checkbox.InputBegan:Connect(function(i)
if i.UserInputType == Enum.UserInputType.MouseButton1 then
local release
release = service.UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
release:Disconnect()
if Lib.CheckMouseInGui(checkbox) then
if self.Style == 0 then
ripple(ripples_container, self.Disabled and self.Colors.Disabled or self.Colors.Primary)
end
if not self.Disabled then
self:SetState(not self.Toggled,true)
else
self:Paint()
end
self.OnInput:Fire()
end
end
end)
end
end)
self:Paint()
end
funcs.Collapse = function(self,anim)
local guiElems = self.GuiElems
if anim then
TweenSize(guiElems.Top, ud2o(14, 14), "In", "Quart", 4/15, true)
TweenSize(guiElems.Bottom, ud2o(14, 14), "In", "Quart", 4/15, true)
TweenSize(guiElems.Left, ud2o(14, 14), "In", "Quart", 4/15, true)
TweenSize(guiElems.Right, ud2o(14, 14), "In", "Quart", 4/15, true)
else
guiElems.Top.Size = ud2o(14, 14)
guiElems.Bottom.Size = ud2o(14, 14)
guiElems.Left.Size = ud2o(14, 14)
guiElems.Right.Size = ud2o(14, 14)
end
end
funcs.Expand = function(self,anim)
local guiElems = self.GuiElems
if anim then
TweenSize(guiElems.Top, ud2o(14, 0), "InOut", "Quart", 4/15, true)
TweenSize(guiElems.Bottom, ud2o(14, 0), "InOut", "Quart", 4/15, true)
TweenSize(guiElems.Left, ud2o(0, 14), "InOut", "Quart", 4/15, true)
TweenSize(guiElems.Right, ud2o(0, 14), "InOut", "Quart", 4/15, true)
else
guiElems.Top.Size = ud2o(14, 0)
guiElems.Bottom.Size = ud2o(14, 0)
guiElems.Left.Size = ud2o(0, 14)
guiElems.Right.Size = ud2o(0, 14)
end
end
funcs.Paint = function(self)
local guiElems = self.GuiElems
if self.Style == 0 then
local color_base = self.Disabled and self.Colors.Disabled
guiElems.Outline.BackgroundColor3 = color_base or (self.Toggled and self.Colors.Primary) or self.Colors.Secondary
local walls_color = color_base or self.Colors.Primary
guiElems.Top.BackgroundColor3 = walls_color
guiElems.Bottom.BackgroundColor3 = walls_color
guiElems.Left.BackgroundColor3 = walls_color
guiElems.Right.BackgroundColor3 = walls_color
else
guiElems.Outline.BackgroundColor3 = self.Disabled and self.Colors.Disabled or self.Colors.Secondary
guiElems.Filler.BackgroundColor3 = self.Disabled and self.Colors.DisabledBackground or self.Colors.Background
guiElems.Checkmark2.ImageColor3 = self.Disabled and self.Colors.DisabledCheck or self.Colors.Primary
end
end
funcs.SetState = function(self,val,anim)
self.Toggled = val
if self.OutlineColorTween then self.OutlineColorTween:Cancel() end
local setStateTime = tick()
self.LastSetStateTime = setStateTime
if self.Toggled then
if self.Style == 0 then
if anim then
self.OutlineColorTween = service.TweenService:Create(self.GuiElems.Outline, ti(4/15, Enum.EasingStyle.Circular, Enum.EasingDirection.Out), {BackgroundColor3 = self.Colors.Primary})
self.OutlineColorTween:Play()
delay(0.15, function()
if setStateTime ~= self.LastSetStateTime then return end
self:Paint()
TweenSize(self.GuiElems.Checkmark, ud2o(14, 20), "Out", "Bounce", 2/15, true)
end)
else
self.GuiElems.Outline.BackgroundColor3 = self.Colors.Primary
self:Paint()
self.GuiElems.Checkmark.Size = ud2o(14, 20)
end
self:Collapse(anim)
else
self:Paint()
self.GuiElems.Checkmark2.Visible = true
self.GuiElems.Middle.Visible = false
end
else
if self.Style == 0 then
if anim then
self.OutlineColorTween = service.TweenService:Create(self.GuiElems.Outline, ti(4/15, Enum.EasingStyle.Circular, Enum.EasingDirection.In), {BackgroundColor3 = self.Colors.Secondary})
self.OutlineColorTween:Play()
delay(0.15, function()
if setStateTime ~= self.LastSetStateTime then return end
self:Paint()
TweenSize(self.GuiElems.Checkmark, ud2o(0, 20), "Out", "Quad", 1/15, true)
end)
else
self.GuiElems.Outline.BackgroundColor3 = self.Colors.Secondary
self:Paint()
self.GuiElems.Checkmark.Size = ud2o(0, 20)
end
self:Expand(anim)
else
self:Paint()
self.GuiElems.Checkmark2.Visible = false
self.GuiElems.Middle.Visible = self.Toggled == nil
end
end
end
local mt = {__index = funcs}
local function new(style)
local obj = setmetatable({
Toggled = false,
Disabled = false,
OnInput = Lib.Signal.new(),
Style = style or 0,
Colors = {
Background = c3(36,36,36),
Primary = c3(49,176,230),
Secondary = c3(25,25,25),
Disabled = c3(64,64,64),
DisabledBackground = c3(52,52,52),
DisabledCheck = c3(80,80,80)
}
},mt)
initGui(obj)
return obj
end
local function fromFrame(frame)
local obj = setmetatable({
Toggled = false,
Disabled = false,
Colors = {
Background = c3(36,36,36),
Primary = c3(49,176,230),
Secondary = c3(25,25,25),
Disabled = c3(64,64,64),
DisabledBackground = c3(52,52,52)
}
},mt)
initGui(obj,frame)
return obj
end
return {new = new, fromFrame}
end)()
Lib.BrickColorPicker = (function()
local funcs = {}
local paletteCount = 0
local mouse = service.Players.LocalPlayer:GetMouse()
local hexStartX = 4
local hexSizeX = 27
local hexTriangleStart = 1
local hexTriangleSize = 8
local bottomColors = {
Color3.fromRGB(17,17,17),
Color3.fromRGB(99,95,98),
Color3.fromRGB(163,162,165),
Color3.fromRGB(205,205,205),
Color3.fromRGB(223,223,222),
Color3.fromRGB(237,234,234),
Color3.fromRGB(27,42,53),
Color3.fromRGB(91,93,105),
Color3.fromRGB(159,161,172),
Color3.fromRGB(202,203,209),
Color3.fromRGB(231,231,236),
Color3.fromRGB(248,248,248)
}
local function isMouseInHexagon(hex)
local relativeX = mouse.X - hex.AbsolutePosition.X
local relativeY = mouse.Y - hex.AbsolutePosition.Y
if relativeX >= hexStartX and relativeX < hexStartX + hexSizeX then
relativeX = relativeX - 4
local relativeWidth = (13-math.min(relativeX,26 - relativeX))/13
if relativeY >= hexTriangleStart + hexTriangleSize*relativeWidth and relativeY < hex.AbsoluteSize.Y - hexTriangleStart - hexTriangleSize*relativeWidth then
return true
end
end
return false
end
local function hexInput(self,hex,color)
hex.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 and isMouseInHexagon(hex) then
self.OnSelect:Fire(color)
self:Close()
end
end)
hex.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement and isMouseInHexagon(hex) then
self.OnPreview:Fire(color)
end
end)
end
local function createGui(self)
local gui = create({
{1,"ScreenGui",{Name="BrickColor",}},
{2,"Frame",{Active=true,BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderColor3=Color3.new(0.1294117718935,0.1294117718935,0.1294117718935),Parent={1},Position=UDim2.new(0.40000000596046,0,0.40000000596046,0),Size=UDim2.new(0,337,0,380),}},
{3,"TextButton",{BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),BorderSizePixel=0,Font=3,Name="MoreColors",Parent={2},Position=UDim2.new(0,5,1,-30),Size=UDim2.new(1,-10,0,25),Text="More Colors",TextColor3=Color3.new(1,1,1),TextSize=14,}},
{4,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Image="rbxassetid://1281023007",ImageColor3=Color3.new(0.33333334326744,0.33333334326744,0.49803924560547),Name="Hex",Parent={2},Size=UDim2.new(0,35,0,35),Visible=false,}},
})
local colorFrame = gui.Frame
local hex = colorFrame.Hex
for row = 1,13 do
local columns = math.min(row,14-row)+6
for column = 1,columns do
local nextColor = BrickColor.palette(paletteCount).Color
local newHex = hex:Clone()
newHex.Position = UDim2.new(0, (column-1)*25-(columns-7)*13+3*26 + 1, 0, (row-1)*23 + 4)
newHex.ImageColor3 = nextColor
newHex.Visible = true
hexInput(self,newHex,nextColor)
newHex.Parent = colorFrame
paletteCount = paletteCount + 1
end
end
for column = 1,12 do
local nextColor = bottomColors[column]
local newHex = hex:Clone()
newHex.Position = UDim2.new(0, (column-1)*25-(12-7)*13+3*26 + 3, 0, 308)
newHex.ImageColor3 = nextColor
newHex.Visible = true
hexInput(self,newHex,nextColor)
newHex.Parent = colorFrame
paletteCount = paletteCount + 1
end
colorFrame.MoreColors.MouseButton1Click:Connect(function()
self.OnMoreColors:Fire()
self:Close()
end)
self.Gui = gui
end
funcs.SetMoreColorsVisible = function(self,vis)
local colorFrame = self.Gui.Frame
colorFrame.Size = UDim2.new(0,337,0,380 - (not vis and 33 or 0))
colorFrame.MoreColors.Visible = vis
end
funcs.Show = function(self,x,y,prevColor)
self.PrevColor = prevColor or self.PrevColor
local reverseY = false
local x,y = x or mouse.X, y or mouse.Y
local maxX,maxY = mouse.ViewSizeX,mouse.ViewSizeY
Lib.ShowGui(self.Gui)
local sizeX,sizeY = self.Gui.Frame.AbsoluteSize.X,self.Gui.Frame.AbsoluteSize.Y
if x + sizeX > maxX then x = self.ReverseX and x - sizeX or maxX - sizeX end
if y + sizeY > maxY then reverseY = true end
local closable = false
if self.CloseEvent then self.CloseEvent:Disconnect() end
self.CloseEvent = service.UserInputService.InputBegan:Connect(function(input)
if not closable or input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
if not Lib.CheckMouseInGui(self.Gui.Frame) then
self.CloseEvent:Disconnect()
self:Close()
end
end)
if reverseY then
local newY = y - sizeY - (self.ReverseYOffset or 0)
y = newY >= 0 and newY or 0
end
self.Gui.Frame.Position = UDim2.new(0,x,0,y)
Lib.FastWait()
closable = true
end
funcs.Close = function(self)
self.Gui.Parent = nil
self.OnCancel:Fire()
end
local mt = {__index = funcs}
local function new()
local obj = setmetatable({
OnPreview = Lib.Signal.new(),
OnSelect = Lib.Signal.new(),
OnCancel = Lib.Signal.new(),
OnMoreColors = Lib.Signal.new(),
PrevColor = Color3.new(0,0,0)
},mt)
createGui(obj)
return obj
end
return {new = new}
end)()
Lib.ColorPicker = (function() -- TODO: Convert to newer class model
local funcs = {}
local function new()
local newMt = setmetatable({},{})
newMt.OnSelect = Lib.Signal.new()
newMt.OnCancel = Lib.Signal.new()
newMt.OnPreview = Lib.Signal.new()
local guiContents = create({
{1,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,ClipsDescendants=true,Name="Content",Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,1,-20),}},
{2,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="BasicColors",Parent={1},Position=UDim2.new(0,5,0,5),Size=UDim2.new(0,180,0,200),}},
{3,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={2},Position=UDim2.new(0,0,0,-5),Size=UDim2.new(1,0,0,26),Text="Basic Colors",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{4,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Blue",Parent={1},Position=UDim2.new(1,-63,0,255),Size=UDim2.new(0,52,0,16),}},
{5,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={4},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{6,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={5},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
{7,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={6},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{8,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={7},Size=UDim2.new(0,16,0,8),}},
{9,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={8},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
{10,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={8},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{11,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={8},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
{12,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={6},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{13,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={12},Size=UDim2.new(0,16,0,8),}},
{14,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={13},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
{15,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={13},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{16,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={13},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
{17,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={4},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Blue:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{18,"Frame",{BackgroundColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),BorderSizePixel=0,ClipsDescendants=true,Name="ColorSpaceFrame",Parent={1},Position=UDim2.new(1,-261,0,4),Size=UDim2.new(0,222,0,202),}},
{19,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),BorderSizePixel=0,Image="rbxassetid://1072518406",Name="ColorSpace",Parent={18},Position=UDim2.new(0,1,0,1),Size=UDim2.new(0,220,0,200),}},
{20,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="Scope",Parent={19},Position=UDim2.new(0,210,0,190),Size=UDim2.new(0,20,0,20),}},
{21,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Name="Line",Parent={20},Position=UDim2.new(0,9,0,0),Size=UDim2.new(0,2,0,20),}},
{22,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Name="Line",Parent={20},Position=UDim2.new(0,0,0,9),Size=UDim2.new(0,20,0,2),}},
{23,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="CustomColors",Parent={1},Position=UDim2.new(0,5,0,210),Size=UDim2.new(0,180,0,90),}},
{24,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={23},Size=UDim2.new(1,0,0,20),Text="Custom Colors (RC = Set)",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{25,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Green",Parent={1},Position=UDim2.new(1,-63,0,233),Size=UDim2.new(0,52,0,16),}},
{26,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={25},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{27,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={26},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
{28,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={27},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{29,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={28},Size=UDim2.new(0,16,0,8),}},
{30,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={29},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
{31,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={29},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{32,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={29},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
{33,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={27},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{34,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={33},Size=UDim2.new(0,16,0,8),}},
{35,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={34},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
{36,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={34},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{37,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={34},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
{38,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={25},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Green:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{39,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Hue",Parent={1},Position=UDim2.new(1,-180,0,211),Size=UDim2.new(0,52,0,16),}},
{40,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={39},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{41,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={40},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
{42,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={41},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{43,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={42},Size=UDim2.new(0,16,0,8),}},
{44,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={43},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
{45,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={43},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{46,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={43},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
{47,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={41},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{48,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={47},Size=UDim2.new(0,16,0,8),}},
{49,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={48},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
{50,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={48},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{51,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={48},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
{52,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={39},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Hue:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{53,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Name="Preview",Parent={1},Position=UDim2.new(1,-260,0,211),Size=UDim2.new(0,35,1,-245),}},
{54,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Red",Parent={1},Position=UDim2.new(1,-63,0,211),Size=UDim2.new(0,52,0,16),}},
{55,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={54},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{56,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={55},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
{57,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={56},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{58,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={57},Size=UDim2.new(0,16,0,8),}},
{59,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={58},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
{60,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={58},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{61,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={58},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
{62,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={56},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{63,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={62},Size=UDim2.new(0,16,0,8),}},
{64,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={63},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
{65,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={63},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{66,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={63},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
{67,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={54},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Red:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{68,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Sat",Parent={1},Position=UDim2.new(1,-180,0,233),Size=UDim2.new(0,52,0,16),}},
{69,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={68},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{70,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={69},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
{71,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={70},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{72,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={71},Size=UDim2.new(0,16,0,8),}},
{73,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={72},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
{74,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={72},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{75,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={72},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
{76,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={70},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{77,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={76},Size=UDim2.new(0,16,0,8),}},
{78,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={77},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
{79,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={77},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{80,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={77},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
{81,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={68},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Sat:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{82,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Val",Parent={1},Position=UDim2.new(1,-180,0,255),Size=UDim2.new(0,52,0,16),}},
{83,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Font=3,Name="Input",Parent={82},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,50,0,16),Text="255",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{84,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={83},Position=UDim2.new(1,-16,0,0),Size=UDim2.new(0,16,1,0),}},
{85,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Up",Parent={84},Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{86,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={85},Size=UDim2.new(0,16,0,8),}},
{87,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={86},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,1),}},
{88,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={86},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{89,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={86},Position=UDim2.new(0,6,0,5),Size=UDim2.new(0,5,0,1),}},
{90,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="Down",Parent={84},Position=UDim2.new(0,0,0,8),Size=UDim2.new(1,0,0,8),Text="",TextSize=14,}},
{91,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={90},Size=UDim2.new(0,16,0,8),}},
{92,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={91},Position=UDim2.new(0,8,0,5),Size=UDim2.new(0,1,0,1),}},
{93,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={91},Position=UDim2.new(0,7,0,4),Size=UDim2.new(0,3,0,1),}},
{94,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={91},Position=UDim2.new(0,6,0,3),Size=UDim2.new(0,5,0,1),}},
{95,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={82},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Val:",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{96,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Font=3,Name="Cancel",Parent={1},Position=UDim2.new(1,-105,1,-28),Size=UDim2.new(0,100,0,25),Text="Cancel",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
{97,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Font=3,Name="Ok",Parent={1},Position=UDim2.new(1,-210,1,-28),Size=UDim2.new(0,100,0,25),Text="OK",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
{98,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Image="rbxassetid://1072518502",Name="ColorStrip",Parent={1},Position=UDim2.new(1,-30,0,5),Size=UDim2.new(0,13,0,200),}},
{99,"Frame",{BackgroundColor3=Color3.new(0.3137255012989,0.3137255012989,0.3137255012989),BackgroundTransparency=1,BorderSizePixel=0,Name="ArrowFrame",Parent={1},Position=UDim2.new(1,-16,0,1),Size=UDim2.new(0,5,0,208),}},
{100,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={99},Position=UDim2.new(0,-2,0,-4),Size=UDim2.new(0,8,0,16),}},
{101,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Parent={100},Position=UDim2.new(0,2,0,8),Size=UDim2.new(0,1,0,1),}},
{102,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Parent={100},Position=UDim2.new(0,3,0,7),Size=UDim2.new(0,1,0,3),}},
{103,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Parent={100},Position=UDim2.new(0,4,0,6),Size=UDim2.new(0,1,0,5),}},
{104,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Parent={100},Position=UDim2.new(0,5,0,5),Size=UDim2.new(0,1,0,7),}},
{105,"Frame",{BackgroundColor3=Color3.new(0,0,0),BorderSizePixel=0,Parent={100},Position=UDim2.new(0,6,0,4),Size=UDim2.new(0,1,0,9),}},
})
local window = Lib.Window.new()
window.Resizable = false
window.Alignable = false
window:SetTitle("Color Picker")
window:Resize(450,330)
for i,v in pairs(guiContents:GetChildren()) do
v.Parent = window.GuiElems.Content
end
newMt.Window = window
newMt.Gui = window.Gui
local pickerGui = window.Gui.Main
local pickerTopBar = pickerGui.TopBar
local pickerFrame = pickerGui.Content
local colorSpace = pickerFrame.ColorSpaceFrame.ColorSpace
local colorStrip = pickerFrame.ColorStrip
local previewFrame = pickerFrame.Preview
local basicColorsFrame = pickerFrame.BasicColors
local customColorsFrame = pickerFrame.CustomColors
local okButton = pickerFrame.Ok
local cancelButton = pickerFrame.Cancel
local closeButton = pickerTopBar.Close
local colorScope = colorSpace.Scope
local colorArrow = pickerFrame.ArrowFrame.Arrow
local hueInput = pickerFrame.Hue.Input
local satInput = pickerFrame.Sat.Input
local valInput = pickerFrame.Val.Input
local redInput = pickerFrame.Red.Input
local greenInput = pickerFrame.Green.Input
local blueInput = pickerFrame.Blue.Input
local user = cloneref(game["Run Service"].Parent:GetService("UserInputService"))
local mouse = cloneref(game["Run Service"].Parent:GetService("Players")).LocalPlayer:GetMouse()
local hue,sat,val = 0,0,1
local red,green,blue = 1,1,1
local chosenColor = Color3.new(0,0,0)
local basicColors = {Color3.new(0,0,0),Color3.new(0.66666668653488,0,0),Color3.new(0,0.33333334326744,0),Color3.new(0.66666668653488,0.33333334326744,0),Color3.new(0,0.66666668653488,0),Color3.new(0.66666668653488,0.66666668653488,0),Color3.new(0,1,0),Color3.new(0.66666668653488,1,0),Color3.new(0,0,0.49803924560547),Color3.new(0.66666668653488,0,0.49803924560547),Color3.new(0,0.33333334326744,0.49803924560547),Color3.new(0.66666668653488,0.33333334326744,0.49803924560547),Color3.new(0,0.66666668653488,0.49803924560547),Color3.new(0.66666668653488,0.66666668653488,0.49803924560547),Color3.new(0,1,0.49803924560547),Color3.new(0.66666668653488,1,0.49803924560547),Color3.new(0,0,1),Color3.new(0.66666668653488,0,1),Color3.new(0,0.33333334326744,1),Color3.new(0.66666668653488,0.33333334326744,1),Color3.new(0,0.66666668653488,1),Color3.new(0.66666668653488,0.66666668653488,1),Color3.new(0,1,1),Color3.new(0.66666668653488,1,1),Color3.new(0.33333334326744,0,0),Color3.new(1,0,0),Color3.new(0.33333334326744,0.33333334326744,0),Color3.new(1,0.33333334326744,0),Color3.new(0.33333334326744,0.66666668653488,0),Color3.new(1,0.66666668653488,0),Color3.new(0.33333334326744,1,0),Color3.new(1,1,0),Color3.new(0.33333334326744,0,0.49803924560547),Color3.new(1,0,0.49803924560547),Color3.new(0.33333334326744,0.33333334326744,0.49803924560547),Color3.new(1,0.33333334326744,0.49803924560547),Color3.new(0.33333334326744,0.66666668653488,0.49803924560547),Color3.new(1,0.66666668653488,0.49803924560547),Color3.new(0.33333334326744,1,0.49803924560547),Color3.new(1,1,0.49803924560547),Color3.new(0.33333334326744,0,1),Color3.new(1,0,1),Color3.new(0.33333334326744,0.33333334326744,1),Color3.new(1,0.33333334326744,1),Color3.new(0.33333334326744,0.66666668653488,1),Color3.new(1,0.66666668653488,1),Color3.new(0.33333334326744,1,1),Color3.new(1,1,1)}
local customColors = {}
local function updateColor(noupdate)
local relativeX,relativeY,relativeStripY = 219 - hue*219, 199 - sat*199, 199 - val*199
local hsvColor = Color3.fromHSV(hue,sat,val)
if noupdate == 2 or not noupdate then
hueInput.Text = tostring(math.ceil(359*hue))
satInput.Text = tostring(math.ceil(255*sat))
valInput.Text = tostring(math.floor(255*val))
end
if noupdate == 1 or not noupdate then
redInput.Text = tostring(math.floor(255*red))
greenInput.Text = tostring(math.floor(255*green))
blueInput.Text = tostring(math.floor(255*blue))
end
chosenColor = Color3.new(red,green,blue)
colorScope.Position = UDim2.new(0,relativeX-9,0,relativeY-9)
colorStrip.ImageColor3 = Color3.fromHSV(hue,sat,1)
colorArrow.Position = UDim2.new(0,-2,0,relativeStripY-4)
previewFrame.BackgroundColor3 = chosenColor
newMt.Color = chosenColor
newMt.OnPreview:Fire(chosenColor)
end
local function colorSpaceInput()
local relativeX = mouse.X - colorSpace.AbsolutePosition.X
local relativeY = mouse.Y - colorSpace.AbsolutePosition.Y
if relativeX < 0 then relativeX = 0 elseif relativeX > 219 then relativeX = 219 end
if relativeY < 0 then relativeY = 0 elseif relativeY > 199 then relativeY = 199 end
hue = (219 - relativeX)/219
sat = (199 - relativeY)/199
local hsvColor = Color3.fromHSV(hue,sat,val)
red,green,blue = hsvColor.r,hsvColor.g,hsvColor.b
updateColor()
end
local function colorStripInput()
local relativeY = mouse.Y - colorStrip.AbsolutePosition.Y
if relativeY < 0 then relativeY = 0 elseif relativeY > 199 then relativeY = 199 end
val = (199 - relativeY)/199
local hsvColor = Color3.fromHSV(hue,sat,val)
red,green,blue = hsvColor.r,hsvColor.g,hsvColor.b
updateColor()
end
local function hookButtons(frame,func)
frame.ArrowFrame.Up.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
frame.ArrowFrame.Up.BackgroundTransparency = 0.5
elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
local releaseEvent,runEvent
local startTime = tick()
local pressing = true
local startNum = tonumber(frame.Text)
if not startNum then return end
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
releaseEvent:Disconnect()
pressing = false
end)
startNum = startNum + 1
func(startNum)
while pressing do
if tick()-startTime > 0.3 then
startNum = startNum + 1
func(startNum)
end
wait(0.1)
end
end
end)
frame.ArrowFrame.Up.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
frame.ArrowFrame.Up.BackgroundTransparency = 1
end
end)
frame.ArrowFrame.Down.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
frame.ArrowFrame.Down.BackgroundTransparency = 0.5
elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
local releaseEvent,runEvent
local startTime = tick()
local pressing = true
local startNum = tonumber(frame.Text)
if not startNum then return end
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
releaseEvent:Disconnect()
pressing = false
end)
startNum = startNum - 1
func(startNum)
while pressing do
if tick()-startTime > 0.3 then
startNum = startNum - 1
func(startNum)
end
wait(0.1)
end
end
end)
frame.ArrowFrame.Down.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
frame.ArrowFrame.Down.BackgroundTransparency = 1
end
end)
end
colorSpace.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
local releaseEvent,mouseEvent
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
releaseEvent:Disconnect()
mouseEvent:Disconnect()
end)
mouseEvent = user.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
colorSpaceInput()
end
end)
colorSpaceInput()
end
end)
colorStrip.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
local releaseEvent,mouseEvent
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
releaseEvent:Disconnect()
mouseEvent:Disconnect()
end)
mouseEvent = user.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
colorStripInput()
end
end)
colorStripInput()
end
end)
local function updateHue(str)
local num = tonumber(str)
if num then
hue = math.clamp(math.floor(num),0,359)/359
local hsvColor = Color3.fromHSV(hue,sat,val)
red,green,blue = hsvColor.r,hsvColor.g,hsvColor.b
hueInput.Text = tostring(hue*359)
updateColor(1)
end
end
hueInput.FocusLost:Connect(function() updateHue(hueInput.Text) end) hookButtons(hueInput,updateHue)
local function updateSat(str)
local num = tonumber(str)
if num then
sat = math.clamp(math.floor(num),0,255)/255
local hsvColor = Color3.fromHSV(hue,sat,val)
red,green,blue = hsvColor.r,hsvColor.g,hsvColor.b
satInput.Text = tostring(sat*255)
updateColor(1)
end
end
satInput.FocusLost:Connect(function() updateSat(satInput.Text) end) hookButtons(satInput,updateSat)
local function updateVal(str)
local num = tonumber(str)
if num then
val = math.clamp(math.floor(num),0,255)/255
local hsvColor = Color3.fromHSV(hue,sat,val)
red,green,blue = hsvColor.r,hsvColor.g,hsvColor.b
valInput.Text = tostring(val*255)
updateColor(1)
end
end
valInput.FocusLost:Connect(function() updateVal(valInput.Text) end) hookButtons(valInput,updateVal)
local function updateRed(str)
local num = tonumber(str)
if num then
red = math.clamp(math.floor(num),0,255)/255
local newColor = Color3.new(red,green,blue)
hue,sat,val = Color3.toHSV(newColor)
redInput.Text = tostring(red*255)
updateColor(2)
end
end
redInput.FocusLost:Connect(function() updateRed(redInput.Text) end) hookButtons(redInput,updateRed)
local function updateGreen(str)
local num = tonumber(str)
if num then
green = math.clamp(math.floor(num),0,255)/255
local newColor = Color3.new(red,green,blue)
hue,sat,val = Color3.toHSV(newColor)
greenInput.Text = tostring(green*255)
updateColor(2)
end
end
greenInput.FocusLost:Connect(function() updateGreen(greenInput.Text) end) hookButtons(greenInput,updateGreen)
local function updateBlue(str)
local num = tonumber(str)
if num then
blue = math.clamp(math.floor(num),0,255)/255
local newColor = Color3.new(red,green,blue)
hue,sat,val = Color3.toHSV(newColor)
blueInput.Text = tostring(blue*255)
updateColor(2)
end
end
blueInput.FocusLost:Connect(function() updateBlue(blueInput.Text) end) hookButtons(blueInput,updateBlue)
local colorChoice = Instance.new("TextButton")
colorChoice.Name = "Choice"
colorChoice.Size = UDim2.new(0,25,0,18)
colorChoice.BorderColor3 = Color3.fromRGB(55,55,55)
colorChoice.Text = ""
colorChoice.AutoButtonColor = false
local row = 0
local column = 0
for i,v in pairs(basicColors) do
local newColor = colorChoice:Clone()
newColor.BackgroundColor3 = v
newColor.Position = UDim2.new(0,1 + 30*column,0,21 + 23*row)
newColor.MouseButton1Click:Connect(function()
red,green,blue = v.r,v.g,v.b
local newColor = Color3.new(red,green,blue)
hue,sat,val = Color3.toHSV(newColor)
updateColor()
end)
newColor.Parent = basicColorsFrame
column = column + 1
if column == 6 then row = row + 1 column = 0 end
end
row = 0
column = 0
for i = 1,12 do
local color = customColors[i] or Color3.new(0,0,0)
local newColor = colorChoice:Clone()
newColor.BackgroundColor3 = color
newColor.Position = UDim2.new(0,1 + 30*column,0,20 + 23*row)
newColor.MouseButton1Click:Connect(function()
local curColor = customColors[i] or Color3.new(0,0,0)
red,green,blue = curColor.r,curColor.g,curColor.b
hue,sat,val = Color3.toHSV(curColor)
updateColor()
end)
newColor.MouseButton2Click:Connect(function()
customColors[i] = chosenColor
newColor.BackgroundColor3 = chosenColor
end)
newColor.Parent = customColorsFrame
column = column + 1
if column == 6 then row = row + 1 column = 0 end
end
okButton.MouseButton1Click:Connect(function() newMt.OnSelect:Fire(chosenColor) window:Close() end)
okButton.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then okButton.BackgroundTransparency = 0.4 end end)
okButton.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then okButton.BackgroundTransparency = 0 end end)
cancelButton.MouseButton1Click:Connect(function() newMt.OnCancel:Fire() window:Close() end)
cancelButton.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then cancelButton.BackgroundTransparency = 0.4 end end)
cancelButton.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then cancelButton.BackgroundTransparency = 0 end end)
updateColor()
newMt.SetColor = function(self,color)
red,green,blue = color.r,color.g,color.b
hue,sat,val = Color3.toHSV(color)
updateColor()
end
newMt.Show = function(self)
self.Window:Show()
end
return newMt
end
return {new = new}
end)()
Lib.NumberSequenceEditor = (function()
local function new() -- TODO: Convert to newer class model
local newMt = setmetatable({},{})
newMt.OnSelect = Lib.Signal.new()
newMt.OnCancel = Lib.Signal.new()
newMt.OnPreview = Lib.Signal.new()
local guiContents = create({
{1,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,ClipsDescendants=true,Name="Content",Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,1,-20),}},
{2,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Time",Parent={1},Position=UDim2.new(0,40,0,210),Size=UDim2.new(0,60,0,20),}},
{3,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),ClipsDescendants=true,Font=3,Name="Input",Parent={2},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,58,0,20),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{4,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={2},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Time",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{5,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Font=3,Name="Close",Parent={1},Position=UDim2.new(1,-90,0,210),Size=UDim2.new(0,80,0,20),Text="Close",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
{6,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Font=3,Name="Reset",Parent={1},Position=UDim2.new(1,-180,0,210),Size=UDim2.new(0,80,0,20),Text="Reset",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
{7,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Font=3,Name="Delete",Parent={1},Position=UDim2.new(0,380,0,210),Size=UDim2.new(0,80,0,20),Text="Delete",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
{8,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Name="NumberLineOutlines",Parent={1},Position=UDim2.new(0,10,0,20),Size=UDim2.new(1,-20,0,170),}},
{9,"Frame",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),Name="NumberLine",Parent={1},Position=UDim2.new(0,10,0,20),Size=UDim2.new(1,-20,0,170),}},
{10,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Value",Parent={1},Position=UDim2.new(0,170,0,210),Size=UDim2.new(0,60,0,20),}},
{11,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={10},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Value",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{12,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),ClipsDescendants=true,Font=3,Name="Input",Parent={10},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,58,0,20),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{13,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Envelope",Parent={1},Position=UDim2.new(0,300,0,210),Size=UDim2.new(0,60,0,20),}},
{14,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),ClipsDescendants=true,Font=3,Name="Input",Parent={13},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,58,0,20),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{15,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={13},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Envelope",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
})
local window = Lib.Window.new()
window.Resizable = false
window:Resize(680,265)
window:SetTitle("NumberSequence Editor")
newMt.Window = window
newMt.Gui = window.Gui
for i,v in pairs(guiContents:GetChildren()) do
v.Parent = window.GuiElems.Content
end
local gui = window.Gui
local pickerGui = gui.Main
local pickerTopBar = pickerGui.TopBar
local pickerFrame = pickerGui.Content
local numberLine = pickerFrame.NumberLine
local numberLineOutlines = pickerFrame.NumberLineOutlines
local timeBox = pickerFrame.Time.Input
local valueBox = pickerFrame.Value.Input
local envelopeBox = pickerFrame.Envelope.Input
local deleteButton = pickerFrame.Delete
local resetButton = pickerFrame.Reset
local closeButton = pickerFrame.Close
local topClose = pickerTopBar.Close
local points = {{1,0,3},{8,0.05,1},{5,0.6,2},{4,0.7,4},{6,1,4}}
local lines = {}
local eLines = {}
local beginPoint = points[1]
local endPoint = points[#points]
local currentlySelected = nil
local currentPoint = nil
local resetSequence = nil
local user = cloneref(game["Run Service"].Parent:GetService("UserInputService"))
local mouse = cloneref(game["Run Service"].Parent:GetService("Players")).LocalPlayer:GetMouse()
for i = 2,10 do
local newLine = Instance.new("Frame")
newLine.BackgroundTransparency = 0.5
newLine.BackgroundColor3 = Color3.new(96/255,96/255,96/255)
newLine.BorderSizePixel = 0
newLine.Size = UDim2.new(0,1,1,0)
newLine.Position = UDim2.new((i-1)/(11-1),0,0,0)
newLine.Parent = numberLineOutlines
end
for i = 2,4 do
local newLine = Instance.new("Frame")
newLine.BackgroundTransparency = 0.5
newLine.BackgroundColor3 = Color3.new(96/255,96/255,96/255)
newLine.BorderSizePixel = 0
newLine.Size = UDim2.new(1,0,0,1)
newLine.Position = UDim2.new(0,0,(i-1)/(5-1),0)
newLine.Parent = numberLineOutlines
end
local lineTemp = Instance.new("Frame")
lineTemp.BackgroundColor3 = Color3.new(0,0,0)
lineTemp.BorderSizePixel = 0
lineTemp.Size = UDim2.new(0,1,0,1)
local sequenceLine = Instance.new("Frame")
sequenceLine.BackgroundColor3 = Color3.new(0,0,0)
sequenceLine.BorderSizePixel = 0
sequenceLine.Size = UDim2.new(0,1,0,0)
for i = 1,numberLine.AbsoluteSize.X do
local line = sequenceLine:Clone()
eLines[i] = line
line.Name = "E"..tostring(i)
line.BackgroundTransparency = 0.5
line.BackgroundColor3 = Color3.new(199/255,44/255,28/255)
line.Position = UDim2.new(0,i-1,0,0)
line.Parent = numberLine
end
for i = 1,numberLine.AbsoluteSize.X do
local line = sequenceLine:Clone()
lines[i] = line
line.Name = tostring(i)
line.Position = UDim2.new(0,i-1,0,0)
line.Parent = numberLine
end
local envelopeDrag = Instance.new("Frame")
envelopeDrag.BackgroundTransparency = 1
envelopeDrag.BackgroundColor3 = Color3.new(0,0,0)
envelopeDrag.BorderSizePixel = 0
envelopeDrag.Size = UDim2.new(0,7,0,20)
envelopeDrag.Visible = false
envelopeDrag.ZIndex = 2
local envelopeDragLine = Instance.new("Frame",envelopeDrag)
envelopeDragLine.Name = "Line"
envelopeDragLine.BackgroundColor3 = Color3.new(0,0,0)
envelopeDragLine.BorderSizePixel = 0
envelopeDragLine.Position = UDim2.new(0,3,0,0)
envelopeDragLine.Size = UDim2.new(0,1,0,20)
envelopeDragLine.ZIndex = 2
local envelopeDragTop,envelopeDragBottom = envelopeDrag:Clone(),envelopeDrag:Clone()
envelopeDragTop.Parent = numberLine
envelopeDragBottom.Parent = numberLine
local function buildSequence()
local newPoints = {}
for i,v in pairs(points) do
table.insert(newPoints,NumberSequenceKeypoint.new(v[2],v[1],v[3]))
end
newMt.Sequence = NumberSequence.new(newPoints)
newMt.OnSelect:Fire(newMt.Sequence)
end
local function round(num,places)
local multi = 10^places
return math.floor(num*multi + 0.5)/multi
end
local function updateInputs(point)
if point then
currentPoint = point
local rawT,rawV,rawE = point[2],point[1],point[3]
timeBox.Text = round(rawT,(rawT < 0.01 and 5) or (rawT < 0.1 and 4) or 3)
valueBox.Text = round(rawV,(rawV < 0.01 and 5) or (rawV < 0.1 and 4) or (rawV < 1 and 3) or 2)
envelopeBox.Text = round(rawE,(rawE < 0.01 and 5) or (rawE < 0.1 and 4) or (rawV < 1 and 3) or 2)
local envelopeDistance = numberLine.AbsoluteSize.Y*(point[3]/10)
envelopeDragTop.Position = UDim2.new(0,point[4].Position.X.Offset-1,0,point[4].Position.Y.Offset-envelopeDistance-17)
envelopeDragTop.Visible = true
envelopeDragBottom.Position = UDim2.new(0,point[4].Position.X.Offset-1,0,point[4].Position.Y.Offset+envelopeDistance+2)
envelopeDragBottom.Visible = true
end
end
envelopeDragTop.InputBegan:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 or not currentPoint or Lib.CheckMouseInGui(currentPoint[4].Select) then return end
local mouseEvent,releaseEvent
local maxSize = numberLine.AbsoluteSize.Y
local mouseDelta = math.abs(envelopeDragTop.AbsolutePosition.Y - mouse.Y)
envelopeDragTop.Line.Position = UDim2.new(0,2,0,0)
envelopeDragTop.Line.Size = UDim2.new(0,3,0,20)
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
mouseEvent:Disconnect()
releaseEvent:Disconnect()
envelopeDragTop.Line.Position = UDim2.new(0,3,0,0)
envelopeDragTop.Line.Size = UDim2.new(0,1,0,20)
end)
mouseEvent = user.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
local topDiff = (currentPoint[4].AbsolutePosition.Y+2)-(mouse.Y-mouseDelta)-19
local newEnvelope = 10*(math.max(topDiff,0)/maxSize)
local maxEnvelope = math.min(currentPoint[1],10-currentPoint[1])
currentPoint[3] = math.min(newEnvelope,maxEnvelope)
newMt:Redraw()
buildSequence()
updateInputs(currentPoint)
end
end)
end)
envelopeDragBottom.InputBegan:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 or not currentPoint or Lib.CheckMouseInGui(currentPoint[4].Select) then return end
local mouseEvent,releaseEvent
local maxSize = numberLine.AbsoluteSize.Y
local mouseDelta = math.abs(envelopeDragBottom.AbsolutePosition.Y - mouse.Y)
envelopeDragBottom.Line.Position = UDim2.new(0,2,0,0)
envelopeDragBottom.Line.Size = UDim2.new(0,3,0,20)
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
mouseEvent:Disconnect()
releaseEvent:Disconnect()
envelopeDragBottom.Line.Position = UDim2.new(0,3,0,0)
envelopeDragBottom.Line.Size = UDim2.new(0,1,0,20)
end)
mouseEvent = user.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
local bottomDiff = (mouse.Y+(20-mouseDelta))-(currentPoint[4].AbsolutePosition.Y+2)-19
local newEnvelope = 10*(math.max(bottomDiff,0)/maxSize)
local maxEnvelope = math.min(currentPoint[1],10-currentPoint[1])
currentPoint[3] = math.min(newEnvelope,maxEnvelope)
newMt:Redraw()
buildSequence()
updateInputs(currentPoint)
end
end)
end)
local function placePoint(point)
local newPoint = Instance.new("Frame")
newPoint.Name = "Point"
newPoint.BorderSizePixel = 0
newPoint.Size = UDim2.new(0,5,0,5)
newPoint.Position = UDim2.new(0,math.floor((numberLine.AbsoluteSize.X-1) * point[2])-2,0,numberLine.AbsoluteSize.Y*(10-point[1])/10-2)
newPoint.BackgroundColor3 = Color3.new(0,0,0)
local newSelect = Instance.new("Frame")
newSelect.Name = "Select"
newSelect.BackgroundTransparency = 1
newSelect.BackgroundColor3 = Color3.new(199/255,44/255,28/255)
newSelect.Position = UDim2.new(0,-2,0,-2)
newSelect.Size = UDim2.new(0,9,0,9)
newSelect.Parent = newPoint
newPoint.Parent = numberLine
newSelect.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
for i,v in pairs(points) do v[4].Select.BackgroundTransparency = 1 end
newSelect.BackgroundTransparency = 0
updateInputs(point)
end
if input.UserInputType == Enum.UserInputType.MouseButton1 and not currentlySelected then
currentPoint = point
local mouseEvent,releaseEvent
currentlySelected = true
newSelect.BackgroundColor3 = Color3.new(249/255,191/255,59/255)
local oldEnvelope = point[3]
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
mouseEvent:Disconnect()
releaseEvent:Disconnect()
currentlySelected = nil
newSelect.BackgroundColor3 = Color3.new(199/255,44/255,28/255)
end)
mouseEvent = user.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
local maxX = numberLine.AbsoluteSize.X-1
local relativeX = mouse.X - numberLine.AbsolutePosition.X
if relativeX < 0 then relativeX = 0 end
if relativeX > maxX then relativeX = maxX end
local maxY = numberLine.AbsoluteSize.Y-1
local relativeY = mouse.Y - numberLine.AbsolutePosition.Y
if relativeY < 0 then relativeY = 0 end
if relativeY > maxY then relativeY = maxY end
if point ~= beginPoint and point ~= endPoint then
point[2] = relativeX/maxX
end
point[1] = 10-(relativeY/maxY)*10
local maxEnvelope = math.min(point[1],10-point[1])
point[3] = math.min(oldEnvelope,maxEnvelope)
newMt:Redraw()
updateInputs(point)
for i,v in pairs(points) do v[4].Select.BackgroundTransparency = 1 end
newSelect.BackgroundTransparency = 0
buildSequence()
end
end)
end
end)
return newPoint
end
local function placePoints()
for i,v in pairs(points) do
v[4] = placePoint(v)
end
end
local function redraw(self)
local numberLineSize = numberLine.AbsoluteSize
table.sort(points,function(a,b) return a[2] < b[2] end)
for i,v in pairs(points) do
v[4].Position = UDim2.new(0,math.floor((numberLineSize.X-1) * v[2])-2,0,(numberLineSize.Y-1)*(10-v[1])/10-2)
end
lines[1].Size = UDim2.new(0,1,0,0)
for i = 1,#points-1 do
local fromPoint = points[i]
local toPoint = points[i+1]
local deltaY = toPoint[4].Position.Y.Offset-fromPoint[4].Position.Y.Offset
local deltaX = toPoint[4].Position.X.Offset-fromPoint[4].Position.X.Offset
local slope = deltaY/deltaX
local fromEnvelope = fromPoint[3]
local nextEnvelope = toPoint[3]
local currentRise = math.abs(slope)
local totalRise = 0
local maxRise = math.abs(toPoint[4].Position.Y.Offset-fromPoint[4].Position.Y.Offset)
for lineCount = math.min(fromPoint[4].Position.X.Offset+1,toPoint[4].Position.X.Offset),toPoint[4].Position.X.Offset do
if deltaX == 0 and deltaY == 0 then return end
local riseNow = math.floor(currentRise)
local line = lines[lineCount+3]
if line then
if totalRise+riseNow > maxRise then riseNow = maxRise-totalRise end
if math.sign(slope) == -1 then
line.Position = UDim2.new(0,lineCount+2,0,fromPoint[4].Position.Y.Offset + -(totalRise+riseNow)+2)
else
line.Position = UDim2.new(0,lineCount+2,0,fromPoint[4].Position.Y.Offset + totalRise+2)
end
line.Size = UDim2.new(0,1,0,math.max(riseNow,1))
end
totalRise = totalRise + riseNow
currentRise = currentRise - riseNow + math.abs(slope)
local envPercent = (lineCount-fromPoint[4].Position.X.Offset)/(toPoint[4].Position.X.Offset-fromPoint[4].Position.X.Offset)
local envLerp = fromEnvelope+(nextEnvelope-fromEnvelope)*envPercent
local relativeSize = (envLerp/10)*numberLineSize.Y
local line = eLines[lineCount + 3]
if line then
line.Position = UDim2.new(0,lineCount+2,0,lines[lineCount+3].Position.Y.Offset-math.floor(relativeSize))
line.Size = UDim2.new(0,1,0,math.floor(relativeSize*2))
end
end
end
end
newMt.Redraw = redraw
local function loadSequence(self,seq)
resetSequence = seq
for i,v in pairs(points) do if v[4] then v[4]:Destroy() end end
points = {}
for i,v in pairs(seq.Keypoints) do
local maxEnvelope = math.min(v.Value,10-v.Value)
local newPoint = {v.Value,v.Time,math.min(v.Envelope,maxEnvelope)}
newPoint[4] = placePoint(newPoint)
table.insert(points,newPoint)
end
beginPoint = points[1]
endPoint = points[#points]
currentlySelected = nil
redraw()
envelopeDragTop.Visible = false
envelopeDragBottom.Visible = false
end
newMt.SetSequence = loadSequence
timeBox.FocusLost:Connect(function()
local point = currentPoint
local num = tonumber(timeBox.Text)
if point and num and point ~= beginPoint and point ~= endPoint then
num = math.clamp(num,0,1)
point[2] = num
redraw()
buildSequence()
updateInputs(point)
end
end)
valueBox.FocusLost:Connect(function()
local point = currentPoint
local num = tonumber(valueBox.Text)
if point and num then
local oldEnvelope = point[3]
num = math.clamp(num,0,10)
point[1] = num
local maxEnvelope = math.min(point[1],10-point[1])
point[3] = math.min(oldEnvelope,maxEnvelope)
redraw()
buildSequence()
updateInputs(point)
end
end)
envelopeBox.FocusLost:Connect(function()
local point = currentPoint
local num = tonumber(envelopeBox.Text)
if point and num then
num = math.clamp(num,0,5)
local maxEnvelope = math.min(point[1],10-point[1])
point[3] = math.min(num,maxEnvelope)
redraw()
buildSequence()
updateInputs(point)
end
end)
local function buttonAnimations(button,inverse)
button.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then button.BackgroundTransparency = (inverse and 0.5 or 0.4) end end)
button.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then button.BackgroundTransparency = (inverse and 1 or 0) end end)
end
numberLine.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 and #points < 20 then
if Lib.CheckMouseInGui(envelopeDragTop) or Lib.CheckMouseInGui(envelopeDragBottom) then return end
for i,v in pairs(points) do
if Lib.CheckMouseInGui(v[4].Select) then return end
end
local maxX = numberLine.AbsoluteSize.X-1
local relativeX = mouse.X - numberLine.AbsolutePosition.X
if relativeX < 0 then relativeX = 0 end
if relativeX > maxX then relativeX = maxX end
local maxY = numberLine.AbsoluteSize.Y-1
local relativeY = mouse.Y - numberLine.AbsolutePosition.Y
if relativeY < 0 then relativeY = 0 end
if relativeY > maxY then relativeY = maxY end
local raw = relativeX/maxX
local newPoint = {10-(relativeY/maxY)*10,raw,0}
newPoint[4] = placePoint(newPoint)
table.insert(points,newPoint)
redraw()
buildSequence()
end
end)
deleteButton.MouseButton1Click:Connect(function()
if currentPoint and currentPoint ~= beginPoint and currentPoint ~= endPoint then
for i,v in pairs(points) do
if v == currentPoint then
v[4]:Destroy()
table.remove(points,i)
break
end
end
currentlySelected = nil
redraw()
buildSequence()
updateInputs(points[1])
end
end)
resetButton.MouseButton1Click:Connect(function()
if resetSequence then
newMt:SetSequence(resetSequence)
buildSequence()
end
end)
closeButton.MouseButton1Click:Connect(function()
window:Close()
end)
buttonAnimations(deleteButton)
buttonAnimations(resetButton)
buttonAnimations(closeButton)
placePoints()
redraw()
newMt.Show = function(self)
window:Show()
end
return newMt
end
return {new = new}
end)()
Lib.ColorSequenceEditor = (function() -- TODO: Convert to newer class model
local function new()
local newMt = setmetatable({},{})
newMt.OnSelect = Lib.Signal.new()
newMt.OnCancel = Lib.Signal.new()
newMt.OnPreview = Lib.Signal.new()
newMt.OnPickColor = Lib.Signal.new()
local guiContents = create({
{1,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,ClipsDescendants=true,Name="Content",Position=UDim2.new(0,0,0,20),Size=UDim2.new(1,0,1,-20),}},
{2,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Name="ColorLine",Parent={1},Position=UDim2.new(0,10,0,5),Size=UDim2.new(1,-20,0,70),}},
{3,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderSizePixel=0,Name="Gradient",Parent={2},Size=UDim2.new(1,0,1,0),}},
{4,"UIGradient",{Parent={3},}},
{5,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Name="Arrows",Parent={1},Position=UDim2.new(0,1,0,73),Size=UDim2.new(1,-2,0,16),}},
{6,"Frame",{BackgroundColor3=Color3.new(0,0,0),BackgroundTransparency=0.5,BorderSizePixel=0,Name="Cursor",Parent={1},Position=UDim2.new(0,10,0,0),Size=UDim2.new(0,1,0,80),}},
{7,"Frame",{BackgroundColor3=Color3.new(0.14901961386204,0.14901961386204,0.14901961386204),BorderColor3=Color3.new(0.12549020349979,0.12549020349979,0.12549020349979),Name="Time",Parent={1},Position=UDim2.new(0,40,0,95),Size=UDim2.new(0,100,0,20),}},
{8,"TextBox",{BackgroundColor3=Color3.new(0.25098040699959,0.25098040699959,0.25098040699959),BackgroundTransparency=1,BorderColor3=Color3.new(0.37647062540054,0.37647062540054,0.37647062540054),ClipsDescendants=true,Font=3,Name="Input",Parent={7},Position=UDim2.new(0,2,0,0),Size=UDim2.new(0,98,0,20),Text="0",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=0,}},
{9,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={7},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Time",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{10,"Frame",{BackgroundColor3=Color3.new(1,1,1),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),Name="ColorBox",Parent={1},Position=UDim2.new(0,220,0,95),Size=UDim2.new(0,20,0,20),}},
{11,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Title",Parent={10},Position=UDim2.new(0,-40,0,0),Size=UDim2.new(0,34,1,0),Text="Color",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,TextXAlignment=1,}},
{12,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),BorderSizePixel=0,Font=3,Name="Close",Parent={1},Position=UDim2.new(1,-90,0,95),Size=UDim2.new(0,80,0,20),Text="Close",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
{13,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),BorderSizePixel=0,Font=3,Name="Reset",Parent={1},Position=UDim2.new(1,-180,0,95),Size=UDim2.new(0,80,0,20),Text="Reset",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
{14,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderColor3=Color3.new(0.21568627655506,0.21568627655506,0.21568627655506),BorderSizePixel=0,Font=3,Name="Delete",Parent={1},Position=UDim2.new(0,280,0,95),Size=UDim2.new(0,80,0,20),Text="Delete",TextColor3=Color3.new(0.86274516582489,0.86274516582489,0.86274516582489),TextSize=14,}},
{15,"Frame",{BackgroundTransparency=1,Name="Arrow",Parent={1},Size=UDim2.new(0,16,0,16),Visible=false,}},
{16,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={15},Position=UDim2.new(0,8,0,3),Size=UDim2.new(0,1,0,2),}},
{17,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={15},Position=UDim2.new(0,7,0,5),Size=UDim2.new(0,3,0,2),}},
{18,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={15},Position=UDim2.new(0,6,0,7),Size=UDim2.new(0,5,0,2),}},
{19,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={15},Position=UDim2.new(0,5,0,9),Size=UDim2.new(0,7,0,2),}},
{20,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={15},Position=UDim2.new(0,4,0,11),Size=UDim2.new(0,9,0,2),}},
})
local window = Lib.Window.new()
window.Resizable = false
window:Resize(650,150)
window:SetTitle("ColorSequence Editor")
newMt.Window = window
newMt.Gui = window.Gui
for i,v in pairs(guiContents:GetChildren()) do
v.Parent = window.GuiElems.Content
end
local gui = window.Gui
local pickerGui = gui.Main
local pickerTopBar = pickerGui.TopBar
local pickerFrame = pickerGui.Content
local colorLine = pickerFrame.ColorLine
local gradient = colorLine.Gradient.UIGradient
local arrowFrame = pickerFrame.Arrows
local arrow = pickerFrame.Arrow
local cursor = pickerFrame.Cursor
local timeBox = pickerFrame.Time.Input
local colorBox = pickerFrame.ColorBox
local deleteButton = pickerFrame.Delete
local resetButton = pickerFrame.Reset
local closeButton = pickerFrame.Close
local topClose = pickerTopBar.Close
local user = cloneref(game["Run Service"].Parent:GetService("UserInputService"))
local mouse = cloneref(game["Run Service"].Parent:GetService("Players")).LocalPlayer:GetMouse()
local colors = {{Color3.new(1,0,1),0},{Color3.new(0.2,0.9,0.2),0.2},{Color3.new(0.4,0.5,0.9),0.7},{Color3.new(0.6,1,1),1}}
local resetSequence = nil
local beginPoint = colors[1]
local endPoint = colors[#colors]
local currentlySelected = nil
local currentPoint = nil
local sequenceLine = Instance.new("Frame")
sequenceLine.BorderSizePixel = 0
sequenceLine.Size = UDim2.new(0,1,1,0)
newMt.Sequence = ColorSequence.new(Color3.new(1,1,1))
local function buildSequence(noupdate)
local newPoints = {}
table.sort(colors,function(a,b) return a[2] < b[2] end)
for i,v in pairs(colors) do
table.insert(newPoints,ColorSequenceKeypoint.new(v[2],v[1]))
end
newMt.Sequence = ColorSequence.new(newPoints)
if not noupdate then newMt.OnSelect:Fire(newMt.Sequence) end
end
local function round(num,places)
local multi = 10^places
return math.floor(num*multi + 0.5)/multi
end
local function updateInputs(point)
if point then
currentPoint = point
local raw = point[2]
timeBox.Text = round(raw,(raw < 0.01 and 5) or (raw < 0.1 and 4) or 3)
colorBox.BackgroundColor3 = point[1]
end
end
local function placeArrow(ind,point)
local newArrow = arrow:Clone()
newArrow.Position = UDim2.new(0,ind-1,0,0)
newArrow.Visible = true
newArrow.Parent = arrowFrame
newArrow.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
cursor.Visible = true
cursor.Position = UDim2.new(0,9 + newArrow.Position.X.Offset,0,0)
end
if input.UserInputType == Enum.UserInputType.MouseButton1 then
updateInputs(point)
if point == beginPoint or point == endPoint or currentlySelected then return end
local mouseEvent,releaseEvent
currentlySelected = true
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
mouseEvent:Disconnect()
releaseEvent:Disconnect()
currentlySelected = nil
cursor.Visible = false
end)
mouseEvent = user.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
local maxSize = colorLine.AbsoluteSize.X-1
local relativeX = mouse.X - colorLine.AbsolutePosition.X
if relativeX < 0 then relativeX = 0 end
if relativeX > maxSize then relativeX = maxSize end
local raw = relativeX/maxSize
point[2] = relativeX/maxSize
updateInputs(point)
cursor.Visible = true
cursor.Position = UDim2.new(0,9 + newArrow.Position.X.Offset,0,0)
buildSequence()
newMt:Redraw()
end
end)
end
end)
newArrow.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
cursor.Visible = false
end
end)
return newArrow
end
local function placeArrows()
for i,v in pairs(colors) do
v[3] = placeArrow(math.floor((colorLine.AbsoluteSize.X-1) * v[2]) + 1,v)
end
end
local function redraw(self)
gradient.Color = newMt.Sequence or ColorSequence.new(Color3.new(1,1,1))
for i = 2,#colors do
local nextColor = colors[i]
local endPos = math.floor((colorLine.AbsoluteSize.X-1) * nextColor[2]) + 1
nextColor[3].Position = UDim2.new(0,endPos,0,0)
end
end
newMt.Redraw = redraw
local function loadSequence(self,seq)
resetSequence = seq
for i,v in pairs(colors) do if v[3] then v[3]:Destroy() end end
colors = {}
currentlySelected = nil
for i,v in pairs(seq.Keypoints) do
local newPoint = {v.Value,v.Time}
newPoint[3] = placeArrow(v.Time,newPoint)
table.insert(colors,newPoint)
end
beginPoint = colors[1]
endPoint = colors[#colors]
currentlySelected = nil
updateInputs(colors[1])
buildSequence(true)
redraw()
end
newMt.SetSequence = loadSequence
local function buttonAnimations(button,inverse)
button.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then button.BackgroundTransparency = (inverse and 0.5 or 0.4) end end)
button.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseMovement then button.BackgroundTransparency = (inverse and 1 or 0) end end)
end
colorLine.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 and #colors < 20 then
local maxSize = colorLine.AbsoluteSize.X-1
local relativeX = mouse.X - colorLine.AbsolutePosition.X
if relativeX < 0 then relativeX = 0 end
if relativeX > maxSize then relativeX = maxSize end
local raw = relativeX/maxSize
local fromColor = nil
local toColor = nil
for i,col in pairs(colors) do
if col[2] >= raw then
fromColor = colors[math.max(i-1,1)]
toColor = colors[i]
break
end
end
local lerpColor = fromColor[1]:lerp(toColor[1],(raw-fromColor[2])/(toColor[2]-fromColor[2]))
local newPoint = {lerpColor,raw}
newPoint[3] = placeArrow(newPoint[2],newPoint)
table.insert(colors,newPoint)
updateInputs(newPoint)
buildSequence()
redraw()
end
end)
colorLine.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
local maxSize = colorLine.AbsoluteSize.X-1
local relativeX = mouse.X - colorLine.AbsolutePosition.X
if relativeX < 0 then relativeX = 0 end
if relativeX > maxSize then relativeX = maxSize end
cursor.Visible = true
cursor.Position = UDim2.new(0,10 + relativeX,0,0)
end
end)
colorLine.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
local inArrow = false
for i,v in pairs(colors) do
if Lib.CheckMouseInGui(v[3]) then
inArrow = v[3]
end
end
cursor.Visible = inArrow and true or false
if inArrow then cursor.Position = UDim2.new(0,9 + inArrow.Position.X.Offset,0,0) end
end
end)
timeBox:GetPropertyChangedSignal("Text"):Connect(function()
local point = currentPoint
local num = tonumber(timeBox.Text)
if point and num and point ~= beginPoint and point ~= endPoint then
num = math.clamp(num,0,1)
point[2] = num
buildSequence()
redraw()
end
end)
colorBox.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
local editor = newMt.ColorPicker
if not editor then
editor = Lib.ColorPicker.new()
editor.Window:SetTitle("ColorSequence Color Picker")
editor.OnSelect:Connect(function(col)
if currentPoint then
currentPoint[1] = col
end
buildSequence()
redraw()
end)
newMt.ColorPicker = editor
end
editor.Window:ShowAndFocus()
end
end)
deleteButton.MouseButton1Click:Connect(function()
if currentPoint and currentPoint ~= beginPoint and currentPoint ~= endPoint then
for i,v in pairs(colors) do
if v == currentPoint then
v[3]:Destroy()
table.remove(colors,i)
break
end
end
currentlySelected = nil
updateInputs(colors[1])
buildSequence()
redraw()
end
end)
resetButton.MouseButton1Click:Connect(function()
if resetSequence then
newMt:SetSequence(resetSequence)
end
end)
closeButton.MouseButton1Click:Connect(function()
window:Close()
end)
topClose.MouseButton1Click:Connect(function()
window:Close()
end)
buttonAnimations(deleteButton)
buttonAnimations(resetButton)
buttonAnimations(closeButton)
placeArrows()
redraw()
newMt.Show = function(self)
window:Show()
end
return newMt
end
return {new = new}
end)()
Lib.ViewportTextBox = (function()
local textService = cloneref(game["Run Service"].Parent:GetService("TextService"))
local props = {
OffsetX = 0,
TextBox = PH,
CursorPos = -1,
Gui = PH,
View = PH
}
local funcs = {}
funcs.Update = function(self)
local cursorPos = self.CursorPos or -1
local text = self.TextBox.Text
if text == "" then self.TextBox.Position = UDim2.new(0,0,0,0) return end
if cursorPos == -1 then return end
local cursorText = text:sub(1,cursorPos-1)
local pos = nil
local leftEnd = -self.TextBox.Position.X.Offset
local rightEnd = leftEnd + self.View.AbsoluteSize.X
local totalTextSize = textService:GetTextSize(text,self.TextBox.TextSize,self.TextBox.Font,Vector2.new(999999999,100)).X
local cursorTextSize = textService:GetTextSize(cursorText,self.TextBox.TextSize,self.TextBox.Font,Vector2.new(999999999,100)).X
if cursorTextSize > rightEnd then
pos = math.max(-1,cursorTextSize - self.View.AbsoluteSize.X + 2)
elseif cursorTextSize < leftEnd then
pos = math.max(-1,cursorTextSize-2)
elseif totalTextSize < rightEnd then
pos = math.max(-1,totalTextSize - self.View.AbsoluteSize.X + 2)
end
if pos then
self.TextBox.Position = UDim2.new(0,-pos,0,0)
self.TextBox.Size = UDim2.new(1,pos,1,0)
end
end
funcs.GetText = function(self)
return self.TextBox.Text
end
funcs.SetText = function(self,text)
self.TextBox.Text = text
end
local mt = getGuiMT(props,funcs)
local function convert(textbox)
local obj = initObj(props,mt)
local view = Instance.new("Frame")
view.BackgroundTransparency = textbox.BackgroundTransparency
view.BackgroundColor3 = textbox.BackgroundColor3
view.BorderSizePixel = textbox.BorderSizePixel
view.BorderColor3 = textbox.BorderColor3
view.Position = textbox.Position
view.Size = textbox.Size
view.ClipsDescendants = true
view.Name = textbox.Name
textbox.BackgroundTransparency = 1
textbox.Position = UDim2.new(0,0,0,0)
textbox.Size = UDim2.new(1,0,1,0)
textbox.TextXAlignment = Enum.TextXAlignment.Left
textbox.Name = "Input"
obj.TextBox = textbox
obj.View = view
obj.Gui = view
textbox.Changed:Connect(function(prop)
if prop == "Text" or prop == "CursorPosition" or prop == "AbsoluteSize" then
local cursorPos = obj.TextBox.CursorPosition
if cursorPos ~= -1 then obj.CursorPos = cursorPos end
obj:Update()
end
end)
obj:Update()
view.Parent = textbox.Parent
textbox.Parent = view
return obj
end
local function new()
local textBox = Instance.new("TextBox")
textBox.Size = UDim2.new(0,100,0,20)
textBox.BackgroundColor3 = Settings.Theme.TextBox
textBox.BorderColor3 = Settings.Theme.Outline3
textBox.ClearTextOnFocus = false
textBox.TextColor3 = Settings.Theme.Text
textBox.Font = Enum.Font.SourceSans
textBox.TextSize = 14
textBox.Text = ""
return convert(textBox)
end
return {new = new, convert = convert}
end)()
Lib.Label = (function()
local props,funcs = {},{}
local mt = getGuiMT(props,funcs)
local function new()
local label = Instance.new("TextLabel")
label.BackgroundTransparency = 1
label.TextXAlignment = Enum.TextXAlignment.Left
label.TextColor3 = Settings.Theme.Text
label.TextTransparency = 0.1
label.Size = UDim2.new(0,100,0,20)
label.Font = Enum.Font.SourceSans
label.TextSize = 14
local obj = setmetatable({
Gui = label
},mt)
return obj
end
return {new = new}
end)()
Lib.Frame = (function()
local props,funcs = {},{}
local mt = getGuiMT(props,funcs)
local function new()
local fr = Instance.new("Frame")
fr.BackgroundColor3 = Settings.Theme.Main1
fr.BorderColor3 = Settings.Theme.Outline1
fr.Size = UDim2.new(0,50,0,50)
local obj = setmetatable({
Gui = fr
},mt)
return obj
end
return {new = new}
end)()
Lib.Button = (function()
local props = {
Gui = PH,
Anim = PH,
Disabled = false,
OnClick = SIGNAL,
OnDown = SIGNAL,
OnUp = SIGNAL,
AllowedButtons = {1}
}
local funcs = {}
local tableFind = table.find
funcs.Trigger = function(self,event,button)
if not self.Disabled and tableFind(self.AllowedButtons,button) then
self["On"..event]:Fire(button)
end
end
funcs.SetDisabled = function(self,dis)
self.Disabled = dis
if dis then
self.Anim:Disable()
self.Gui.TextTransparency = 0.5
else
self.Anim.Enable()
self.Gui.TextTransparency = 0
end
end
local mt = getGuiMT(props,funcs)
local function new()
local b = Instance.new("TextButton")
b.AutoButtonColor = false
b.TextColor3 = Settings.Theme.Text
b.TextTransparency = 0.1
b.Size = UDim2.new(0,100,0,20)
b.Font = Enum.Font.SourceSans
b.TextSize = 14
b.BackgroundColor3 = Settings.Theme.Button
b.BorderColor3 = Settings.Theme.Outline2
local obj = initObj(props,mt)
obj.Gui = b
obj.Anim = Lib.ButtonAnim(b,{Mode = 2, StartColor = Settings.Theme.Button, HoverColor = Settings.Theme.ButtonHover, PressColor = Settings.Theme.ButtonPress, OutlineColor = Settings.Theme.Outline2})
b.MouseButton1Click:Connect(function() obj:Trigger("Click",1) end)
b.MouseButton1Down:Connect(function() obj:Trigger("Down",1) end)
b.MouseButton1Up:Connect(function() obj:Trigger("Up",1) end)
b.MouseButton2Click:Connect(function() obj:Trigger("Click",2) end)
b.MouseButton2Down:Connect(function() obj:Trigger("Down",2) end)
b.MouseButton2Up:Connect(function() obj:Trigger("Up",2) end)
return obj
end
return {new = new}
end)()
Lib.DropDown = (function()
local props = {
Gui = PH,
Anim = PH,
Context = PH,
Selected = PH,
Disabled = false,
CanBeEmpty = true,
Options = {},
GuiElems = {},
OnSelect = SIGNAL
}
local funcs = {}
funcs.Update = function(self)
local options = self.Options
if #options > 0 then
if not self.Selected then
if not self.CanBeEmpty then
self.Selected = options[1]
self.GuiElems.Label.Text = options[1]
else
self.GuiElems.Label.Text = "- Select -"
end
else
self.GuiElems.Label.Text = self.Selected
end
else
self.GuiElems.Label.Text = "- Select -"
end
end
funcs.ShowOptions = function(self)
local context = self.Context
context.Width = self.Gui.AbsoluteSize.X
context.ReverseYOffset = self.Gui.AbsoluteSize.Y
context:Show(self.Gui.AbsolutePosition.X, self.Gui.AbsolutePosition.Y + context.ReverseYOffset)
end
funcs.SetOptions = function(self,opts)
self.Options = opts
local context = self.Context
local options = self.Options
context:Clear()
local onClick = function(option) self.Selected = option self.OnSelect:Fire(option) self:Update() end
if self.CanBeEmpty then
context:Add({Name = "- Select -", OnClick = function() self.Selected = nil self.OnSelect:Fire(nil) self:Update() end})
end
for i = 1,#options do
context:Add({Name = options[i], OnClick = onClick})
end
self:Update()
end
funcs.SetSelected = function(self,opt)
self.Selected = type(opt) == "number" and self.Options[opt] or opt
self:Update()
end
local mt = getGuiMT(props,funcs)
local function new()
local f = Instance.new("TextButton")
f.AutoButtonColor = false
f.Text = ""
f.Size = UDim2.new(0,100,0,20)
f.BackgroundColor3 = Settings.Theme.TextBox
f.BorderColor3 = Settings.Theme.Outline3
local label = Lib.Label.new()
label.Position = UDim2.new(0,2,0,0)
label.Size = UDim2.new(1,-22,1,0)
label.TextTruncate = Enum.TextTruncate.AtEnd
label.Parent = f
local arrow = create({
{1,"Frame",{BackgroundTransparency=1,Name="EnumArrow",Position=UDim2.new(1,-16,0,2),Size=UDim2.new(0,16,0,16),}},
{2,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,8,0,9),Size=UDim2.new(0,1,0,1),}},
{3,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,7,0,8),Size=UDim2.new(0,3,0,1),}},
{4,"Frame",{BackgroundColor3=Color3.new(0.86274510622025,0.86274510622025,0.86274510622025),BorderSizePixel=0,Parent={1},Position=UDim2.new(0,6,0,7),Size=UDim2.new(0,5,0,1),}},
})
arrow.Parent = f
local obj = initObj(props,mt)
obj.Gui = f
obj.Anim = Lib.ButtonAnim(f,{Mode = 2, StartColor = Settings.Theme.TextBox, LerpTo = Settings.Theme.Button, LerpDelta = 0.15})
obj.Context = Lib.ContextMenu.new()
obj.Context.Iconless = true
obj.Context.MaxHeight = 200
obj.Selected = nil
obj.GuiElems = {Label = label}
f.MouseButton1Down:Connect(function() obj:ShowOptions() end)
obj:Update()
return obj
end
return {new = new}
end)()
Lib.ClickSystem = (function()
local props = {
LastItem = PH,
OnDown = SIGNAL,
OnRelease = SIGNAL,
AllowedButtons = {1},
Combo = 0,
MaxCombo = 2,
ComboTime = 0.5,
Items = {},
ItemCons = {},
ClickId = -1,
LastButton = ""
}
local funcs = {}
local tostring = tostring
local disconnect = function(con)
local pos = table.find(con.Signal.Connections,con)
if pos then table.remove(con.Signal.Connections,pos) end
end
funcs.Trigger = function(self,item,button)
if table.find(self.AllowedButtons,button) then
if self.LastButton ~= button or self.LastItem ~= item or self.Combo == self.MaxCombo or tick() - self.ClickId > self.ComboTime then
self.Combo = 0
self.LastButton = button
self.LastItem = item
end
self.Combo = self.Combo + 1
self.ClickId = tick()
local release
release = service.UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType["MouseButton"..button] then
release:Disconnect()
if Lib.CheckMouseInGui(item) and self.LastButton == button and self.LastItem == item then
self["OnRelease"]:Fire(item,self.Combo,button)
end
end
end)
self["OnDown"]:Fire(item,self.Combo,button)
end
end
funcs.Add = function(self,item)
if table.find(self.Items,item) then return end
local cons = {}
cons[1] = item.MouseButton1Down:Connect(function() self:Trigger(item,1) end)
cons[2] = item.MouseButton2Down:Connect(function() self:Trigger(item,2) end)
self.ItemCons[item] = cons
self.Items[#self.Items+1] = item
end
funcs.Remove = function(self,item)
local ind = table.find(self.Items,item)
if not ind then return end
for i,v in pairs(self.ItemCons[item]) do
v:Disconnect()
end
self.ItemCons[item] = nil
table.remove(self.Items,ind)
end
local mt = {__index = funcs}
local function new()
local obj = initObj(props,mt)
return obj
end
return {new = new}
end)()
return Lib
end
return {InitDeps = initDeps, InitAfterMain = initAfterMain, Main = main} end }
-- Main vars local Main, Explorer, Properties, ScriptViewer, DefaultSettings, Notebook, Serializer, Lib local API, RMD
-- Default Settings DefaultSettings = (function() local rgb = Color3.fromRGB return { Explorer = { _Recurse = true, Sorting = true, TeleportToOffset = Vector3.new(0,0,0), ClickToRename = true, AutoUpdateSearch = true, AutoUpdateMode = 0, -- 0 Default, 1 no tree update, 2 no descendant events, 3 frozen PartSelectionBox = true, GuiSelectionBox = true, CopyPathUseGetChildren = true }, Properties = { _Recurse = true, MaxConflictCheck = 50, ShowDeprecated = false, ShowHidden = false, ClearOnFocus = false, LoadstringInput = true, NumberRounding = 3, ShowAttributes = false, MaxAttributes = 50, ScaleType = 1 -- 0 Full Name Shown, 1 Equal Halves }, Theme = { _Recurse = true, Main1 = rgb(52,52,52), Main2 = rgb(45,45,45), Outline1 = rgb(33,33,33), -- Mainly frames Outline2 = rgb(55,55,55), -- Mainly button Outline3 = rgb(30,30,30), -- Mainly textbox TextBox = rgb(38,38,38), Menu = rgb(32,32,32), ListSelection = rgb(11,90,175), Button = rgb(60,60,60), ButtonHover = rgb(68,68,68), ButtonPress = rgb(40,40,40), Highlight = rgb(75,75,75), Text = rgb(255,255,255), PlaceholderText = rgb(100,100,100), Important = rgb(255,0,0), ExplorerIconMap = "", MiscIconMap = "", Syntax = { Text = rgb(204,204,204), Background = rgb(36,36,36), Selection = rgb(255,255,255), SelectionBack = rgb(11,90,175), Operator = rgb(204,204,204), Number = rgb(255,198,0), String = rgb(173,241,149), Comment = rgb(102,102,102), Keyword = rgb(248,109,124), Error = rgb(255,0,0), FindBackground = rgb(141,118,0), MatchingWord = rgb(85,85,85), BuiltIn = rgb(132,214,247), CurrentLine = rgb(45,50,65), LocalMethod = rgb(253,251,172), LocalProperty = rgb(97,161,241), Nil = rgb(255,198,0), Bool = rgb(255,198,0), Function = rgb(248,109,124), Local = rgb(248,109,124), Self = rgb(248,109,124), FunctionName = rgb(253,251,172), Bracket = rgb(204,204,204) }, } } end)()
-- Vars local Settings = {} local Apps = {} local env = {} local service = setmetatable({},{__index = function(self,name) local serv = cloneref(game["Run Service"].Parent:GetService(name)) self[name] = serv return serv end}) local plr = service.Players.LocalPlayer or service.Players.PlayerAdded:wait()
local create = function(data) local insts = {} for i,v in pairs(data) do insts[v[1]] = Instance.new(v[2]) end
for _,v in pairs(data) do
for prop,val in pairs(v[3]) do
if type(val) == "table" then
insts[v[1]][prop] = insts[val[1]]
else
insts[v[1]][prop] = val
end
end
end
return insts[1]
end
local createSimple = function(class,props) local inst = Instance.new(class) for i,v in next,props do inst[i] = v end return inst end
Main = (function() local Main = {}
Main.ModuleList = {"Explorer","Properties","ScriptViewer"}
Main.Elevated = false
Main.MissingEnv = {}
Main.Version = "" -- Beta 1.0.0
Main.Mouse = plr:GetMouse()
Main.AppControls = {}
Main.Apps = Apps
Main.MenuApps = {}
Main.DisplayOrders = {
SideWindow = 8,
Window = 10,
Menu = 100000,
Core = 101000
}
Main.GetInitDeps = function()
return {
Main = Main,
Lib = Lib,
Apps = Apps,
Settings = Settings,
API = API,
RMD = RMD,
env = env,
service = service,
plr = plr,
create = create,
createSimple = createSimple
}
end
Main.Error = function(str)
if rconsoleprint then
rconsoleprint("DEX ERROR: "..tostring(str).."\n")
wait(9e9)
else
error(str)
end
end
Main.LoadModule = function(name)
if Main.Elevated then -- If you don't have filesystem api then ur outta luck tbh
local control
if EmbeddedModules then -- Offline Modules
control = EmbeddedModules[name]()
if not control then Main.Error("Missing Embedded Module: "..name) end
end
Main.AppControls[name] = control
control.InitDeps(Main.GetInitDeps())
local moduleData = control.Main()
Apps[name] = moduleData
return moduleData
else
local module = script:WaitForChild("Modules"):WaitForChild(name,2)
if not module then Main.Error("CANNOT FIND MODULE "..name) end
local control = require(module)
Main.AppControls[name] = control
control.InitDeps(Main.GetInitDeps())
local moduleData = control.Main()
Apps[name] = moduleData
return moduleData
end
end
Main.LoadModules = function()
for i,v in pairs(Main.ModuleList) do
local s,e = pcall(Main.LoadModule,v)
if not s then
Main.Error("FAILED LOADING " + v + " CAUSE " + e)
end
end
-- Init Major Apps and define them in modules
Explorer = Apps.Explorer
Properties = Apps.Properties
ScriptViewer = Apps.ScriptViewer
Notebook = Apps.Notebook
local appTable = {
Explorer = Explorer,
Properties = Properties,
ScriptViewer = ScriptViewer,
Notebook = Notebook
}
Main.AppControls.Lib.InitAfterMain(appTable)
for i,v in pairs(Main.ModuleList) do
local control = Main.AppControls[v]
if control then
control.InitAfterMain(appTable)
end
end
end
Main.InitEnv = function()
setmetatable(env, {__newindex = function(self, name, func)
if not func then Main.MissingEnv[#Main.MissingEnv + 1] = name return end
rawset(self, name, func)
end})
-- file
env.readfile = readfile
env.writefile = writefile
env.appendfile = appendfile
env.makefolder = makefolder
env.listfiles = listfiles
env.loadfile = loadfile
env.movefileas = movefileas
env.saveinstance = saveinstance
-- debug
env.getupvalues = (debug and debug.getupvalues) or getupvalues or getupvals
env.getconstants = (debug and debug.getconstants) or getconstants or getconsts
env.getinfo = (debug and (debug.getinfo or debug.info)) or getinfo
env.islclosure = islclosure or is_l_closure or is_lclosure
env.checkcaller = checkcaller
--env.getreg = getreg
env.getgc = getgc or get_gc_objects
env.base64encode = crypt and crypt.base64 and crypt.base64.encode
env.getscriptbytecode = getscriptbytecode
-- other
--env.setfflag = setfflag
env.request = (syn and syn.request) or (http and http.request) or http_request or (fluxus and fluxus.request) or request
env.decompile = decompile or (env.getscriptbytecode and env.request and env.base64encode and function(scr)
local s, bytecode = pcall(env.getscriptbytecode, scr)
if not s then
return "failed to get bytecode " .. tostring(bytecode)
end
local response = env.request({
Url = "https://unluau.lonegladiator.dev/unluau/decompile",
Method = "POST",
Headers = {
["Content-Type"] = "application/json"
},
Body = service.HttpService:JSONEncode({
version = 5,
bytecode = env.base64encode(bytecode)
})
})
local decoded = service.HttpService:JSONDecode(response.Body)
if decoded.status ~= "ok" then
return "decompilation failed: " .. tostring(decoded.status)
end
return decoded.output
end)
env.protectgui = protect_gui or (syn and syn.protect_gui)
env.gethui = gethui or get_hidden_gui
env.setclipboard = setclipboard or toclipboard or set_clipboard or (Clipboard and Clipboard.set)
env.getnilinstances = getnilinstances or get_nil_instances
env.getloadedmodules = getloadedmodules
-- if identifyexecutor and type(identifyexecutor) == "function" then Main.Executor = identifyexecutor() end
Main.GuiHolder = Main.Elevated and service.CoreGui or plr:FindFirstChildWhichIsA("PlayerGui")
setmetatable(env, nil)
end
Main.LoadSettings = function()
local s,data = pcall(env.readfile or error,"DexSettings.json")
if s and data and data ~= "" then
local s,decoded = service.HttpService:JSONDecode(data)
if s and decoded then
for i,v in next,decoded do
end
else
-- TODO: Notification
end
else
Main.ResetSettings()
end
end
Main.ResetSettings = function()
local function recur(t,res)
for set,val in pairs(t) do
if type(val) == "table" and val._Recurse then
if type(res[set]) ~= "table" then
res[set] = {}
end
recur(val,res[set])
else
res[set] = val
end
end
return res
end
recur(DefaultSettings,Settings)
end
Main.FetchAPI = function()
local api,rawAPI
if Main.Elevated then
if Main.LocalDepsUpToDate() then
local localAPI = Lib.ReadFile("dex/rbx_api.dat")
if localAPI then
rawAPI = localAPI
else
Main.DepsVersionData[1] = ""
end
end
rawAPI = rawAPI or game:HttpGet("http://setup.roblox.com/"..Main.RobloxVersion.."-API-Dump.json")
else
if script:FindFirstChild("API") then
rawAPI = require(script.API)
else
error("NO API EXISTS")
end
end
Main.RawAPI = rawAPI
api = service.HttpService:JSONDecode(rawAPI)
local classes,enums = {},{}
local categoryOrder,seenCategories = {},{}
local function insertAbove(t,item,aboveItem)
local findPos = table.find(t,item)
if not findPos then return end
table.remove(t,findPos)
local pos = table.find(t,aboveItem)
if not pos then return end
table.insert(t,pos,item)
end
for _,class in pairs(api.Classes) do
local newClass = {}
newClass.Name = class.Name
newClass.Superclass = class.Superclass
newClass.Properties = {}
newClass.Functions = {}
newClass.Events = {}
newClass.Callbacks = {}
newClass.Tags = {}
if class.Tags then for c,tag in pairs(class.Tags) do newClass.Tags[tag] = true end end
for __,member in pairs(class.Members) do
local newMember = {}
newMember.Name = member.Name
newMember.Class = class.Name
newMember.Security = member.Security
newMember.Tags ={}
if member.Tags then for c,tag in pairs(member.Tags) do newMember.Tags[tag] = true end end
local mType = member.MemberType
if mType == "Property" then
local propCategory = member.Category or "Other"
propCategory = propCategory:match("^%s*(.-)%s*$")
if not seenCategories[propCategory] then
categoryOrder[#categoryOrder+1] = propCategory
seenCategories[propCategory] = true
end
newMember.ValueType = member.ValueType
newMember.Category = propCategory
newMember.Serialization = member.Serialization
table.insert(newClass.Properties,newMember)
elseif mType == "Function" then
newMember.Parameters = {}
newMember.ReturnType = member.ReturnType.Name
for c,param in pairs(member.Parameters) do
table.insert(newMember.Parameters,{Name = param.Name, Type = param.Type.Name})
end
table.insert(newClass.Functions,newMember)
elseif mType == "Event" then
newMember.Parameters = {}
for c,param in pairs(member.Parameters) do
table.insert(newMember.Parameters,{Name = param.Name, Type = param.Type.Name})
end
table.insert(newClass.Events,newMember)
end
end
classes[class.Name] = newClass
end
for _,class in pairs(classes) do
class.Superclass = classes[class.Superclass]
end
for _,enum in pairs(api.Enums) do
local newEnum = {}
newEnum.Name = enum.Name
newEnum.Items = {}
newEnum.Tags = {}
if enum.Tags then for c,tag in pairs(enum.Tags) do newEnum.Tags[tag] = true end end
for __,item in pairs(enum.Items) do
local newItem = {}
newItem.Name = item.Name
newItem.Value = item.Value
table.insert(newEnum.Items,newItem)
end
enums[enum.Name] = newEnum
end
local function getMember(class,member)
if not classes[class] or not classes[class][member] then return end
local result = {}
local currentClass = classes[class]
while currentClass do
for _,entry in pairs(currentClass[member]) do
result[#result+1] = entry
end
currentClass = currentClass.Superclass
end
table.sort(result,function(a,b) return a.Name < b.Name end)
return result
end
insertAbove(categoryOrder,"Behavior","Tuning")
insertAbove(categoryOrder,"Appearance","Data")
insertAbove(categoryOrder,"Attachments","Axes")
insertAbove(categoryOrder,"Cylinder","Slider")
insertAbove(categoryOrder,"Localization","Jump Settings")
insertAbove(categoryOrder,"Surface","Motion")
insertAbove(categoryOrder,"Surface Inputs","Surface")
insertAbove(categoryOrder,"Part","Surface Inputs")
insertAbove(categoryOrder,"Assembly","Surface Inputs")
insertAbove(categoryOrder,"Character","Controls")
categoryOrder[#categoryOrder+1] = "Unscriptable"
categoryOrder[#categoryOrder+1] = "Attributes"
local categoryOrderMap = {}
for i = 1,#categoryOrder do
categoryOrderMap[categoryOrder[i]] = i
end
return {
Classes = classes,
Enums = enums,
CategoryOrder = categoryOrderMap,
GetMember = getMember
}
end
Main.FetchRMD = function()
local rawXML
if Main.Elevated then
if Main.LocalDepsUpToDate() then
local localRMD = Lib.ReadFile("dex/rbx_rmd.dat")
if localRMD then
rawXML = localRMD
else
Main.DepsVersionData[1] = ""
end
end
rawXML = rawXML or game:HttpGet("https://raw.githubusercontent.com/CloneTrooper1019/Roblox-Client-Tracker/roblox/ReflectionMetadata.xml")
else
if script:FindFirstChild("RMD") then
rawXML = require(script.RMD)
else
error("NO RMD EXISTS")
end
end
Main.RawRMD = rawXML
local parsed = Lib.ParseXML(rawXML)
local classList = parsed.children[1].children[1].children
local enumList = parsed.children[1].children[2].children
local propertyOrders = {}
local classes,enums = {},{}
for _,class in pairs(classList) do
local className = ""
for _,child in pairs(class.children) do
if child.tag == "Properties" then
local data = {Properties = {}, Functions = {}}
local props = child.children
for _,prop in pairs(props) do
local name = prop.attrs.name
name = name:sub(1,1):upper()..name:sub(2)
data[name] = prop.children[1].text
end
className = data.Name
classes[className] = data
elseif child.attrs.class == "ReflectionMetadataProperties" then
local members = child.children
for _,member in pairs(members) do
if member.attrs.class == "ReflectionMetadataMember" then
local data = {}
if member.children[1].tag == "Properties" then
local props = member.children[1].children
for _,prop in pairs(props) do
if prop.attrs then
local name = prop.attrs.name
name = name:sub(1,1):upper()..name:sub(2)
data[name] = prop.children[1].text
end
end
if data.PropertyOrder then
local orders = propertyOrders[className]
if not orders then orders = {} propertyOrders[className] = orders end
orders[data.Name] = tonumber(data.PropertyOrder)
end
classes[className].Properties[data.Name] = data
end
end
end
elseif child.attrs.class == "ReflectionMetadataFunctions" then
local members = child.children
for _,member in pairs(members) do
if member.attrs.class == "ReflectionMetadataMember" then
local data = {}
if member.children[1].tag == "Properties" then
local props = member.children[1].children
for _,prop in pairs(props) do
if prop.attrs then
local name = prop.attrs.name
name = name:sub(1,1):upper()..name:sub(2)
data[name] = prop.children[1].text
end
end
classes[className].Functions[data.Name] = data
end
end
end
end
end
end
for _,enum in pairs(enumList) do
local enumName = ""
for _,child in pairs(enum.children) do
if child.tag == "Properties" then
local data = {Items = {}}
local props = child.children
for _,prop in pairs(props) do
local name = prop.attrs.name
name = name:sub(1,1):upper()..name:sub(2)
data[name] = prop.children[1].text
end
enumName = data.Name
enums[enumName] = data
elseif child.attrs.class == "ReflectionMetadataEnumItem" then
local data = {}
if child.children[1].tag == "Properties" then
local props = child.children[1].children
for _,prop in pairs(props) do
local name = prop.attrs.name
name = name:sub(1,1):upper()..name:sub(2)
data[name] = prop.children[1].text
end
enums[enumName].Items[data.Name] = data
end
end
end
end
return {Classes = classes, Enums = enums, PropertyOrders = propertyOrders}
end
Main.ShowGui = function(gui)
if env.gethui then
gui.Parent = env.gethui()
elseif env.protectgui then
env.protectgui(gui)
gui.Parent = Main.GuiHolder
else
gui.Parent = Main.GuiHolder
end
end
Main.CreateIntro = function(initStatus) -- TODO: Must theme and show errors
local gui = create({
{1,"ScreenGui",{Name="Intro",}},
{2,"Frame",{Active=true,BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="Main",Parent={1},Position=UDim2.new(0.5,-175,0.5,-100),Size=UDim2.new(0,350,0,200),}},
{3,"Frame",{BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,ClipsDescendants=true,Name="Holder",Parent={2},Size=UDim2.new(1,0,1,0),}},
{4,"UIGradient",{Parent={3},Rotation=30,Transparency=NumberSequence.new({NumberSequenceKeypoint.new(0,1,0),NumberSequenceKeypoint.new(1,1,0),}),}},
{5,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=4,Name="Title",Parent={3},Position=UDim2.new(0,-190,0,15),Size=UDim2.new(0,100,0,50),Text="Dex",TextColor3=Color3.new(1,1,1),TextSize=50,TextTransparency=1,}},
{6,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Desc",Parent={3},Position=UDim2.new(0,-230,0,60),Size=UDim2.new(0,180,0,25),Text="Ultimate Debugging Suite",TextColor3=Color3.new(1,1,1),TextSize=18,TextTransparency=1,}},
{7,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="StatusText",Parent={3},Position=UDim2.new(0,20,0,110),Size=UDim2.new(0,180,0,25),Text="Fetching API",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=1,}},
{8,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="ProgressBar",Parent={3},Position=UDim2.new(0,110,0,145),Size=UDim2.new(0,0,0,4),}},
{9,"Frame",{BackgroundColor3=Color3.new(0.2392156869173,0.56078433990479,0.86274510622025),BorderSizePixel=0,Name="Bar",Parent={8},Size=UDim2.new(0,0,1,0),}},
{10,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://2764171053",ImageColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),Parent={8},ScaleType=1,Size=UDim2.new(1,0,1,0),SliceCenter=Rect.new(2,2,254,254),}},
{11,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Creator",Parent={2},Position=UDim2.new(1,-110,1,-20),Size=UDim2.new(0,105,0,20),Text="Developed by Moon",TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=1,}},
{12,"UIGradient",{Parent={11},Transparency=NumberSequence.new({NumberSequenceKeypoint.new(0,1,0),NumberSequenceKeypoint.new(1,1,0),}),}},
{13,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Version",Parent={2},Position=UDim2.new(1,-110,1,-35),Size=UDim2.new(0,105,0,20),Text=Main.Version,TextColor3=Color3.new(1,1,1),TextSize=14,TextXAlignment=1,}},
{14,"UIGradient",{Parent={13},Transparency=NumberSequence.new({NumberSequenceKeypoint.new(0,1,0),NumberSequenceKeypoint.new(1,1,0),}),}},
{15,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Image="rbxassetid://1427967925",Name="Outlines",Parent={2},Position=UDim2.new(0,-5,0,-5),ScaleType=1,Size=UDim2.new(1,10,1,10),SliceCenter=Rect.new(6,6,25,25),TileSize=UDim2.new(0,20,0,20),}},
{16,"UIGradient",{Parent={15},Rotation=-30,Transparency=NumberSequence.new({NumberSequenceKeypoint.new(0,1,0),NumberSequenceKeypoint.new(1,1,0),}),}},
{17,"UIGradient",{Parent={2},Rotation=-30,Transparency=NumberSequence.new({NumberSequenceKeypoint.new(0,1,0),NumberSequenceKeypoint.new(1,1,0),}),}},
})
Main.ShowGui(gui)
local backGradient = gui.Main.UIGradient
local outlinesGradient = gui.Main.Outlines.UIGradient
local holderGradient = gui.Main.Holder.UIGradient
local titleText = gui.Main.Holder.Title
local descText = gui.Main.Holder.Desc
local versionText = gui.Main.Version
local versionGradient = versionText.UIGradient
local creatorText = gui.Main.Creator
local creatorGradient = creatorText.UIGradient
local statusText = gui.Main.Holder.StatusText
local progressBar = gui.Main.Holder.ProgressBar
local tweenS = service.TweenService
local renderStepped = service.RunService.RenderStepped
local signalWait = renderStepped.wait
local fastwait = function(s)
if not s then return signalWait(renderStepped) end
local start = tick()
while tick() - start < s do signalWait(renderStepped) end
end
statusText.Text = initStatus
local function tweenNumber(n,ti,func)
local tweenVal = Instance.new("IntValue")
tweenVal.Value = 0
tweenVal.Changed:Connect(func)
local tween = tweenS:Create(tweenVal,ti,{Value = n})
tween:Play()
tween.Completed:Connect(function()
tweenVal:Destroy()
end)
end
local ti = TweenInfo.new(0.4,Enum.EasingStyle.Quad,Enum.EasingDirection.Out)
tweenNumber(100,ti,function(val)
val = val/200
local start = NumberSequenceKeypoint.new(0,0)
local a1 = NumberSequenceKeypoint.new(val,0)
local a2 = NumberSequenceKeypoint.new(math.min(0.5,val+math.min(0.05,val)),1)
if a1.Time == a2.Time then a2 = a1 end
local b1 = NumberSequenceKeypoint.new(1-val,0)
local b2 = NumberSequenceKeypoint.new(math.max(0.5,1-val-math.min(0.05,val)),1)
if b1.Time == b2.Time then b2 = b1 end
local goal = NumberSequenceKeypoint.new(1,0)
backGradient.Transparency = NumberSequence.new({start,a1,a2,b2,b1,goal})
outlinesGradient.Transparency = NumberSequence.new({start,a1,a2,b2,b1,goal})
end)
fastwait(0.4)
tweenNumber(100,ti,function(val)
val = val/166.66
local start = NumberSequenceKeypoint.new(0,0)
local a1 = NumberSequenceKeypoint.new(val,0)
local a2 = NumberSequenceKeypoint.new(val+0.01,1)
local goal = NumberSequenceKeypoint.new(1,1)
holderGradient.Transparency = NumberSequence.new({start,a1,a2,goal})
end)
tweenS:Create(titleText,ti,{Position = UDim2.new(0,60,0,15), TextTransparency = 0}):Play()
tweenS:Create(descText,ti,{Position = UDim2.new(0,20,0,60), TextTransparency = 0}):Play()
local function rightTextTransparency(obj)
tweenNumber(100,ti,function(val)
val = val/100
local a1 = NumberSequenceKeypoint.new(1-val,0)
local a2 = NumberSequenceKeypoint.new(math.max(0,1-val-0.01),1)
if a1.Time == a2.Time then a2 = a1 end
local start = NumberSequenceKeypoint.new(0,a1 == a2 and 0 or 1)
local goal = NumberSequenceKeypoint.new(1,0)
obj.Transparency = NumberSequence.new({start,a2,a1,goal})
end)
end
rightTextTransparency(versionGradient)
rightTextTransparency(creatorGradient)
fastwait(0.9)
local progressTI = TweenInfo.new(0.25,Enum.EasingStyle.Quad,Enum.EasingDirection.Out)
tweenS:Create(statusText,progressTI,{Position = UDim2.new(0,20,0,120), TextTransparency = 0}):Play()
tweenS:Create(progressBar,progressTI,{Position = UDim2.new(0,60,0,145), Size = UDim2.new(0,100,0,4)}):Play()
fastwait(0.25)
local function setProgress(text,n)
statusText.Text = text
tweenS:Create(progressBar.Bar,progressTI,{Size = UDim2.new(n,0,1,0)}):Play()
end
local function close()
tweenS:Create(titleText,progressTI,{TextTransparency = 1}):Play()
tweenS:Create(descText,progressTI,{TextTransparency = 1}):Play()
tweenS:Create(versionText,progressTI,{TextTransparency = 1}):Play()
tweenS:Create(creatorText,progressTI,{TextTransparency = 1}):Play()
tweenS:Create(statusText,progressTI,{TextTransparency = 1}):Play()
tweenS:Create(progressBar,progressTI,{BackgroundTransparency = 1}):Play()
tweenS:Create(progressBar.Bar,progressTI,{BackgroundTransparency = 1}):Play()
tweenS:Create(progressBar.ImageLabel,progressTI,{ImageTransparency = 1}):Play()
tweenNumber(100,TweenInfo.new(0.4,Enum.EasingStyle.Back,Enum.EasingDirection.In),function(val)
val = val/250
local start = NumberSequenceKeypoint.new(0,0)
local a1 = NumberSequenceKeypoint.new(0.6+val,0)
local a2 = NumberSequenceKeypoint.new(math.min(1,0.601+val),1)
if a1.Time == a2.Time then a2 = a1 end
local goal = NumberSequenceKeypoint.new(1,a1 == a2 and 0 or 1)
holderGradient.Transparency = NumberSequence.new({start,a1,a2,goal})
end)
fastwait(0.5)
gui.Main.BackgroundTransparency = 1
outlinesGradient.Rotation = 30
tweenNumber(100,ti,function(val)
val = val/100
local start = NumberSequenceKeypoint.new(0,1)
local a1 = NumberSequenceKeypoint.new(val,1)
local a2 = NumberSequenceKeypoint.new(math.min(1,val+math.min(0.05,val)),0)
if a1.Time == a2.Time then a2 = a1 end
local goal = NumberSequenceKeypoint.new(1,a1 == a2 and 1 or 0)
outlinesGradient.Transparency = NumberSequence.new({start,a1,a2,goal})
holderGradient.Transparency = NumberSequence.new({start,a1,a2,goal})
end)
fastwait(0.45)
gui:Destroy()
end
return {SetProgress = setProgress, Close = close}
end
Main.CreateApp = function(data)
if Main.MenuApps[data.Name] then return end -- TODO: Handle conflict
local control = {}
local app = Main.AppTemplate:Clone()
local iconIndex = data.Icon
if data.IconMap and iconIndex then
if type(iconIndex) == "number" then
data.IconMap:Display(app.Main.Icon,iconIndex)
elseif type(iconIndex) == "string" then
data.IconMap:DisplayByKey(app.Main.Icon,iconIndex)
end
elseif type(iconIndex) == "string" then
app.Main.Icon.Image = iconIndex
else
app.Main.Icon.Image = ""
end
local function updateState()
app.Main.BackgroundTransparency = data.Open and 0 or (Lib.CheckMouseInGui(app.Main) and 0 or 1)
app.Main.Highlight.Visible = data.Open
end
local function enable(silent)
if data.Open then return end
data.Open = true
updateState()
if not silent then
if data.Window then data.Window:Show() end
if data.OnClick then data.OnClick(data.Open) end
end
end
local function disable(silent)
if not data.Open then return end
data.Open = false
updateState()
if not silent then
if data.Window then data.Window:Hide() end
if data.OnClick then data.OnClick(data.Open) end
end
end
updateState()
local ySize = service.TextService:GetTextSize(data.Name,14,Enum.Font.SourceSans,Vector2.new(62,999999)).Y
app.Main.Size = UDim2.new(1,0,0,math.clamp(46+ySize,60,74))
app.Main.AppName.Text = data.Name
app.Main.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
app.Main.BackgroundTransparency = 0
app.Main.BackgroundColor3 = Settings.Theme.ButtonHover
end
end)
app.Main.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
app.Main.BackgroundTransparency = data.Open and 0 or 1
app.Main.BackgroundColor3 = Settings.Theme.Button
end
end)
app.Main.MouseButton1Click:Connect(function()
if data.Open then disable() else enable() end
end)
local window = data.Window
if window then
window.OnActivate:Connect(function() enable(true) end)
window.OnDeactivate:Connect(function() disable(true) end)
end
app.Visible = true
app.Parent = Main.AppsContainer
Main.AppsFrame.CanvasSize = UDim2.new(0,0,0,Main.AppsContainerGrid.AbsoluteCellCount.Y*82 + 8)
control.Enable = enable
control.Disable = disable
Main.MenuApps[data.Name] = control
return control
end
Main.SetMainGuiOpen = function(val)
Main.MainGuiOpen = val
Main.MainGui.OpenButton.Text = val and "X" or "Dex"
if val then Main.MainGui.OpenButton.MainFrame.Visible = true end
Main.MainGui.OpenButton.MainFrame:TweenSize(val and UDim2.new(0,224,0,200) or UDim2.new(0,0,0,0),Enum.EasingDirection.Out,Enum.EasingStyle.Quad,0.2,true)
--Main.MainGui.OpenButton.BackgroundTransparency = val and 0 or (Lib.CheckMouseInGui(Main.MainGui.OpenButton) and 0 or 0.2)
service.TweenService:Create(Main.MainGui.OpenButton,TweenInfo.new(0.2,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{BackgroundTransparency = val and 0 or (Lib.CheckMouseInGui(Main.MainGui.OpenButton) and 0 or 0.2)}):Play()
if Main.MainGuiMouseEvent then Main.MainGuiMouseEvent:Disconnect() end
if not val then
local startTime = tick()
Main.MainGuiCloseTime = startTime
coroutine.wrap(function()
Lib.FastWait(0.2)
if not Main.MainGuiOpen and startTime == Main.MainGuiCloseTime then Main.MainGui.OpenButton.MainFrame.Visible = false end
end)()
else
Main.MainGuiMouseEvent = service.UserInputService.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 and not Lib.CheckMouseInGui(Main.MainGui.OpenButton) and not Lib.CheckMouseInGui(Main.MainGui.OpenButton.MainFrame) then
Main.SetMainGuiOpen(false)
end
end)
end
end
Main.CreateMainGui = function()
local gui = create({
{1,"ScreenGui",{IgnoreGuiInset=true,Name="MainMenu",}},
{2,"TextButton",{AnchorPoint=Vector2.new(0.5,0),AutoButtonColor=false,BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),BorderSizePixel=0,Font=4,Name="OpenButton",Parent={1},Position=UDim2.new(0.5,0,0,2),Size=UDim2.new(0,32,0,32),Text="Dex",TextColor3=Color3.new(1,1,1),TextSize=16,TextTransparency=0.20000000298023,}},
{3,"UICorner",{CornerRadius=UDim.new(0,4),Parent={2},}},
{4,"Frame",{AnchorPoint=Vector2.new(0.5,0),BackgroundColor3=Color3.new(0.17647059261799,0.17647059261799,0.17647059261799),ClipsDescendants=true,Name="MainFrame",Parent={2},Position=UDim2.new(0.5,0,1,-4),Size=UDim2.new(0,224,0,200),}},
{5,"UICorner",{CornerRadius=UDim.new(0,4),Parent={4},}},
{6,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),Name="BottomFrame",Parent={4},Position=UDim2.new(0,0,1,-24),Size=UDim2.new(1,0,0,24),}},
{7,"UICorner",{CornerRadius=UDim.new(0,4),Parent={6},}},
{8,"Frame",{BackgroundColor3=Color3.new(0.20392157137394,0.20392157137394,0.20392157137394),BorderSizePixel=0,Name="CoverFrame",Parent={6},Size=UDim2.new(1,0,0,4),}},
{9,"Frame",{BackgroundColor3=Color3.new(0.1294117718935,0.1294117718935,0.1294117718935),BorderSizePixel=0,Name="Line",Parent={8},Position=UDim2.new(0,0,0,-1),Size=UDim2.new(1,0,0,1),}},
{10,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Settings",Parent={6},Position=UDim2.new(1,-48,0,0),Size=UDim2.new(0,24,1,0),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
{11,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://6578871732",ImageTransparency=0.20000000298023,Name="Icon",Parent={10},Position=UDim2.new(0,4,0,4),Size=UDim2.new(0,16,0,16),}},
{12,"TextButton",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Font=3,Name="Information",Parent={6},Position=UDim2.new(1,-24,0,0),Size=UDim2.new(0,24,1,0),Text="",TextColor3=Color3.new(1,1,1),TextSize=14,}},
{13,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://6578933307",ImageTransparency=0.20000000298023,Name="Icon",Parent={12},Position=UDim2.new(0,4,0,4),Size=UDim2.new(0,16,0,16),}},
{14,"ScrollingFrame",{Active=true,AnchorPoint=Vector2.new(0.5,0),BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderColor3=Color3.new(0.1294117718935,0.1294117718935,0.1294117718935),BorderSizePixel=0,Name="AppsFrame",Parent={4},Position=UDim2.new(0.5,0,0,0),ScrollBarImageColor3=Color3.new(0,0,0),ScrollBarThickness=4,Size=UDim2.new(0,222,1,-25),}},
{15,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="Container",Parent={14},Position=UDim2.new(0,7,0,8),Size=UDim2.new(1,-14,0,2),}},
{16,"UIGridLayout",{CellSize=UDim2.new(0,66,0,74),Parent={15},SortOrder=2,}},
{17,"Frame",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Name="App",Parent={1},Size=UDim2.new(0,100,0,100),Visible=false,}},
{18,"TextButton",{AutoButtonColor=false,BackgroundColor3=Color3.new(0.2352941185236,0.2352941185236,0.2352941185236),BorderSizePixel=0,Font=3,Name="Main",Parent={17},Size=UDim2.new(1,0,0,60),Text="",TextColor3=Color3.new(0,0,0),TextSize=14,}},
{19,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://6579106223",ImageRectSize=Vector2.new(32,32),Name="Icon",Parent={18},Position=UDim2.new(0.5,-16,0,4),ScaleType=4,Size=UDim2.new(0,32,0,32),}},
{20,"TextLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,BorderSizePixel=0,Font=3,Name="AppName",Parent={18},Position=UDim2.new(0,2,0,38),Size=UDim2.new(1,-4,1,-40),Text="Explorer",TextColor3=Color3.new(1,1,1),TextSize=14,TextTransparency=0.10000000149012,TextTruncate=1,TextWrapped=true,TextYAlignment=0,}},
{21,"Frame",{BackgroundColor3=Color3.new(0,0.66666668653488,1),BorderSizePixel=0,Name="Highlight",Parent={18},Position=UDim2.new(0,0,1,-2),Size=UDim2.new(1,0,0,2),}},
})
Main.MainGui = gui
Main.AppsFrame = gui.OpenButton.MainFrame.AppsFrame
Main.AppsContainer = Main.AppsFrame.Container
Main.AppsContainerGrid = Main.AppsContainer.UIGridLayout
Main.AppTemplate = gui.App
Main.MainGuiOpen = false
local openButton = gui.OpenButton
openButton.BackgroundTransparency = 0.2
openButton.MainFrame.Size = UDim2.new(0,0,0,0)
openButton.MainFrame.Visible = false
openButton.MouseButton1Click:Connect(function()
Main.SetMainGuiOpen(not Main.MainGuiOpen)
end)
openButton.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
service.TweenService:Create(Main.MainGui.OpenButton,TweenInfo.new(0,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{BackgroundTransparency = 0}):Play()
end
end)
openButton.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement then
service.TweenService:Create(Main.MainGui.OpenButton,TweenInfo.new(0,Enum.EasingStyle.Quad,Enum.EasingDirection.Out),{BackgroundTransparency = Main.MainGuiOpen and 0 or 0.2}):Play()
end
end)
-- Create Main Apps
Main.CreateApp({Name = "Explorer", IconMap = Main.LargeIcons, Icon = "Explorer", Open = true, Window = Explorer.Window})
Main.CreateApp({Name = "Properties", IconMap = Main.LargeIcons, Icon = "Properties", Open = true, Window = Properties.Window})
Main.CreateApp({Name = "Script Viewer", IconMap = Main.LargeIcons, Icon = "Script_Viewer", Window = ScriptViewer.Window})
local cptsOnMouseClick = nil
Main.CreateApp({Name = "Click part to select", IconMap = Main.LargeIcons, Icon = 6, OnClick = function(callback)
if callback then
local mouse = Main.Mouse
cptsOnMouseClick = mouse.Button1Down:Connect(function()
pcall(function()
local object = mouse.Target
if nodes[object] then
selection:Set(nodes[object])
Explorer.ViewNode(nodes[object])
end
end)
end)
else if cptsOnMouseClick ~= nil then cptsOnMouseClick:Disconnect() cptsOnMouseClick = nil end end
end})
Lib.ShowGui(gui)
end
Main.SetupFilesystem = function()
if not env.writefile or not env.makefolder then return end
local writefile, makefolder = env.writefile, env.makefolder
makefolder("dex")
makefolder("dex/assets")
makefolder("dex/saved")
makefolder("dex/plugins")
makefolder("dex/ModuleCache")
end
Main.LocalDepsUpToDate = function()
return Main.DepsVersionData and Main.ClientVersion == Main.DepsVersionData[1]
end
Main.Init = function()
Main.Elevated = pcall(function() local a = cloneref(game["Run Service"].Parent:GetService("CoreGui")):GetFullName() end)
Main.InitEnv()
Main.LoadSettings()
Main.SetupFilesystem()
-- Load Lib
local intro = Main.CreateIntro("Initializing Library")
Lib = Main.LoadModule("Lib")
Lib.FastWait()
-- Init other stuff
--Main.IncompatibleTest()
-- Init icons
Main.MiscIcons = Lib.IconMap.new("rbxassetid://6511490623",256,256,16,16)
Main.MiscIcons:SetDict({
Reference = 0, Cut = 1, Cut_Disabled = 2, Copy = 3, Copy_Disabled = 4, Paste = 5, Paste_Disabled = 6,
Delete = 7, Delete_Disabled = 8, Group = 9, Group_Disabled = 10, Ungroup = 11, Ungroup_Disabled = 12, TeleportTo = 13,
Rename = 14, JumpToParent = 15, ExploreData = 16, Save = 17, CallFunction = 18, CallRemote = 19, Undo = 20,
Undo_Disabled = 21, Redo = 22, Redo_Disabled = 23, Expand_Over = 24, Expand = 25, Collapse_Over = 26, Collapse = 27,
SelectChildren = 28, SelectChildren_Disabled = 29, InsertObject = 30, ViewScript = 31, AddStar = 32, RemoveStar = 33, Script_Disabled = 34,
LocalScript_Disabled = 35, Play = 36, Pause = 37, Rename_Disabled = 38
})
Main.LargeIcons = Lib.IconMap.new("rbxassetid://6579106223",256,256,32,32)
Main.LargeIcons:SetDict({
Explorer = 0, Properties = 1, Script_Viewer = 2,
})
-- Fetch version if needed
intro.SetProgress("Fetching Roblox Version",0.2)
if Main.Elevated then
local fileVer = Lib.ReadFile("dex/deps_version.dat")
Main.ClientVersion = Version()
if fileVer then
Main.DepsVersionData = string.split(fileVer,"\n")
if Main.LocalDepsUpToDate() then
Main.RobloxVersion = Main.DepsVersionData[2]
end
end
Main.RobloxVersion = Main.RobloxVersion or game:HttpGet("http://setup.roblox.com/versionQTStudio")
end
-- Fetch external deps
intro.SetProgress("Fetching API",0.35)
API = Main.FetchAPI()
Lib.FastWait()
intro.SetProgress("Fetching RMD",0.5)
RMD = Main.FetchRMD()
Lib.FastWait()
-- Save external deps locally if needed
if Main.Elevated and env.writefile and not Main.LocalDepsUpToDate() then
env.writefile("dex/deps_version.dat",Main.ClientVersion.."\n"..Main.RobloxVersion)
env.writefile("dex/rbx_api.dat",Main.RawAPI)
env.writefile("dex/rbx_rmd.dat",Main.RawRMD)
end
-- Load other modules
intro.SetProgress("Loading Modules",0.75)
Main.AppControls.Lib.InitDeps(Main.GetInitDeps()) -- Missing deps now available
Main.LoadModules()
Lib.FastWait()
-- Init other modules
intro.SetProgress("Initializing Modules",0.9)
Explorer.Init()
Properties.Init()
ScriptViewer.Init()
Lib.FastWait()
-- Done
intro.SetProgress("Complete",1)
coroutine.wrap(function()
Lib.FastWait(1.25)
intro.Close()
end)()
-- Init window system, create main menu, show explorer and properties
Lib.Window.Init()
Main.CreateMainGui()
Explorer.Window:Show({Align = "right", Pos = 1, Size = 0.5, Silent = true})
Properties.Window:Show({Align = "right", Pos = 2, Size = 0.5, Silent = true})
Lib.DeferFunc(function() Lib.Window.ToggleSide("right") end)
end
return Main
end)()
-- Start Main.Init()