Run it
Copy the script and paste it into your executor.
cloneref = cloneref ((...) ... )
getnilinstances = getnilinstances (() {} )
nodes, service = {}, ({}, {
__index = (self, name)
serv = (:(name))
self[name] = serv
serv
})
selection = ;
EmbeddedModules = {
Explorer = ()
Decompile
Success, Decompile_Source = (()
:(, )
)
Success
CONSTANTS =
(()
(
.(
.(
Decompile_Source, , ,
), , CONSTANTS
),
)()
, )
_ENV = ( getfenv)()
Decompile = _ENV.decompile
Main,Lib,Apps,Settings
Explorer, Properties, ScriptViewer, Notebook
API,RMD,env,service,plr,create,createSimple
(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
()
Explorer = Apps.Explorer
Properties = Apps.Properties
ScriptViewer = Apps.ScriptViewer
Notebook = Apps.Notebook
()
Explorer = {}
tree,listEntries,explorerOrders,searchResults,specResults = {},{},{},{},{}
expanded
entryTemplate,treeFrame,toolBar,descendantAddedCon,descendantRemovingCon,itemChangedCon
ffa = .FindFirstAncestorWhichIsA
getDescendants = .GetDescendants
getTextSize = service.TextService.GetTextSize
updateDebounce,refreshDebounce = ,
nilNode = {Obj = .()}
idCounter =
scrollV,scrollH,clipboard
renameBox,renamingNode,searchFunc
sortingEnabled,autoUpdateSearch
, = ,
nilMap,nilCons = {},{}
connectSignal = .DescendantAdded.Connect
addObject,removeObject,moveObject = ,,
addObject = (root)
nodes[root]
isNil =
rootParObj = (root,)
par = nodes[rootParObj]
par
nilMap[root]
nilCons[root] = nilCons[root] {
(root.ChildAdded,addObject),
(root.AncestryChanged,moveObject),
}
par = nilNode
isNil =
nilMap[rootParObj] par == nilNode
nilMap[root] =
nilCons[root] = nilCons[root] {
(root.ChildAdded,addObject),
(root.AncestryChanged,moveObject),
}
isNil =
newNode = {Obj = root, Parent = par}
nodes[root] = newNode
sortingEnabled expanded[par] par.Sorted
left,right = ,#par
floor = .floor
sorter = Explorer.NodeSorter
pos = (right == )
pos
left >= right
(newNode,par[left])
pos = left
pos = left+
mid = ((left+right)/)
(newNode,par[mid])
right = mid-
left = mid+
.(par,pos,newNode)
par[#par+] = newNode
par.Sorted =
insts = (root)
i = ,#insts
obj = insts[i]
nodes[obj]
par = nodes[(obj,)]
par
newNode = {Obj = obj, Parent = par}
nodes[obj] = newNode
par[#par+] = newNode
isNil
nilMap[obj] =
nilCons[obj] = nilCons[obj] {
(obj.ChildAdded,addObject),
(obj.AncestryChanged,moveObject),
}
searchFunc autoUpdateSearch
({newNode})
updateDebounce Explorer.(par)
expanded[par]
Explorer.()
refreshDebounce
Explorer.()
removeObject = (root)
node = nodes[root]
node
nilMap[node.Obj]
(node.Obj)
par = node.Parent
par
par.HasDel =
(root)
i = ,#root
node = root[i]
node.Del
nodes[node.Obj] =
#node > (node)
(node)
node.Del =
nodes[root] =
par updateDebounce Explorer.(par)
expanded[par]
Explorer.()
refreshDebounce
Explorer.()
moveObject = (obj)
node = nodes[obj]
node
oldPar = node.Parent
newPar = nodes[(obj,)]
oldPar == newPar
newPar
nilMap[obj]
newPar = nilNode
nilMap[newPar.Obj] newPar == nilNode
nilMap[obj] =
nilCons[obj] = nilCons[obj] {
(obj.ChildAdded,addObject),
(obj.AncestryChanged,moveObject),
}
oldPar
parPos = .(oldPar,node)
parPos .(oldPar,parPos)
node.Id =
node.Parent = newPar
sortingEnabled expanded[newPar] newPar.Sorted
left,right = ,#newPar
floor = .floor
sorter = Explorer.NodeSorter
pos = (right == )
pos
left >= right
(node,newPar[left])
pos = left
pos = left+
mid = ((left+right)/)
(node,newPar[mid])
right = mid-
left = mid+
.(newPar,pos,node)
newPar[#newPar+] = node
newPar.Sorted =
searchFunc searchResults[node]
currentNode = node.Parent
currentNode ( searchResults[currentNode] expanded[currentNode] == )
expanded[currentNode] =
searchResults[currentNode] =
currentNode = currentNode.Parent
updateDebounce (Explorer.(newPar) Explorer.(oldPar))
expanded[newPar] expanded[oldPar]
Explorer.()
refreshDebounce
Explorer.()
Explorer.ViewWidth =
Explorer.Index =
Explorer.EntryIndent =
Explorer.FreeWidth =
Explorer.GuiElems = {}
Explorer.InitRenameBox = ()
renameBox = ({{,,{BackgroundColor3=.(,,),BorderColor3=.(,,),BorderMode=,ClearTextOnFocus=,Font=,Name=,PlaceholderColor3=.(,,),Position=.(,,,),Size=.(,,,),Text=,TextColor3=.(,,),TextSize=,TextXAlignment=,Visible=,ZIndex=}}})
renameBox.Parent = Explorer.Window.GuiElems.Content.List
renameBox.FocusLost:(()
renamingNode
(() renamingNode.Obj.Name = renameBox.Text )
renamingNode =
Explorer.()
)
renameBox.Focused:(()
renameBox.SelectionStart =
renameBox.CursorPosition = #renameBox.Text +
)
Explorer.SetRenamingNode = (node)
renamingNode = node
renameBox.Text = (node.Obj)
renameBox:()
Explorer.()
Explorer.SetSortingEnabled = (val)
sortingEnabled = val
Settings.Explorer.Sorting = val
Explorer.UpdateView = ()
maxNodes = .(treeFrame.AbsoluteSize.Y / )
maxX = treeFrame.AbsoluteSize.X
totalWidth = Explorer.ViewWidth + Explorer.FreeWidth
scrollV.VisibleSpace = maxNodes
scrollV.TotalSpace = #tree +
scrollH.VisibleSpace = maxX
scrollH.TotalSpace = totalWidth
scrollV.Gui.Visible = #tree + > maxNodes
scrollH.Gui.Visible = totalWidth > maxX
oldSize = treeFrame.Size
treeFrame.Size = .(,(scrollV.Gui.Visible - ),,(scrollH.Gui.Visible - -))
oldSize ~= treeFrame.Size
Explorer.()
scrollV:()
scrollH:()
renameBox.Size = .(,maxX-,,)
scrollV.Gui.Visible scrollH.Gui.Visible
scrollV.Gui.Size = .(,,,-)
scrollH.Gui.Size = .(,-,,)
Explorer.Window.GuiElems.Content.ScrollCorner.Visible =
scrollV.Gui.Size = .(,,,-)
scrollH.Gui.Size = .(,,,)
Explorer.Window.GuiElems.Content.ScrollCorner.Visible =
Explorer.Index = scrollV.Index
Explorer.NodeSorter = (a,b)
a.Del b.Del
aClass = a.Class
bClass = b.Class
aClass aClass = a.Obj.ClassName a.Class = aClass
bClass bClass = b.Obj.ClassName b.Class = bClass
aOrder = explorerOrders[aClass]
bOrder = explorerOrders[bClass]
aOrder aOrder = RMD.Classes[aClass] (RMD.Classes[aClass].ExplorerOrder) explorerOrders[aClass] = aOrder
bOrder bOrder = RMD.Classes[bClass] (RMD.Classes[bClass].ExplorerOrder) explorerOrders[bClass] = bOrder
aOrder ~= bOrder
aOrder < bOrder
aName,bName = (a.Obj),(b.Obj)
aName ~= bName
aName < bName
aClass ~= bClass
aClass < bClass
aId = a.Id aId aId = idCounter idCounter = (idCounter+)% a.Id = aId
bId = b.Id bId bId = idCounter idCounter = (idCounter+)% b.Id = bId
aId < bId
Explorer.Update = ()
.(tree)
maxNameWidth,maxDepth,count = ,,
nameCache = {}
font = .Font.SourceSans
size = .(.huge,)
useNameWidth = Settings.Explorer.UseNameWidth
tSort = .sort
sortFunc = Explorer.NodeSorter
isSearching = (expanded == Explorer.SearchExpanded)
textServ = service.TextService
(root,depth)
depth > maxDepth maxDepth = depth
depth = depth +
sortingEnabled root.Sorted
(root,sortFunc)
root.Sorted =
i = ,#root
n = root[i]
(isSearching searchResults[n]) n.Del
useNameWidth
nameWidth = n.NameWidth
nameWidth
objName = (n.Obj)
nameWidth = nameCache[objName]
nameWidth
nameWidth = (textServ,objName,,font,size).X
nameCache[objName] = nameWidth
n.NameWidth = nameWidth
nameWidth > maxNameWidth
maxNameWidth = nameWidth
tree[count] = n
count = count +
expanded[n] #n >
(n,depth)
(nodes[],)
env.getnilinstances
(isSearching searchResults[nilNode])
tree[count] = nilNode
count = count +
expanded[nilNode]
(nilNode,)
Explorer.MaxNameWidth = maxNameWidth
Explorer.MaxDepth = maxDepth
Explorer.ViewWidth = useNameWidth Explorer.EntryIndent*maxDepth + maxNameWidth + Explorer.EntryIndent*maxDepth +
Explorer.()
Explorer.StartDrag = (offX,offY)
Explorer.Dragging
i,v , selection.List
Obj = v.Obj
Obj.Parent == Obj:()
Explorer.Dragging =
dragTree = treeFrame:()
dragTree:()
i,v (listEntries)
node = tree[i + Explorer.Index]
node selection.Map[node]
clone = v:()
clone.Active =
clone.Indent.Expand.Visible =
clone.Parent = dragTree
newGui = .()
newGui.DisplayOrder = Main.DisplayOrders.Menu
dragTree.Parent = newGui
Lib.(newGui)
dragOutline = ({
{,,{BackgroundColor3=.(,,),BackgroundTransparency=,Name=,Size=.(,,,),}},
{,,{BackgroundColor3=.(,,),BorderSizePixel=,Name=,Parent={},Size=.(,,,),ZIndex=,}},
{,,{BackgroundColor3=.(,,),BorderSizePixel=,Name=,Parent={},Position=.(,,,-),Size=.(,,,),ZIndex=,}},
{,,{BackgroundColor3=.(,,),BorderSizePixel=,Name=,Parent={},Size=.(,,,),ZIndex=,}},
{,,{BackgroundColor3=.(,,),BorderSizePixel=,Name=,Parent={},Position=.(,-,,),Size=.(,,,),ZIndex=,}},
})
dragOutline.Parent = treeFrame
mouse = Main.Mouse service.Players.LocalPlayer:()
()
posX = mouse.X - offX
posY = mouse.Y - offY
dragTree.Position = .(,posX,,posY)
i = ,#listEntries
entry = listEntries[i]
Lib.(entry)
dragOutline.Position = .(,entry.Indent.Position.X.Offset-scrollH.Index,,entry.Position.Y.Offset)
dragOutline.Size = .(,entry.Size.X.Offset-entry.Indent.Position.X.Offset,,)
dragOutline.Visible =
dragOutline.Visible =
()
input = service.UserInputService
mouseEvent,releaseEvent
mouseEvent = input.InputChanged:((input)
input.UserInputType == .UserInputType.MouseMovement input.UserInputType == .UserInputType.Touch
()
)
releaseEvent = input.InputEnded:((input)
input.UserInputType == .UserInputType.MouseButton1 input.UserInputType == .UserInputType.Touch
releaseEvent:()
mouseEvent:()
newGui:()
dragOutline:()
Explorer.Dragging =
i = ,#listEntries
Lib.(listEntries[i])
node = tree[i + Explorer.Index]
node
selection.Map[node]
newPar = node.Obj
sList = selection.List
i = ,#sList
n = sList[i]
(() n.Obj.Parent = newPar )
Explorer.(sList[])
)
Explorer.NewListEntry = (index)
newEntry = entryTemplate:()
newEntry.Position = .(,,,*(index-))
isRenaming =
newEntry.InputBegan:((input)
node = tree[index + Explorer.Index]
node selection.Map[node] (input.UserInputType ~= .UserInputType.MouseMovement input.UserInputType ~= .UserInputType.Touch)
newEntry.Indent.BackgroundColor3 = Settings.Theme.Button
newEntry.Indent.BorderSizePixel =
newEntry.Indent.BackgroundTransparency =
)
newEntry.InputEnded:((input)
node = tree[index + Explorer.Index]
node selection.Map[node] (input.UserInputType ~= .UserInputType.MouseMovement input.UserInputType ~= .UserInputType.Touch)
newEntry.Indent.BackgroundTransparency =
)
newEntry.MouseButton1Down:(()
)
newEntry.MouseButton1Up:(()
)
newEntry.InputBegan:((input)
input.UserInputType == .UserInputType.MouseButton1 input.UserInputType == .UserInputType.Touch
releaseEvent, mouseEvent
mouse = Main.Mouse plr:()
startX, startY
input.UserInputType == .UserInputType.Touch
startX = input.Position.X
startY = input.Position.Y
startX = mouse.X
startY = mouse.Y
listOffsetX = startX - treeFrame.AbsolutePosition.X
listOffsetY = startY - treeFrame.AbsolutePosition.Y
releaseEvent = service.UserInputService.InputEnded:((input)
input.UserInputType == .UserInputType.MouseButton1 input.UserInputType == .UserInputType.Touch
releaseEvent:()
mouseEvent:()
)
mouseEvent = service.UserInputService.InputChanged:((input)
input.UserInputType == .UserInputType.MouseMovement input.UserInputType == .UserInputType.Touch
currentX, currentY
input.UserInputType == .UserInputType.Touch
currentX = input.Position.X
currentY = input.Position.Y
currentX = mouse.X
currentY = mouse.Y
deltaX = currentX - startX
deltaY = currentY - startY
dist = .(deltaX^ + deltaY^)
dist >
releaseEvent:()
mouseEvent:()
isRenaming =
Explorer.(listOffsetX, listOffsetY)
)
)
newEntry.MouseButton2Down:(()
)
newEntry.Indent.Expand.InputBegan:((input)
node = tree[index + Explorer.Index]
node (input.UserInputType ~= .UserInputType.MouseMovement input.UserInputType ~= .UserInputType.Touch)
input.UserInputType == .UserInputType.Touch
Explorer.MiscIcons:(newEntry.Indent.Expand.Icon, expanded[node] )
input.UserInputType == .UserInputType.MouseMovement
Explorer.MiscIcons:(newEntry.Indent.Expand.Icon, expanded[node] )
)
newEntry.Indent.Expand.InputEnded:((input)
node = tree[index + Explorer.Index]
node (input.UserInputType ~= .UserInputType.MouseMovement input.UserInputType ~= .UserInputType.Touch)
input.UserInputType == .UserInputType.Touch
Explorer.MiscIcons:(newEntry.Indent.Expand.Icon, expanded[node] )
input.UserInputType == .UserInputType.MouseMovement
Explorer.MiscIcons:(newEntry.Indent.Expand.Icon, expanded[node] )
)
newEntry.Indent.Expand.MouseButton1Down:(()
node = tree[index + Explorer.Index]
node #node ==
expanded[node] = expanded[node]
Explorer.()
Explorer.()
)
newEntry.Parent = treeFrame
newEntry
Explorer.Refresh = ()
maxNodes = .(.((treeFrame.AbsoluteSize.Y) / ), )
renameNodeVisible =
isa = .IsA
i = ,maxNodes
entry = listEntries[i]
listEntries[i] entry = Explorer.(i) listEntries[i] = entry Explorer.ClickSystem:(entry)
node = tree[i + Explorer.Index]
node
obj = node.Obj
depth = Explorer.EntryIndent*Explorer.(node)
entry.Visible =
entry.Position = .(,-scrollH.Index,,entry.Position.Y.Offset)
entry.Size = .(,Explorer.ViewWidth,,)
entry.Indent.EntryName.Text = (node.Obj)
entry.Indent.Position = .(,depth,,)
entry.Indent.Size = .(,-depth,,)
entry.Indent.EntryName.TextTruncate = (Settings.Explorer.UseNameWidth .TextTruncate.None .TextTruncate.AtEnd)
Explorer.MiscIcons:(entry.Indent.Icon, obj.ClassName)
selection.Map[node]
entry.Indent.BackgroundColor3 = Settings.Theme.ListSelection
entry.Indent.BorderSizePixel =
entry.Indent.BackgroundTransparency =
Lib.(entry)
entry.Indent.BackgroundColor3 = Settings.Theme.Button
entry.Indent.BackgroundTransparency =
node == renamingNode
renameNodeVisible =
renameBox.Position = .(,depth+-scrollH.Index,,entry.Position.Y.Offset+)
renameBox.Visible =
#node > expanded[node] ~=
Lib.(entry.Indent.Expand)
Explorer.MiscIcons:(entry.Indent.Expand.Icon, expanded[node] )
Explorer.MiscIcons:(entry.Indent.Expand.Icon, expanded[node] )
entry.Indent.Expand.Visible =
entry.Indent.Expand.Visible =
entry.Visible =
renameNodeVisible
renameBox.Visible =
i = maxNodes+, #listEntries
Explorer.ClickSystem:(listEntries[i])
listEntries[i]:()
listEntries[i] =
Explorer.PerformUpdate = (instant)
updateDebounce =
Lib.( instant )
updateDebounce
updateDebounce =
Explorer.Window:()
Explorer.()
Explorer.()
Explorer.ForceUpdate = (norefresh)
updateDebounce =
Explorer.()
norefresh Explorer.()
Explorer.PerformRefresh = ()
refreshDebounce =
Lib.()
refreshDebounce =
updateDebounce Explorer.Window:()
Explorer.()
Explorer.IsNodeVisible = (node)
node
curNode = node.Parent
curNode
expanded[curNode]
curNode = curNode.Parent
Explorer.NodeDepth = (node)
depth =
node == nilNode
curNode = node.Parent
curNode
curNode == nilNode depth = depth +
curNode = curNode.Parent
depth = depth +
depth
Explorer.SetupConnections = ()
descendantAddedCon descendantAddedCon:()
descendantRemovingCon descendantRemovingCon:()
itemChangedCon itemChangedCon:()
Main.Elevated
descendantAddedCon = .DescendantAdded:(addObject)
descendantRemovingCon = .DescendantRemoving:(removeObject)
descendantAddedCon = .DescendantAdded:((obj) (addObject,obj) )
descendantRemovingCon = .DescendantRemoving:((obj) (removeObject,obj) )
Settings.Explorer.UseNameWidth
itemChangedCon = .ItemChanged:((obj,prop)
prop == nodes[obj]
(obj)
prop == nodes[obj]
nodes[obj].NameWidth =
)
itemChangedCon = .ItemChanged:((obj,prop)
prop == nodes[obj]
(obj)
)
Explorer.ViewNode = (node)
node
Explorer.(node)
Explorer.()
visibleSpace = scrollV.VisibleSpace
i,v ,tree
v == node
relative = i -
Explorer.Index > relative
scrollV.Index = relative
Explorer.Index + visibleSpace - <= relative
scrollV.Index = relative - visibleSpace +
scrollV:() Explorer.Index = scrollV.Index
Explorer.()
Explorer.ViewObj = (obj)
Explorer.(nodes[obj])
Explorer.MakeNodeVisible = (node,expandRoot)
node
hasExpanded =
expandRoot expanded[node]
expanded[node] =
hasExpanded =
currentNode = node.Parent
currentNode
hasExpanded =
expanded[currentNode] =
currentNode = currentNode.Parent
hasExpanded updateDebounce
.(Explorer.PerformUpdate)()
Explorer.ShowRightClick = (MousePos)
Mouse = MousePos Main.Mouse
context = Explorer.RightClickContext
absoluteSize = context.Gui.AbsoluteSize
context.MaxHeight = (absoluteSize.Y <= (absoluteSize.Y - ))
context:()
sList = selection.List
sMap = selection.Map
emptyClipboard = #clipboard ==
presentClasses = {}
apiClasses = API.Classes
i = , #sList
node = sList[i]
class = node.Class
class class = node.Obj.ClassName node.Class = class
curClass = apiClasses[class]
curClass presentClasses[curClass.Name]
presentClasses[curClass.Name] =
curClass = curClass.Superclass
context:()
context:()
context:(, emptyClipboard)
context:()
context:()
context:(, #sList ~= )
context:()
context:()
context:()
context:()
context:()
context:()
context:()
context:()
expanded == Explorer.SearchExpanded context:()
env.setclipboard context:()
context:()
context:()
context:()
context:()
context:()
context:()
presentClasses[] presentClasses[]
context:()
context:()
presentClasses[] context:()
presentClasses[]
context:()
context:()
presentClasses[] context:(, == )
presentClasses[] context:(, == )
presentClasses[] context:(, == )
presentClasses[] context:()context:()
presentClasses[]
context:()
context:()
presentClasses[]
context:()
context:()
sMap[nilNode]
context:()
context:()
Explorer.LastRightClickX, Explorer.LastRightClickY = Mouse.X, Mouse.Y
context:(Mouse.X, Mouse.Y)
Explorer.InitRightClick = ()
context = Lib.ContextMenu.()
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , DisabledIcon = , Shortcut = , OnClick = ()
destroy,clone = .Destroy,.Clone
sList,newClipboard = selection.List,{}
count =
i = ,#sList
inst = sList[i].Obj
s,cloned = (clone,inst)
s cloned
newClipboard[count] = cloned
count = count +
(destroy,inst)
clipboard = newClipboard
selection:()
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , DisabledIcon = , Shortcut = , OnClick = ()
clone = .Clone
sList,newClipboard = selection.List,{}
count =
i = ,#sList
inst = sList[i].Obj
s,cloned = (clone,inst)
s cloned
newClipboard[count] = cloned
count = count +
clipboard = newClipboard
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , DisabledIcon = , Shortcut = , OnClick = ()
sList = selection.List
newSelection = {}
count =
i = ,#sList
node = sList[i]
inst = node.Obj
Explorer.(node,)
c = ,#clipboard
cloned = clipboard[c]:()
cloned
cloned.Parent = inst
clonedNode = nodes[cloned]
clonedNode newSelection[count] = clonedNode count = count +
selection:(newSelection)
#newSelection >
Explorer.(newSelection[])
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , DisabledIcon = , Shortcut = , OnClick = ()
clone = .Clone
sList = selection.List
newSelection = {}
count =
i = ,#sList
node = sList[i]
inst = node.Obj
instPar = node.Parent node.Parent.Obj
Explorer.(node)
s,cloned = (clone,inst)
s cloned
cloned.Parent = instPar
clonedNode = nodes[cloned]
clonedNode newSelection[count] = clonedNode count = count +
selection:(newSelection)
#newSelection >
Explorer.(newSelection[])
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , DisabledIcon = , Shortcut = , OnClick = ()
destroy = .Destroy
sList = selection.List
i = ,#sList
(destroy,sList[i].Obj)
selection:()
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , DisabledIcon = , Shortcut = , OnClick = ()
sList = selection.List
sList[]
Explorer.(sList[])
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , DisabledIcon = , Shortcut = , OnClick = ()
sList = selection.List
#sList ==
model = .(,sList[#sList].Obj.Parent)
i = ,#sList
(() sList[i].Obj.Parent = model )
nodes[model]
selection:(nodes[model])
Explorer.(nodes[model])
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , DisabledIcon = , Shortcut = , OnClick = ()
newSelection = {}
count =
isa = .IsA
(node)
par = node.Parent.Obj
ch = {}
chCount =
i = ,#node
n = node[i]
newSelection[count] = n
ch[chCount] = n
count = count +
chCount = chCount +
i = ,#ch
(() ch[i].Obj.Parent = par )
node.Obj:()
i,v ,selection.List
(v.Obj,)
(v)
selection:(newSelection)
#newSelection >
Explorer.(newSelection[])
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , DisabledIcon = , OnClick = ()
newSelection = {}
count =
sList = selection.List
i = ,#sList
node = sList[i]
ind = ,#node
cNode = node[ind]
ind == Explorer.(cNode)
newSelection[count] = cNode
count = count +
selection:(newSelection)
#newSelection >
Explorer.(newSelection[])
Explorer.()
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , OnClick = ()
newSelection = {}
count =
sList = selection.List
i = ,#sList
node = sList[i]
node.Parent
newSelection[count] = node.Parent
count = count +
selection:(newSelection)
#newSelection >
Explorer.(newSelection[])
Explorer.()
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , OnClick = ()
sList = selection.List
plrRP = plr.Character plr.Character:()
plrRP
_,node , sList
Obj = node.Obj
Obj:()
Obj.CanCollide
plr.Character:(Obj.Position)
plrRP.CFrame = .(Obj.Position + Settings.Explorer.TeleportToOffset)
Obj:()
Obj.PrimaryPart
Obj.PrimaryPart.CanCollide
plr.Character:(Obj.PrimaryPart.Position)
plrRP.CFrame = .(Obj.PrimaryPart.Position + Settings.Explorer.TeleportToOffset)
part = Obj:(, )
part nodes[part]
part.CanCollide
plr.Character:(part.Position)
plrRP.CFrame = .(part.Position + Settings.Explorer.TeleportToOffset)
Obj.WorldPivot
plrRP.CFrame = Obj.WorldPivot
})
OldAnimation
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , OnClick = ()
sList = selection.List
i = , #sList
node = sList[i]
Obj = node.Obj
Obj:() Obj:()
})
OldAnimation
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , OnClick = ()
sList = selection.List
Humanoid = plr.Character plr.Character:()
Humanoid
i = , #sList
node = sList[i]
Obj = node.Obj
Obj:()
OldAnimation OldAnimation:()
OldAnimation = Humanoid:(Obj)
OldAnimation:()
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , OnClick = ()
sList = selection.List
Humanoid = plr.Character plr.Character:()
Humanoid
i = , #sList
node = sList[i]
Obj = node.Obj
Obj:()
OldAnimation OldAnimation:()
Humanoid:(Obj):()
})
context:(,{Name = , OnClick = ()
sList = selection.List
(node)
expanded[node] =
i = ,#node
#node[i] >
(node[i])
i = ,#sList
(sList[i])
Explorer.()
})
context:(,{Name = , OnClick = ()
sList = selection.List
(node)
expanded[node] =
i = ,#node
#node[i] >
(node[i])
i = ,#sList
(sList[i])
Explorer.()
})
context:(,{Name = , OnClick = ()
newSelection = {}
count =
sList = selection.List
i = ,#sList
newSelection[count] = sList[i]
count = count +
selection:(newSelection)
Explorer.()
#newSelection >
Explorer.(newSelection[])
})
clth = (str)
str:(, ) == str = str:(, , )
str:(, + #plr.Name) == .. plr.Name str = str:( .. plr.Name, , )
str
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
sList = selection.List
#sList ==
env.((Explorer.(sList[].Obj)))
#sList >
resList = {}
count =
i = ,#sList
path = ..(Explorer.(sList[i].Obj))..
#path >
resList[count] = path
count = count+
resList[count] =
env.(.(resList,))
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , OnClick = ()
mouse = Main.Mouse
x,y = Explorer.LastRightClickX mouse.X, Explorer.LastRightClickY mouse.Y
Explorer.InsertObjectContext:(x,y)
})
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
})
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , OnClick = ()
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , OnClick = ()
})
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
sList = selection.List
isa = .IsA
i = ,#sList
node = sList[i]
(node.Obj,) (node.Obj, )
.CurrentCamera.CameraSubject = node.Obj
, OnRightClick = ()
.CurrentCamera.CameraSubject = plr.Character
})
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
hrp = plr.Character plr.Character:()
hrp
_, v (selection.List) v.Obj v.Obj:() (hrp, v.Obj.Parent, )
})
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
hrp = plr.Character plr.Character:()
hrp
_, v (selection.List) v.Obj v.Obj:() (v.Obj)
})
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
hrp = plr.Character plr.Character:()
hrp
_, v (selection.List) v.Obj v.Obj:() (v.Obj)
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , OnClick = ()
scr = selection.List[] selection.List[].Obj
scr ScriptViewer.(scr)
})
context:(,{Name = , IconMap = Explorer.MiscIcons, Icon = , OnClick = ()
_,v , selection.List
v.Obj:()
success, bytecode = (getscriptbytecode, scr)
success (bytecode) ==
Name = ():(.PlaceId, scr.Name)
(Name, bytecode)
.()
})
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
newSelection = {}
count =
sList = selection.List
isa = .IsA
i = ,#sList
node = sList[i]
(node.Obj,) nodes[node.Obj.Character]
newSelection[count] = nodes[node.Obj.Character]
count = count +
selection:(newSelection)
#newSelection >
Explorer.(newSelection[])
Explorer.()
})
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
newSelection = {}
count =
sList = selection.List
isa = .IsA
i = ,#sList
node = sList[i]
Obj = node.Obj
Obj:() Obj.Character
.CurrentCamera.CameraSubject = Obj.Character
})
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
(() nodes[plr] selection:(nodes[plr]) Explorer.(nodes[plr]) )
})
context:(,{Name = , IconMap = Explorer.ClassIcons, Icon = , OnClick = ()
newSelection = {}
sList = selection.List
i,v , service.Players:()
v.Character nodes[v.Character]
i == Explorer.(v.Character)
.(newSelection, nodes[v.Character])
selection:(newSelection)
#newSelection >
Explorer.(newSelection[])
Explorer.()
})
context:(,{Name = , OnClick = ()
Explorer.()
})
context:(,{Name = , OnClick = ()
Explorer.()
})
Explorer.RightClickContext = context
Explorer.HideNilInstances = ()
.(nilMap)
disconnectCon = .().ChildAdded:(() ).Disconnect
i,v ,nilCons
(v[])
(v[])
.(nilCons)
i = ,#nilNode
.(removeObject)(nilNode[i].Obj)
Explorer.()
Explorer.()
Explorer.RefreshNilInstances = ()
env.getnilinstances
nilInsts = env.()
=
getDescs = .GetDescendants
i = ,#nilInsts
obj = nilInsts[i]
obj ~=
nilMap[obj] =
descs = (obj)
j = ,#descs
nilMap[descs[j]] =
i = ,#nilInsts
obj = nilInsts[i]
node = nodes[obj]
node .(addObject)(obj)
Explorer.()
Explorer.()
Explorer.GetInstancePath = (obj)
ffc = .FindFirstChild
getCh = .GetChildren
path =
curObj = obj
ts =
match = .match
gsub = .gsub
tableFind = .find
useGetCh = Settings.Explorer.CopyPathUseGetChildren
formatLuaString = Lib.FormatLuaString
curObj
curObj ==
path = ..path
className = curObj.ClassName
curName = (curObj)
indexName
(curName,)
indexName = ..curName
cleanName = (curName)
indexName = ..cleanName..
parObj = curObj.Parent
parObj
fc = (parObj,curName)
useGetCh fc fc ~= curObj
parCh = (parObj)
fcInd = (parCh,curObj)
indexName = ..fcInd..
parObj == API.Classes[className] API.Classes[className].Tags.Service
indexName = ..className..
parObj ==
getnil =
gotnil =
indexName = getnil .. gotnil:(curObj.Name, className)
path = indexName..path
curObj = parObj
path
Explorer.DefaultProps = {
[] = {
Position = (Obj)
Player = service.Players.LocalPlayer
Player.Character Player.Character:()
Obj.Position = (Player.Character.HumanoidRootPart.CFrame * .(, , -)).p
Obj.Position
,
Anchored =
},
[] = {
Position = (Obj) (Obj.Parent:() .(, , , )) Obj.Position ,
Active =
}
}
Explorer.InitInsertObject = ()
context = Lib.ContextMenu.()
context.SearchEnabled =
context.MaxHeight =
context:({
ContentColor = Settings.Theme.Main2,
OutlineColor = Settings.Theme.Outline1,
DividerColor = Settings.Theme.Outline1,
TextColor = Settings.Theme.Text,
HighlightColor = Settings.Theme.ButtonHover
})
classes = {}
i,class ,API.Classes
tags = class.Tags
tags.NotCreatable tags.Service
rmdEntry = RMD.Classes[class.Name]
classes[#classes+] = {class,rmdEntry rmdEntry.ClassCategory }
.(classes,(a,b)
a[] ~= b[]
a[] < b[]
a[].Name < b[].Name
)
(obj)
class, props (Explorer.DefaultProps)
obj:(class)
prop, value (props)
obj[prop] = ((value) == (obj)) value
(className)
sList = selection.List
instNew = .new
i = ,#sList
node = sList[i]
obj = node.Obj
Explorer.(node, )
success, obj = (instNew, className, obj)
success obj (obj)
lastCategory =
i = ,#classes
class = classes[i][]
rmdEntry = RMD.Classes[class.Name]
iconInd = rmdEntry (rmdEntry.ExplorerImageIndex)
category = classes[i][]
lastCategory ~= category
context:(category)
lastCategory = category
context:({Name = class.Name, IconMap = Explorer.ClassIcons, Icon = iconInd, OnClick = onClick})
Explorer.InsertObjectContext = context
Explorer._SearchFilters = {}
Filters = Explorer._SearchFilters
(list, func)
_,v , list
Filters[v:() .. ] = func
Only = {
remotes = {, , , },
scripts = {, , },
players = {}
}
({, }, (Obj, str) Obj.Parent (Obj.Parent.Name:()):(str) )
({, }, (Obj, str) (Obj.ClassName:()):(str) )
({, }, (Obj, str) Obj:(str) )
({, }, (Obj, str)
Special = Only[str]
Special .(Special, Obj.ClassName)
)
Explorer.DoSearch = (query)
.(Explorer.SearchExpanded)
.(searchResults)
expanded = (#query == Explorer.Expanded) Explorer.SearchExpanded
tostr = ;
tfind = .find;
Filters = Explorer._SearchFilters
expandTable = Explorer.SearchExpanded
allnodes = nodes[]
defaultSearch = ((Obj, str) (Obj.Name:()):(str, , ) )
(root, str, func)
expandedpar =
_,node (root)
(node.Obj, str)
expandTable[node] =
searchResults[node] =
expandedpar
parnode = node.Parent
parnode ( searchResults[parnode] expandTable[parnode] == )
expanded[parnode] =
searchResults[parnode] =
parnode = parnode.Parent
expandedpar =
#node > (node, str, func)
(query)
query:() ==
lower = query:()
split = lower:()
Filter = (Filters[split[] .. ])
Filter
(allnodes, (split[] ), Filter)
(allnodes, (lower ), defaultSearch)
_,v (query:())
v:() >
(v)
Explorer.()
Explorer.ClearSearch = ()
Explorer.GuiElems.SearchBar.Text =
expanded = Explorer.Expanded
searchFunc =
Explorer.InitSearch = ()
TweenService = service.TweenService
SearchFrame = Explorer.GuiElems.ToolBar.SearchFrame
searchBox = SearchFrame.SearchBox
Explorer.GuiElems.SearchBar = searchBox
TweenInfo = TweenInfo.(, .EasingStyle.Quint)
Tweens = {
Start = TweenService:(SearchFrame.UIStroke, TweenInfo, { Color = .(, , ) }),
End = TweenService:(SearchFrame.UIStroke, TweenInfo, { Color = .(, , ) })
}
searchBox.FocusLost:(() Tweens.End:() )
searchBox.Focused:(() Tweens.Start:() )
Lib.ViewportTextBox.(searchBox)
searchBox.FocusLost:(()
Explorer.(searchBox.Text)
)
Explorer.InitEntryTemplate = ()
entryTemplate = ({
{,,{AutoButtonColor=,BackgroundColor3=.(,,),BackgroundTransparency=,BorderColor3=.(,,),Font=,Name=,Position=.(,,,),Size=.(,,,),Text=,TextSize=,}},
{,,{BackgroundColor3=.(,,),BackgroundTransparency=,BorderColor3=.(,,),BorderSizePixel=,Name=,Parent={},Position=.(,,,),Size=.(,-,,),}},
{,,{BackgroundColor3=.(,,),BackgroundTransparency=,Font=,Name=,Parent={},Position=.(,,,),Size=.(,-,,),Text=,TextColor3=.(,,),TextSize=,TextXAlignment=,}},
{,,{BackgroundColor3=.(,,),BackgroundTransparency=,ClipsDescendants=,Font=,Name=,Parent={},Position=.(,-,,),Size=.(,,,),Text=,TextSize=,}},
{,,{BackgroundColor3=.(,,),BackgroundTransparency=,Image=,ImageRectOffset=.(,),ImageRectSize=.(,),Name=,Parent={},Position=.(,,,),ScaleType=,Size=.(,,,),}},
{,,{BackgroundColor3=.(,,),BackgroundTransparency=,ImageRectOffset=.(,),ImageRectSize=.(,),Name=,Parent={},Position=.(,,,),ScaleType=,Size=.(,,,),}},
})
sys = Lib.ClickSystem.()
sys.AllowedButtons = {,}
sys.OnDown:((item,combo,button)
ind = .(listEntries,item)
ind
node = tree[ind + Explorer.Index]
node
entry = listEntries[ind]
button ==
combo ==
node.Obj:()
ScriptViewer.(node.Obj)
#node > expanded[node] ~=
expanded[node] = expanded[node]
Explorer.()
Properties.(node.Obj)
sys.IsRenaming =
sys.IsRenaming = selection.Map[node]
Lib.()
selection.Piviot
fromIndex = .(tree,selection.Piviot)
toIndex = .(tree,node)
fromIndex toIndex
fromIndex,toIndex = .(fromIndex,toIndex),.(fromIndex,toIndex)
sList = selection.List
i = #sList,,-
elem = sList[i]
selection.ShiftSet[elem]
selection.Map[elem] =
.(sList,i)
selection.ShiftSet = {}
i = fromIndex,toIndex
elem = tree[i]
selection.Map[elem]
selection.ShiftSet[elem] =
selection.Map[elem] =
sList[#sList+] = 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,position)
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(position)
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,"UIStroke",{Thickness=1.4,Parent={3},Color=Color3.fromRGB(42,42,42)}},
{7,"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,}},
{8,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5034718129",ImageColor3=Color3.new(0.39215686917305,0.39215686917305,0.39215686917305),Parent={7},Size=UDim2.new(0,16,0,16),}},
{9,"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,}},
{10,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642310344",Parent={9},Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,12,0,12),}},
{11,"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,}},
{12,"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 and input.UserInputType ~= Enum.UserInputType.Touch) 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 and input.UserInputType ~= Enum.UserInputType.Touch) 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 or input.UserInputType == Enum.UserInputType.Touch) 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.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 or input.UserInputType == Enum.UserInputType.Touch) 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 and input.UserInputType ~= Enum.UserInputType.Touch then return end
local releaseEvent, inputEvent
releaseEvent = service.UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
releaseEvent:Disconnect()
if inputEvent then
inputEvent:Disconnect()
end
end
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 inputX = (input.UserInputType == Enum.UserInputType.Touch) and input.Position.X or input.Position.X
local timeLineSize = timeLine.AbsoluteSize
local relaX = inputX - 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)
inputEvent = service.UserInputService.InputChanged:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch 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 TweenService = service.TweenService
local SearchFrame = Properties.GuiElems.ToolBar.SearchFrame
local searchBox = SearchFrame.SearchBox
local TweenInfo = TweenInfo.new(0.2, Enum.EasingStyle.Quint)
local Tweens = {
Start = TweenService:Create(SearchFrame.UIStroke, TweenInfo, { Color = Color3.fromRGB(0, 120, 215) }),
End = TweenService:Create(SearchFrame.UIStroke, TweenInfo, { Color = Color3.fromRGB(42, 42, 42) })
}
Lib.ViewportTextBox.convert(searchBox)
searchBox.FocusLost:Connect(function() Tweens.End:Play() end)
searchBox.Focused:Connect(function() Tweens.Start:Play() end)
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,"UIStroke",{Thickness=1.4,Parent={3},Color=Color3.fromRGB(42,42,42)}},
{7,"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,}},
{8,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5034718129",ImageColor3=Color3.new(0.39215686917305,0.39215686917305,0.39215686917305),Parent={7},Size=UDim2.new(0,16,0,16),}},
{9,"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,}},
{10,"ImageLabel",{BackgroundColor3=Color3.new(1,1,1),BackgroundTransparency=1,Image="rbxassetid://5642310344",Parent={9},Position=UDim2.new(0,3,0,3),Size=UDim2.new(0,12,0,12),}},
{11,"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,}},
{12,"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(decompile or function() end, scr)
if not success or not source then source, PreviousScr = "-- DEX - Source failed to decompile", nil else PreviousScr = scr end
codeFrame:SetText(source:gsub("\0", "\\0"))
window:Show()
end
ScriptViewer.Init = function()
window = Lib.Window.new()
window:SetTitle("Script Viewer")
window:Resize(500, 400)
ScriptViewer.Window = window
codeFrame = Lib.CodeFrame.new()
codeFrame.Frame.Position = UDim2.new(0,0,0,20)
codeFrame.Frame.Size = UDim2.new(1,0,1,-20)
codeFrame.Frame.Parent = window.GuiElems.Content
-- TODO: REMOVE AND MAKE BETTER
local copy = Instance.new("TextButton", window.GuiElems.Content)
copy.BackgroundTransparency = 1
copy.Size = UDim2.new(0.5,0,0,20)
copy.Text = "Copy to Clipboard"
copy.TextColor3 = Color3.new(1,1,1)
copy.MouseButton1Click:Connect(function()
local source = codeFrame:GetText()
env.setclipboard(source)
end)
local save = Instance.new("TextButton",window.GuiElems.Content)
save.BackgroundTransparency = 1
save.Position = UDim2.new(0.35,0,0,0)
save.Size = UDim2.new(0.3,0,0,20)
save.Text = "Save to File"
save.TextColor3 = Color3.new(1,1,1)
save.MouseButton1Click:Connect(function()
local source = codeFrame:GetText()
local filename = "Place_"..game.PlaceId.."_Script_"..os.time()..".txt"
env.writefile(filename, source)
if env.movefileas then
env.movefileas(filename, ".txt")
end
end)
local dumpbtn = Instance.new("TextButton",window.GuiElems.Content)
dumpbtn.BackgroundTransparency = 1
dumpbtn.Position = UDim2.new(0.7,0,0,0)
dumpbtn.Size = UDim2.new(0.3,0,0,20)
dumpbtn.Text = "Dump Functions"
dumpbtn.TextColor3 = Color3.new(1,1,1)
dumpbtn.MouseButton1Click:Connect(function()
if PreviousScr ~= nil then
pcall(function()
-- thanks King.Kevin#6025 you'll obviously be credited (no discord tag since that can easily be impersonated)
local getgc = getgc or get_gc_objects
local getupvalues = (debug and debug.getupvalues) or getupvalues or getupvals
local getconstants = (debug and debug.getconstants) or getconstants or getconsts
local getinfo = (debug and (debug.getinfo or debug.info)) or getinfo
local original = ("\n-- // Function Dumper made by King.Kevin\n-- // Script Path: %s\n\n--[["):format(PreviousScr:GetFullName())
local dump = original
local functions, function_count, data_base = {}, 0, {}
function functions:add_to_dump(str, indentation, new_line)
local new_line = new_line or true
dump = dump .. ("%s%s%s"):format(string.rep(" ", indentation), tostring(str), new_line and "\n" or "")
end
function functions:get_function_name(func)
local n = getinfo(func).name
return n ~= "" and n or "Unknown Name"
end
function functions:dump_table(input, indent, index)
local indent = indent < 0 and 0 or indent
functions:add_to_dump(("%s [%s] %s"):format(tostring(index), tostring(typeof(input)), tostring(input)), indent - 1)
local count = 0
for index, value in pairs(input) do
count = count + 1
if type(value) == "function" then
functions:add_to_dump(("%d [function] = %s"):format(count, functions:get_function_name(value)), indent)
elseif type(value) == "table" then
if not data_base[value] then
data_base[value] = true
functions:add_to_dump(("%d [table]:"):format(count), indent)
functions:dump_table(value, indent + 1, index)
else
functions:add_to_dump(("%d [table] (Recursive table detected)"):format(count), indent)
end
else
functions:add_to_dump(("%d [%s] = %s"):format(count, tostring(typeof(value)), tostring(value)), indent)
end
end
end
function functions:dump_function(input, indent)
functions:add_to_dump(("\nFunction Dump: %s"):format(functions:get_function_name(input)), indent)
functions:add_to_dump(("\nFunction Upvalues: %s"):format(functions:get_function_name(input)), indent)
for index, upvalue in pairs(getupvalues(input)) do
if type(upvalue) == "function" then
functions:add_to_dump(("%d [function] = %s"):format(index, functions:get_function_name(upvalue)), indent + 1)
elseif type(upvalue) == "table" then
if not data_base[upvalue] then
data_base[upvalue] = true
functions:add_to_dump(("%d [table]:"):format(index), indent + 1)
functions:dump_table(upvalue, indent + 2, index)
else
functions:add_to_dump(("%d [table] (Recursive table detected)"):format(index), indent + 1)
end
else
functions:add_to_dump(("%d [%s] = %s"):format(index, tostring(typeof(upvalue)), tostring(upvalue)), indent + 1)
end
end
functions:add_to_dump(("\nFunction Constants: %s"):format(functions:get_function_name(input)), indent)
for index, constant in pairs(getconstants(input)) do
if type(constant) == "function" then
functions:add_to_dump(("%d [function] = %s"):format(index, functions:get_function_name(constant)), indent + 1)
elseif type(constant) == "table" then
if not data_base[constant] then
data_base[constant] = true
functions:add_to_dump(("%d [table]:"):format(index), indent + 1)
functions:dump_table(constant, indent + 2, index)
else
functions:add_to_dump(("%d [table] (Recursive table detected)"):format(index), indent + 1)
end
else
functions:add_to_dump(("%d [%s] = %s"):format(index, tostring(typeof(constant)), tostring(constant)), indent + 1)
end
end
end
for _, _function in pairs(getgc()) do
if typeof(_function) == "function" and getfenv(_function).script and getfenv(_function).script == PreviousScr then
functions:dump_function(_function, 0)
functions:add_to_dump("\n" .. ("="):rep(100), 0, false)
end
end
local source = codeFrame:GetText()
if dump ~= original then source = source .. dump .. "]]" end
codeFrame:SetText(source)
end)
end
end)
end
return ScriptViewer
end
return {InitDeps = initDeps, InitAfterMain = initAfterMain, Main = main}
end,
Lib = function()
--[[
Lib Module
Container for functions and classes
]]
-- Common Locals
local Main,Lib,Apps,Settings -- Main Containers
local Explorer, Properties, ScriptViewer, Notebook -- Major Apps
local API,RMD,env,service,plr,create,createSimple -- Main Locals
local function initDeps(data)
Main = data.Main
Lib = data.Lib
Apps = data.Apps
Settings = data.Settings
API = data.API
RMD = data.RMD
env = data.env
service = data.service
plr = data.plr
create = data.create
createSimple = data.createSimple
end
local function initAfterMain()
Explorer = Apps.Explorer
Properties = Apps.Properties
ScriptViewer = Apps.ScriptViewer
Notebook = Apps.Notebook
end
local function main()
local Lib = {}
local renderStepped = service.RunService.RenderStepped
local signalWait = renderStepped.wait
local PH = newproxy() -- Placeholder, must be replaced in constructor
local SIGNAL = newproxy()
-- Usually for classes that work with a Roblox Object
local function initObj(props,mt)
local type = type
local function copy(t)
local res = {}
for i,v in pairs(t) do
if v == SIGNAL then
res[i] = Lib.Signal.new()
elseif type(v) == "table" then
res[i] = copy(v)
else
res[i] = v
end
end
return res
end
local newObj = copy(props)
return setmetatable(newObj,mt)
end
local function getGuiMT(props,funcs)
return {__index = function(self,ind) if not props[ind] then return funcs[ind] or self.Gui[ind] end end,
__newindex = function(self,ind,val) if not props[ind] then self.Gui[ind] = val else rawset(self,ind,val) end end}
end
-- Functions
Lib.FormatLuaString = (function()
local string = string
local gsub = string.gsub
local format = string.format
local char = string.char
local cleanTable = {['"'] = '\\"', ['\\'] = '\\\\'}
for i = 0,31 do
cleanTable[char(i)] = "\\"..format("%03d",i)
end
for i = 127,255 do
cleanTable[char(i)] = "\\"..format("%03d",i)
end
return function(str)
return gsub(str,"[\"\\\0-\31\127-\255]",cleanTable)
end
end)()
Lib.CheckMouseInGui = function(gui)
if gui == nil then return false end
local mouse = Main.Mouse
local guiPosition = gui.AbsolutePosition
local guiSize = gui.AbsoluteSize
return mouse.X >= guiPosition.X and mouse.X < guiPosition.X + guiSize.X and mouse.Y >= guiPosition.Y and mouse.Y < guiPosition.Y + guiSize.Y
end
Lib.IsShiftDown = function()
return service.UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or service.UserInputService:IsKeyDown(Enum.KeyCode.RightShift)
end
Lib.IsCtrlDown = function()
return service.UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or service.UserInputService:IsKeyDown(Enum.KeyCode.RightControl)
end
Lib.CreateArrow = function(size,num,dir)
local max = num
local arrowFrame = createSimple("Frame",{
BackgroundTransparency = 1,
Name = "Arrow",
Size = UDim2.new(0,size,0,size)
})
if dir == "up" then
for i = 1,num do
local newLine = createSimple("Frame",{
BackgroundColor3 = Color3.new(220/255,220/255,220/255),
BorderSizePixel = 0,
Position = UDim2.new(0,math.floor(size/2)-(i-1),0,math.floor(size/2)+i-math.floor(max/2)-1),
Size = UDim2.new(0,i+(i-1),0,1),
Parent = arrowFrame
})
end
return arrowFrame
elseif dir == "down" then
for i = 1,num do
local newLine = createSimple("Frame",{
BackgroundColor3 = Color3.new(220/255,220/255,220/255),
BorderSizePixel = 0,
Position = UDim2.new(0,math.floor(size/2)-(i-1),0,math.floor(size/2)-i+math.floor(max/2)+1),
Size = UDim2.new(0,i+(i-1),0,1),
Parent = arrowFrame
})
end
return arrowFrame
elseif dir == "left" then
for i = 1,num do
local newLine = createSimple("Frame",{
BackgroundColor3 = Color3.new(220/255,220/255,220/255),
BorderSizePixel = 0,
Position = UDim2.new(0,math.floor(size/2)+i-math.floor(max/2)-1,0,math.floor(size/2)-(i-1)),
Size = UDim2.new(0,1,0,i+(i-1)),
Parent = arrowFrame
})
end
return arrowFrame
elseif dir == "right" then
for i = 1,num do
local newLine = createSimple("Frame",{
BackgroundColor3 = Color3.new(220/255,220/255,220/255),
BorderSizePixel = 0,
Position = UDim2.new(0,math.floor(size/2)-i+math.floor(max/2)+1,0,math.floor(size/2)-(i-1)),
Size = UDim2.new(0,1,0,i+(i-1)),
Parent = arrowFrame
})
end
return arrowFrame
end
error("r u ok")
end
Lib.ParseXML = (function()
local func = function()
-- Only exists to parse RMD
-- from https://github.com/jonathanpoelen/xmlparser
local string, print, pairs = string, print, pairs
-- http://lua-users.org/wiki/StringTrim
local trim = function(s)
local from = s:match"^%s*()"
return from > #s and "" or s:match(".*%S", from)
end
local gtchar = string.byte('>', 1)
local slashchar = string.byte('/', 1)
local D = string.byte('D', 1)
local E = string.byte('E', 1)
function parse(s, evalEntities)
-- remove comments
s = s:gsub('<!%-%-(.-)%-%->', '')
local entities, tentities = {}
if evalEntities then
local pos = s:find('<[_%w]')
if pos then
s:sub(1, pos):gsub('<!ENTITY%s+([_%w]+)%s+(.)(.-)%2', function(name, q, entity)
entities[#entities+1] = {name=name, value=entity}
end)
tentities = createEntityTable(entities)
s = replaceEntities(s:sub(pos), tentities)
end
end
local t, l = {}, {}
local addtext = function(txt)
txt = txt:match'^%s*(.*%S)' or ''
if #txt ~= 0 then
t[#t+1] = {text=txt}
end
end
s:gsub('<([?!/]?)([-:_%w]+)%s*(/?>?)([^<]*)', function(type, name, closed, txt)
-- open
if #type == 0 then
local a = {}
if #closed == 0 then
local len = 0
for all,aname,_,value,starttxt in string.gmatch(txt, "(.-([-_%w]+)%s*=%s*(.)(.-)%3%s*(/?>?))") do
len = len + #all
a[aname] = value
if #starttxt ~= 0 then
txt = txt:sub(len+1)
closed = starttxt
break
end
end
end
t[#t+1] = {tag=name, attrs=a, children={}}
if closed:byte(1) ~= slashchar then
l[#l+1] = t
t = t[#t].children
end
addtext(txt)
-- close
elseif '/' == type then
t = l[#l]
l[#l] = nil
addtext(txt)
-- ENTITY
elseif '!' == type then
if E == name:byte(1) then
txt:gsub('([_%w]+)%s+(.)(.-)%2', function(name, q, entity)
entities[#entities+1] = {name=name, value=entity}
end, 1)
end
-- elseif '?' == type then
-- print('? ' .. name .. ' // ' .. attrs .. '$$')
-- elseif '-' == type then
-- print('comment ' .. name .. ' // ' .. attrs .. '$$')
-- else
-- print('o ' .. #p .. ' // ' .. name .. ' // ' .. attrs .. '$$')
end
end)
return {children=t, entities=entities, tentities=tentities}
end
function parseText(txt)
return parse(txt)
end
function defaultEntityTable()
return { quot='"', apos='\'', lt='<', gt='>', amp='&', tab='\t', nbsp=' ', }
end
function replaceEntities(s, entities)
return s:gsub('&([^;]+);', entities)
end
function createEntityTable(docEntities, resultEntities)
entities = resultEntities or defaultEntityTable()
for _,e in pairs(docEntities) do
e.value = replaceEntities(e.value, entities)
entities[e.name] = e.value
end
return entities
end
return parseText
end
local newEnv = setmetatable({},{__index = getfenv()})
setfenv(func,newEnv)
return func()
end)()
Lib.FastWait = function(s)
if not s then return signalWait(renderStepped) end
local start = tick()
while tick() - start < s do signalWait(renderStepped) end
end
Lib.ButtonAnim = function(button,data)
local holding = false
local disabled = false
local mode = data and data.Mode or 1
local control = {}
if mode == 2 then
local lerpTo = data.LerpTo or Color3.new(0,0,0)
local delta = data.LerpDelta or 0.2
control.StartColor = data.StartColor or button.BackgroundColor3
control.PressColor = data.PressColor or control.StartColor:lerp(lerpTo,delta)
control.HoverColor = data.HoverColor or control.StartColor:lerp(control.PressColor,0.6)
control.OutlineColor = data.OutlineColor
end
button.InputBegan:Connect(function(input)
if disabled then return end
if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
if not holding then
if mode == 1 then
button.BackgroundTransparency = 0.4
elseif mode == 2 then
button.BackgroundColor3 = control.HoverColor
end
end
elseif input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch 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 or input.UserInputType == Enum.UserInputType.Touch then
if not holding then
if mode == 1 then
button.BackgroundTransparency = 1
elseif mode == 2 then
button.BackgroundColor3 = control.StartColor
end
end
elseif input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
holding = false
if mode == 1 then
button.BackgroundTransparency = Lib.CheckMouseInGui(button) and 0.4 or 1
elseif mode == 2 then
button.BackgroundColor3 = Lib.CheckMouseInGui(button) and control.HoverColor or control.StartColor
if control.OutlineColor then button.BorderColor3 = control.OutlineColor end
end
end
end)
control.Disable = function()
disabled = true
holding = false
if mode == 1 then
button.BackgroundTransparency = 1
elseif mode == 2 then
button.BackgroundColor3 = control.StartColor
end
end
control.Enable = function()
disabled = false
end
return control
end
Lib.FindAndRemove = function(t,item)
local pos = table.find(t,item)
if pos then table.remove(t,pos) end
end
Lib.AttachTo = function(obj,data)
local target,posOffX,posOffY,sizeOffX,sizeOffY,resize,con
local disabled = false
local function update()
if not obj or not target then return end
local targetPos = target.AbsolutePosition
local targetSize = target.AbsoluteSize
obj.Position = UDim2.new(0,targetPos.X + posOffX,0,targetPos.Y + posOffY)
if resize then obj.Size = UDim2.new(0,targetSize.X + sizeOffX,0,targetSize.Y + sizeOffY) end
end
local function setup(o,data)
obj = o
data = data or {}
target = data.Target
posOffX = data.PosOffX or 0
posOffY = data.PosOffY or 0
sizeOffX = data.SizeOffX or 0
sizeOffY = data.SizeOffY or 0
resize = data.Resize or false
if con then con:Disconnect() con = nil end
if target then
con = target.Changed:Connect(function(prop)
if not disabled and prop == "AbsolutePosition" or prop == "AbsoluteSize" then
update()
end
end)
end
update()
end
setup(obj,data)
return {
SetData = function(obj,data)
setup(obj,data)
end,
Enable = function()
disabled = false
update()
end,
Disable = function()
disabled = true
end,
Destroy = function()
con:Disconnect()
con = nil
end,
}
end
Lib.ProtectedGuis = {}
Lib.ShowGui = function(gui)
if env.gethui then
gui.Parent = env.gethui()
elseif env.protectgui then
env.protectgui(gui)
gui.Parent = Main.GuiHolder
else
gui.Parent = Main.GuiHolder
end
end
Lib.ColorToBytes = function(col)
local round = math.round
return string.format("%d, %d, %d",round(col.r*255),round(col.g*255),round(col.b*255))
end
Lib.ReadFile = function(filename)
if not env.readfile then return end
local s,contents = pcall(env.readfile,filename)
if s and contents then return contents end
end
Lib.DeferFunc = function(f,...)
signalWait(renderStepped)
return f(...)
end
Lib.LoadCustomAsset = function(filepath)
if not env.getcustomasset or not env.isfile or not env.isfile(filepath) then return end
return env.getcustomasset(filepath)
end
Lib.FetchCustomAsset = function(url,filepath)
if not env.writefile then return end
local s,data = pcall(game.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 = {}
local _MapId, _Icons = (483448923), {
["Accessory"] = 32;
["Accoutrement"] = 32;
["AdService"] = 73;
["Animation"] = 60;
["AnimationController"] = 60;
["AnimationTrack"] = 60;
["Animator"] = 60;
["ArcHandles"] = 56;
["AssetService"] = 72;
["Attachment"] = 34;
["Backpack"] = 20;
["BadgeService"] = 75;
["BallSocketConstraint"] = 89;
["BillboardGui"] = 64;
["BinaryStringValue"] = 4;
["BindableEvent"] = 67;
["BindableFunction"] = 66;
["BlockMesh"] = 8;
["BloomEffect"] = 90;
["BlurEffect"] = 90;
["BodyAngularVelocity"] = 14;
["BodyForce"] = 14;
["BodyGyro"] = 14;
["BodyPosition"] = 14;
["BodyThrust"] = 14;
["BodyVelocity"] = 14;
["BoolValue"] = 4;
["BoxHandleAdornment"] = 54;
["BrickColorValue"] = 4;
["Camera"] = 5;
["CFrameValue"] = 4;
["CharacterMesh"] = 60;
["Chat"] = 33;
["ClickDetector"] = 41;
["CollectionService"] = 30;
["Color3Value"] = 4;
["ColorCorrectionEffect"] = 90;
["ConeHandleAdornment"] = 54;
["Configuration"] = 58;
["ContentProvider"] = 72;
["ContextActionService"] = 41;
["CoreGui"] = 46;
["CoreScript"] = 18;
["CornerWedgePart"] = 1;
["CustomEvent"] = 4;
["CustomEventReceiver"] = 4;
["CylinderHandleAdornment"] = 54;
["CylinderMesh"] = 8;
["CylindricalConstraint"] = 89;
["Debris"] = 30;
["Decal"] = 7;
["Dialog"] = 62;
["DialogChoice"] = 63;
["DoubleConstrainedValue"] = 4;
["Explosion"] = 36;
["FileMesh"] = 8;
["Fire"] = 61;
["Flag"] = 38;
["FlagStand"] = 39;
["FloorWire"] = 4;
["Folder"] = 70;
["ForceField"] = 37;
["Frame"] = 48;
["GamePassService"] = 19;
["Glue"] = 34;
["GuiButton"] = 52;
["GuiMain"] = 47;
["GuiService"] = 47;
["Handles"] = 53;
["HapticService"] = 84;
["Hat"] = 45;
["HingeConstraint"] = 89;
["Hint"] = 33;
["HopperBin"] = 22;
["HttpService"] = 76;
["Humanoid"] = 9;
["ImageButton"] = 52;
["ImageLabel"] = 49;
["InsertService"] = 72;
["IntConstrainedValue"] = 4;
["IntValue"] = 4;
["JointInstance"] = 34;
["JointsService"] = 34;
["Keyframe"] = 60;
["KeyframeSequence"] = 60;
["KeyframeSequenceProvider"] = 60;
["Lighting"] = 13;
["LineHandleAdornment"] = 54;
["LocalScript"] = 18;
["LogService"] = 87;
["MarketplaceService"] = 46;
["Message"] = 33;
["Model"] = 2;
["ModuleScript"] = 71;
["Motor"] = 34;
["Motor6D"] = 34;
["MoveToConstraint"] = 89;
["NegateOperation"] = 78;
["NetworkClient"] = 16;
["NetworkReplicator"] = 29;
["NetworkServer"] = 15;
["NumberValue"] = 4;
["ObjectValue"] = 4;
["Pants"] = 44;
["ParallelRampPart"] = 1;
["Part"] = 1;
["ParticleEmitter"] = 69;
["PartPairLasso"] = 57;
["PathfindingService"] = 37;
["Platform"] = 35;
["Player"] = 12;
["PlayerGui"] = 46;
["Players"] = 21;
["PlayerScripts"] = 82;
["PointLight"] = 13;
["PointsService"] = 83;
["Pose"] = 60;
["PrismaticConstraint"] = 89;
["PrismPart"] = 1;
["PyramidPart"] = 1;
["RayValue"] = 4;
["ReflectionMetadata"] = 86;
["ReflectionMetadataCallbacks"] = 86;
["ReflectionMetadataClass"] = 86;
["ReflectionMetadataClasses"] = 86;
["ReflectionMetadataEnum"] = 86;
["ReflectionMetadataEnumItem"] = 86;
["ReflectionMetadataEnums"] = 86;
["ReflectionMetadataEvents"] = 86;
["ReflectionMetadataFunctions"] = 86;
["ReflectionMetadataMember"] = 86;
["ReflectionMetadataProperties"] = 86;
["ReflectionMetadataYieldFunctions"] = 86;
["RemoteEvent"] = 80;
["RemoteFunction"] = 79;
["ReplicatedFirst"] = 72;
["ReplicatedStorage"] = 72;
["RightAngleRampPart"] = 1;
["RocketPropulsion"] = 14;
["RodConstraint"] = 89;
["RopeConstraint"] = 89;
["Rotate"] = 34;
["RotateP"] = 34;
["RotateV"] = 34;
["RunService"] = 66;
["ScreenGui"] = 47;
["Script"] = 6;
["ScrollingFrame"] = 48;
["Seat"] = 35;
["Selection"] = 55;
["SelectionBox"] = 54;
["SelectionPartLasso"] = 57;
["SelectionPointLasso"] = 57;
["SelectionSphere"] = 54;
["ServerScriptService"] = 0;
["ServerStorage"] = 74;
["Shirt"] = 43;
["ShirtGraphic"] = 40;
["SkateboardPlatform"] = 35;
["Sky"] = 28;
["SlidingBallConstraint"] = 89;
["Smoke"] = 59;
["Snap"] = 34;
["Sound"] = 11;
["SoundService"] = 31;
["Sparkles"] = 42;
["SpawnLocation"] = 25;
["SpecialMesh"] = 8;
["SphereHandleAdornment"] = 54;
["SpotLight"] = 13;
["SpringConstraint"] = 89;
["StarterCharacterScripts"] = 82;
["StarterGear"] = 20;
["StarterGui"] = 46;
["StarterPack"] = 20;
["StarterPlayer"] = 88;
["StarterPlayerScripts"] = 82;
["Status"] = 2;
["StringValue"] = 4;
["SunRaysEffect"] = 90;
["SurfaceGui"] = 64;
["SurfaceLight"] = 13;
["SurfaceSelection"] = 55;
["Team"] = 24;
["Teams"] = 23;
["TeleportService"] = 81;
["Terrain"] = 65;
["TerrainRegion"] = 65;
["TestService"] = 68;
["TextBox"] = 51;
["TextButton"] = 51;
["TextLabel"] = 50;
["Texture"] = 10;
["TextureTrail"] = 4;
["Tool"] = 17;
["TouchTransmitter"] = 37;
["TrussPart"] = 1;
["UnionOperation"] = 77;
["UserInputService"] = 84;
["Vector3Value"] = 4;
["VehicleSeat"] = 35;
["VelocityMotor"] = 34;
["WedgePart"] = 1;
["Weld"] = 34;
["Workspace"] = 19;
}
funcs.ExplorerIcons = { ["MapId"] = _MapId, ["Icons"] = _Icons }
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])
else
local rmdEntry = RMD.Classes[obj.ClassName]
Explorer.ClassIcons:Display(obj, rmdEntry and rmdEntry.ExplorerImageIndex or 0)
end
end
funcs.IconDehash = function(self, _id)
return math.floor(_id / 14 % 14), math.floor(_id % 14)
end
funcs.GetExplorerIcon = function(self, obj, index)
index = (self.ExplorerIcons.Icons[index] or 0)
local row, col = self:IconDehash(index)
local MapSize = Vector2.new(256, 256)
local pad, border = 2, 1
local IconSize = 16
obj.Position = UDim2.new(-col - (pad * (col + 1) + border) / IconSize, 0, -row - (pad * (row + 1) + border) / IconSize, 0)
obj.Size = UDim2.new(MapSize.X / IconSize, 0, MapSize.Y / IconSize, 0)
end
funcs.DisplayExplorerIcons = function(self, Frame, index)
if Frame:FindFirstChild("IconMap") then
self:GetExplorerIcon(Frame.IconMap, index)
else
Frame.ClipsDescendants = true
local obj = Instance.new("ImageLabel", Frame)
obj.BackgroundTransparency = 1
obj.Image = ("http://www.roblox.com/asset/?id=" .. (self.ExplorerIcons.MapId))
obj.Name = "IconMap"
self:GetExplorerIcon(obj, index)
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, button2
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("ImageButton", {
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 function handleButtonPress(button, scrollDirection)
if self:CanScroll(scrollDirection) then
button.BackgroundTransparency = 0.5
self:ScrollToDirection(scrollDirection)
self.Scrolled:Fire()
local buttonTick = tick()
local releaseEvent
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
releaseEvent:Disconnect()
button.BackgroundTransparency = checkMouseInGui(button) and 0.8 or 1
buttonPress = false
end
end)
while buttonPress do
if tick() - buttonTick >= 0.25 and self:CanScroll(scrollDirection) then
self:ScrollToDirection(scrollDirection)
self.Scrolled:Fire()
end
task.wait()
end
end
end
button1.MouseButton1Down:Connect(function(input)
buttonPress = true
handleButtonPress(button1, "Up")
end)
button1.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
button1.BackgroundTransparency = 1
end
end)
button2.MouseButton1Down:Connect(function(input)
buttonPress = true
handleButtonPress(button2, "Down")
end)
button2.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
button2.BackgroundTransparency = 1
end
end)
scrollThumb.InputBegan:Connect(function(input)
if (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then
local dir = self.Horizontal and "X" or "Y"
local lastThumbPos = nil
thumbPress = true
scrollThumb.BackgroundTransparency = 0
local mouseOffset = mouse[dir] - scrollThumb.AbsolutePosition[dir]
local releaseEvent
local mouseEvent
releaseEvent = user.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
releaseEvent:Disconnect()
if mouseEvent then mouseEvent:Disconnect() end
scrollThumb.BackgroundTransparency = 0.2
thumbPress = false
end
end)
mouseEvent = user.InputChanged:Connect(function(input)
if (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) and thumbPress 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
end
end)
end
end)
scrollThumb.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then
scrollThumb.BackgroundTransparency = 0
end
end)
scrollThumbFrame.InputBegan:Connect(function(input)
if (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) and not checkMouseInGui(scrollThumb) then
local dir = self.Horizontal and "X" or "Y"
local scrollDir = (mouse[dir] >= scrollThumb.AbsolutePosition[dir] + scrollThumb.AbsoluteSize[dir]) and 1 or 0
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 or input.UserInputType == Enum.UserInputType.Touch then
releaseEvent:Disconnect()
thumbFramePress = false
end
end)
while thumbFramePress do
if tick() - thumbFrameTick >= 0.3 and checkMouseInGui(scrollThumbFrame) then
doTick()
end
task.wait()
end
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.CanScroll = function(self, direction)
if direction == "Up" then
return self:CanScrollUp()
elseif direction == "Down" then
return self:CanScrollDown()
end
return false
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.ScrollToDirection = function(self, Direaction)
if Direaction == "Up" then
self:ScrollUp()
elseif Direaction == "Down" then
self:ScrollDown()
end
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 or input.UserInputType == Enum.UserInputType.Touch then
resizer.BackgroundTransparency = 0.5
elseif input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
local releaseEvent, mouseEvent
local offX = input.Position.X - resizer.AbsolutePosition.X
local offY = input.Position.Y - resizer.AbsolutePosition.Y
self.Resizing = resizer
resizer.BackgroundTransparency = 1
releaseEvent = service.UserInputService.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
releaseEvent:Disconnect()
if mouseEvent then mouseEvent:Disconnect() end
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 or input.UserInputType == Enum.UserInputType.Touch) 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)
end
end)
end
end
end)
resizer.InputEnded:Connect(function(input)
if (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) 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,"TextButton",{Text="",AutoButtonColor=false,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
local ButtonDown = false
guiTopBar.MouseButton1Down:Connect(function() ButtonDown = true end)
guiTopBar.MouseButton1Up:Connect(function() ButtonDown = false end)
guiTopBar.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then
if self.Draggable then
local releaseEvent, mouseEvent
local maxX = sidesGui.ALoads code from the internet. It downloads more code when it runs (loadstring + HttpGet), which can change after it was posted. From: github.com, raw.githubusercontent.com, lua-users.org, www.roblox.com.
Freindly mobile dex (Script is not made by me! All credit goes to original owner) Credit for fixed the script: https://scriptblox.com/script/Universal-Script-Keyless-Mobile-Dex-Reupload-22707