Created
January 28, 2025 23:40
-
-
Save YeesterPlus/e34154a3256b659bf920ddfec80da4e0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--[[ | |
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 = { | |
["'"] = "'", | |
["\""] = """, | |
["<"] = "<", | |
[">"] = ">", | |
["&"] = "&" | |
} | |
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