Skip to content

Instantly share code, notes, and snippets.

@YeesterPlus
Created January 28, 2025 23:40
Show Gist options
  • Save YeesterPlus/e34154a3256b659bf920ddfec80da4e0 to your computer and use it in GitHub Desktop.
Save YeesterPlus/e34154a3256b659bf920ddfec80da4e0 to your computer and use it in GitHub Desktop.
--[[
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.
This is the final version of this script.
You are encouraged to edit, fork, do whatever with this. I pretty much won't be updating it anymore.
Though I would appreciate it if you kept the credits in the script if you enjoy this hard work.
If you want more info, you can join the server: https://discord.io/zinnia
Note that very limited to no support will be provided.
]]
local nodes = {}
local selection
local clonerefs = 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.FindFirstAncestorWhichIsA
local getDescendants = game.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.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],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 = clonerefs(game:GetService("UserInputService")).InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
releaseEvent:Disconnect()
mouseEvent:Disconnect()
end
end)
mouseEvent = clonerefs(game: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.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.DescendantAdded:Connect(addObject)
descendantRemovingCon = game.DescendantRemoving:Connect(removeObject)
else
descendantAddedCon = game.DescendantAdded:Connect(function(obj) pcall(addObject,obj) end)
descendantRemovingCon = game.DescendantRemoving:Connect(function(obj) pcall(removeObject,obj) end)
end
if Settings.Explorer.UseNameWidth then
itemChangedCon = game.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.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.Destroy,game.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.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.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.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.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.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.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.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 game = game
local getDescs = game.GetDescendants
--local newNilMap = {}
--local newNilRoots = {}
--local nilRoots = Explorer.NilRoots
--local connect = game.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 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.FindFirstChild
local getCh = game.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 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 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])
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.IsA
local clone = game.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] = {Obj = game}
expanded[nodes[game]] = true
-- Nil Instances
if env.getnilinstances then
nodes[nilNode.Obj] = nilNode
end
Explorer.SetupConnections()
local insts = getDescendants(game)
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.GetPropertyChangedSignal
local getAttributeChangedSignal = game.GetAttributeChangedSignal
local isa = game.IsA
local getAttribute = game.GetAttribute
local setAttribute = game.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.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.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)
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()
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.PlaceId.."_Script_"..os.time()..".txt"
writefile(filename,source)
if movefileas then -- TODO: USE 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.protectgui then
env.protectgui(gui)
end
gui.Parent = Main.GuiHolder
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.HttpGet,game,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.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 = clonerefs(game: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 = clonerefs(game: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 = {
["'"] = "&apos;",
["\""] = "&quot;",
["<"] = "&lt;",
[">"] = "&gt;",
["&"] = "&amp;"
}
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 = clonerefs(game: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 = clonerefs(game:GetService("UserInputService"))
local mouse = clonerefs(game: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 = clonerefs(game:GetService("UserInputService"))
local mouse = clonerefs(game: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 = clonerefs(game:GetService("UserInputService"))
local mouse = clonerefs(game: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 = clonerefs(game: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 = clonerefs(game: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.saveinstance = saveinstance
-- debug
env.getupvalues = debug.getupvalues or getupvals
env.getconstants = debug.getconstants or getconsts
env.islclosure = islclosure or is_l_closure
env.checkcaller = checkcaller
env.getreg = getreg
env.getgc = getgc
-- other
env.setfflag = setfflag
env.decompile = decompile
env.protectgui = protect_gui or (syn and syn.protect_gui)
env.gethui = gethui
env.setclipboard = setclipboard
env.getnilinstances = getnilinstances or get_nil_instances
env.getloadedmodules = getloadedmodules
if identifyexecutor then Main.Executor = identifyexecutor() end
Main.GuiHolder = Main.Elevated and service.CoreGui or plr:FindFirstChildOfClass("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.protectgui then
env.protectgui(gui)
end
gui.Parent = Main.GuiHolder
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 = clonerefs(game: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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment