Advertisement
coastsss

Old Developer Console Pink Style

May 12th, 2020
120
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 71.03 KB | None | 0 0
  1. local Instance_new = Instance.new
  2. local UDim2_new = UDim2.new
  3. local Color3_new = Color3.new
  4. local math_max = math.max
  5. local tick = tick
  6. local pairs = pairs
  7. local os_time = os.time
  8. local DEBUG = false
  9.  
  10. if syn then require = getrenv().require end
  11.  
  12. local AnalyticsCategory_Game = "Game"
  13. local AnalyticsAction_InitialOpenTab = "DeveloperConsole_InitialOpenTab"
  14. local AnalyticsAction_ClickToOpenOpenTab = "DeveloperConsole_ClickToOpenOpenTab"
  15.  
  16. local CoreGui = game:GetService("CoreGui")
  17. local RobloxGui = CoreGui:FindFirstChild("RobloxGui")
  18. local Modules = RobloxGui:FindFirstChild("Modules")
  19.  
  20. local ContextActionService = game:GetService("ContextActionService")
  21. local TextService = game:GetService("TextService")
  22. local GuiService = game:GetService("GuiService")
  23. local VRService = game:GetService("VRService")
  24. local isTenFootInterface = GuiService:IsTenFootInterface()
  25.  
  26. local ClientMemoryAnalyzerClass = require(CoreGui:WaitForChild("RobloxGui"):WaitForChild("Modules"):WaitForChild("Stats"):WaitForChild("ClientMemoryAnalyzer"))
  27. local ServerMemoryAnalyzerClass = require(CoreGui:WaitForChild("RobloxGui"):WaitForChild("Modules"):WaitForChild("Stats"):WaitForChild("ServerMemoryAnalyzer"))
  28. local StatsUtils = require(CoreGui:WaitForChild("RobloxGui"):WaitForChild("Modules"):WaitForChild("Stats"):WaitForChild("StatsUtils"))
  29.  
  30. local Style; do
  31.     local c3 = Color3.fromRGB;
  32.     local frameColor = Color3.new(0.1, 0.1, 0.1)
  33.     local textColor = Color3.new(1, 1, 1)
  34.     local optionsFrameColor = Color3.new(1, 1, 1)
  35.    
  36.     pcall(function() -- Fun window colors for cool people
  37.         local Players = game:GetService("Players")
  38.         if not Players or not Players.LocalPlayer then
  39.             return
  40.         end
  41.         local FunColors = {
  42.             [22425814]   = {c3(255, 63,  127)}; -- ReeseMcBlox
  43.             [1440557846] = {c3(255, 63,  127)}; -- NobleDragon
  44.         }
  45.         local funColor = FunColors[Players.LocalPlayer.UserId]
  46.         if funColor then
  47.             frameColor = funColor[1] or frameColor
  48.             textColor = funColor[2] or textColor
  49.         end
  50.     end)
  51.    
  52.     Style = {
  53.         ZINDEX = 6;
  54.         Font = Enum.Font.SourceSans;
  55.         FontBold = Enum.Font.SourceSansBold;
  56.        
  57.         HandleHeight = 24; -- How tall the top window handle is, as well as the width of the scroll bar
  58.         TabHeight = 28;
  59.         GearSize = 24;
  60.         BorderSize = 2;
  61.         CommandLineHeight = 22;
  62.        
  63.         OptionAreaHeight = 56;
  64.        
  65.         FrameColor = frameColor; -- Applies to pretty much everything, including buttons
  66.         FrameTransparency = 0.5;
  67.         OptionsFrameColor = optionsFrameColor;
  68.        
  69.         TextColor = textColor;
  70.        
  71.         MessageColors = {
  72.             [0] = Color3.new(1, 1, 1); -- Enum.MessageType.MessageOutput
  73.             [1] = Color3.new(0.4, 0.5, 1); -- Enum.MessageType.MessageInfo
  74.             [2] = Color3.new(1, 0.6, 0.4); -- Enum.MessageType.MessageWarning
  75.             [3] = Color3.new(1, 0, 0); -- Enum.MessageType.MessageError
  76.         };
  77.        
  78.         ScrollbarFrameColor = frameColor;
  79.         ScrollbarBarColor = frameColor;
  80.        
  81.         ScriptButtonHeight = 32;
  82.         ScriptButtonColor = Color3.new(0, 1/3, 2/3);
  83.         ScriptButtonTransparency = 0.5;
  84.        
  85.         CheckboxSize = 24;
  86.        
  87.         ChartTitleHeight = 20;
  88.         ChartGraphHeight = 64;
  89.         ChartDataHeight = 24;
  90.         ChartHeight = 0; -- This gets added up at end and set at end of block
  91.         ChartWidth = 620;
  92.        
  93.         -- (-1) means right to left
  94.         -- (1) means left to right
  95.         ChartGraphDirection = 1; -- the direction the bars move
  96.        
  97.        
  98.         GetButtonDownColor = function(normalColor)
  99.             local r, g, b = normalColor.r, normalColor.g, normalColor.b
  100.             return Color3.new(1 - 0.75 * (1 - r), 1 - 0.75 * (1 - g), 1 - 0.75 * (1 - b))
  101.         end;
  102.         GetButtonHoverColor = function(normalColor)
  103.             local r, g, b = normalColor.r, normalColor.g, normalColor.b
  104.             return Color3.new(1 - 0.875 * (1 - r), 1 - 0.875 * (1 - g), 1 - 0.875 * (1 - b))
  105.         end;
  106.  
  107.     }
  108.    
  109.     Style.ChartHeight = Style.ChartTitleHeight + Style.ChartGraphHeight + Style.ChartDataHeight + Style.BorderSize
  110. end
  111.  
  112. local Primitives = {}; do
  113.     local function new(className, parent, name)
  114.         local n = Instance.new(className, parent)
  115.         n.ZIndex = 0
  116.         if name then
  117.             n.Name = name
  118.         end
  119.         return n
  120.     end
  121.     local unitSize = UDim2.new(1, 0, 1, 0)
  122.    
  123.     local function setupFrame(n)
  124.         n.BackgroundColor3 = Style.FrameColor
  125.         n.BackgroundTransparency = Style.FrameTransparency
  126.         n.BorderSizePixel = 0
  127.     end
  128.     local function setupText(n, text)
  129.         n.Font = Style.Font
  130.         n.TextColor3 = Style.TextColor
  131.         n.Text = text or n.Text
  132.     end
  133.    
  134.     function Primitives.Frame(parent, name)
  135.         local n = new('Frame', parent, name)
  136.         setupFrame(n)
  137.         return n
  138.     end
  139.     function Primitives.TextLabel(parent, name, text)
  140.         local n = new('TextLabel', parent, name)
  141.         setupFrame(n)
  142.         setupText(n, text)
  143.         return n
  144.     end
  145.     function Primitives.TextBox(parent, name, text)
  146.         local n = new('TextBox', parent, name)
  147.         setupFrame(n)
  148.         setupText(n, text)
  149.         return n
  150.     end
  151.     function Primitives.TextButton(parent, name, text)
  152.         local n = new('TextButton', parent, name)
  153.         setupFrame(n)
  154.         setupText(n, text)
  155.         return n
  156.     end
  157.     function Primitives.Button(parent, name)
  158.         local n = new('TextButton', parent, name)
  159.         setupFrame(n)
  160.         n.Text = ""
  161.         return n
  162.     end
  163.     function Primitives.ImageButton(parent, name, image)
  164.         local n = new('ImageButton', parent, name)
  165.         setupFrame(n)
  166.         n.Image = image or ""
  167.         n.Size = unitSize
  168.         return n
  169.     end
  170.    
  171.     -- An invisible frame of size (1, 0, 1, 0)
  172.     function Primitives.FolderFrame(parent, name) -- Should this be called InvisibleFrame? lol
  173.         local n = new('Frame', parent, name)
  174.         n.BackgroundTransparency = 1
  175.         n.Size = unitSize
  176.         return n
  177.     end
  178.     function Primitives.InvisibleTextLabel(parent, name, text)
  179.         local n = new('TextLabel', parent, name)
  180.         setupText(n, text)
  181.         n.BackgroundTransparency = 1
  182.         return n
  183.     end
  184.     function Primitives.InvisibleButton(parent, name, text)
  185.         local n = new('TextButton', parent, name)
  186.         n.BackgroundTransparency = 1
  187.         n.Text = ""
  188.         return n
  189.     end
  190.     function Primitives.InvisibleImageLabel(parent, name, image)
  191.         local n = new('ImageLabel', parent, name)
  192.         n.BackgroundTransparency = 1
  193.         n.Image = image or ""
  194.         n.Size = unitSize
  195.         return n
  196.     end
  197. end
  198.  
  199. --[[ Flags ]]--
  200. local function checkFFlag(flagName)
  201.     local flagSuccess, flagValue = pcall(function()
  202.             return settings():GetFFlag(flagName)
  203.         end)
  204.     return (flagSuccess and flagValue)
  205. end
  206.  
  207.  
  208. -- Eye candy uses RenderStepped
  209. local EYECANDY_ENABLED = true
  210.  
  211. local AUTO_TAB_WIDTH = -1
  212. local TAB_TEXT_SIZE = 14
  213. local TAB_TEXT_PADDING = 8
  214.  
  215.  
  216. local function CreateSignal()
  217.     local this = {}
  218.  
  219.     local mBindableEvent = Instance.new('BindableEvent')
  220.     local mAllCns = {} --all connection objects returned by mBindableEvent::connect
  221.  
  222.     --main functions
  223.     function this:connect(func)
  224.         if self ~= this then error("connect must be called with `:`, not `.`", 2) end
  225.         if type(func) ~= 'function' then
  226.             error("Argument #1 of connect must be a function, got a "..type(func), 2)
  227.         end
  228.         local cn = mBindableEvent.Event:Connect(func)
  229.         mAllCns[cn] = true
  230.         local pubCn = {}
  231.         function pubCn:disconnect()
  232.             cn:Disconnect()
  233.             mAllCns[cn] = nil
  234.         end
  235.         pubCn.Disconnect = pubCn.disconnect
  236.        
  237.         return pubCn
  238.     end
  239.    
  240.     function this:disconnect()
  241.         if self ~= this then error("disconnect must be called with `:`, not `.`", 2) end
  242.         for cn, _ in pairs(mAllCns) do
  243.             cn:Disconnect()
  244.             mAllCns[cn] = nil
  245.         end
  246.     end
  247.    
  248.     function this:wait()
  249.         if self ~= this then error("wait must be called with `:`, not `.`", 2) end
  250.         return mBindableEvent.Event:Wait()
  251.     end
  252.    
  253.     function this:fire(...)
  254.         if self ~= this then error("fire must be called with `:`, not `.`", 2) end
  255.         mBindableEvent:Fire(...)
  256.     end
  257.    
  258.     this.Connect = this.connect
  259.     this.Disconnect = this.disconnect
  260.     this.Wait = this.wait
  261.     this.Fire = this.fire
  262.  
  263.     return this
  264. end
  265.  
  266. -- This is a Signal that only calls once, then forgets about the function. It also accepts event listeners as functions
  267. local CreateDisconnectSignal; do
  268.     local Methods = {}
  269.     local Metatable = {__index = Methods}
  270.     function Methods.fire(this, ...)
  271.         return this.Signal:fire(...)
  272.     end
  273.     function Methods.wait(this, ...)
  274.         return this.Signal:wait(...)
  275.     end
  276.     function Methods.connect(this, func)
  277.         local t = type(func)
  278.         if t == 'table' or t == 'userdata' then
  279.             -- Got event listener
  280.             local listener = func
  281.             function func()
  282.                 listener:disconnect()
  283.             end
  284.         elseif t ~= 'function' then
  285.             error('Invalid disconnect method type: ' .. t, 2)
  286.         end
  287.  
  288.         local listener;
  289.         listener = this.Signal:connect(function(...)
  290.             if listener then
  291.                 listener:disconnect()
  292.                 listener = nil
  293.                 func(...)
  294.             end
  295.         end)
  296.         return listener
  297.     end
  298.     function CreateDisconnectSignal()
  299.         return setmetatable({
  300.             Signal = CreateSignal();
  301.         }, Metatable)
  302.     end
  303. end
  304.  
  305. -- Services
  306. local UserInputService = game:GetService('UserInputService')
  307. local RunService = game:GetService('RunService')
  308. local TouchEnabled = UserInputService.TouchEnabled
  309.  
  310. local DeveloperConsole = {}
  311.  
  312. local Methods = {}
  313. local Metatable = {__index = Methods}
  314.  
  315. -------------------------
  316. -- Listener management --
  317. -------------------------
  318. function Methods.ConnectSetVisible(devConsole, func)
  319.     -- This is used mainly for pausing rendering and stuff when the console isn't visible
  320.     func(devConsole.Visible)
  321.     return devConsole.VisibleChanged:connect(function(visible)
  322.         func(visible)
  323.     end)
  324. end
  325. function Methods.ConnectObjectSetVisible(devConsole, object, func)
  326.     -- Same as above, but used for calling methods like object:SetVisible()
  327.     func(object, devConsole.Visible)
  328.     return devConsole.VisibleChanged:connect(function(visible)
  329.         func(object, visible)
  330.     end)
  331. end
  332.  
  333. -----------------------------
  334. -- Frame/Window Dimensions --
  335. -----------------------------
  336.  
  337. local function connectPropertyChanged(object, property, callback)
  338.     return object.Changed:connect(function(propertyChanged)
  339.         if propertyChanged == property then
  340.             callback(object[property])
  341.         end
  342.     end)
  343. end
  344.  
  345. function Methods.ResetFrameDimensions(devConsole)
  346.     devConsole.Frame.Size = UDim2_new(0.5, 20, 0.5, 20);
  347.    
  348.     local abSize = devConsole.Frame.AbsoluteSize
  349.     devConsole:SetFrameSize(abSize.x, abSize.y)
  350.     local newSize = devConsole.Frame.Size
  351.     devConsole.Frame.Position = UDim2_new(0.5, -newSize.X.Offset/2, 0.5, -newSize.Y.Offset/2)
  352. end
  353. function Methods.BoundFrameSize(devConsole, x, y)
  354.     -- Minimum frame size
  355.     return math_max(x, 400), math_max(y, 200)
  356. end
  357. function Methods.SetFrameSize(devConsole, x, y)
  358.     x, y = devConsole:BoundFrameSize(x, y)
  359.     devConsole.Frame.Size = UDim2_new(0, x, 0, y)
  360. end
  361. function Methods.BoundFramePosition(devConsole, x, y)
  362.     -- Make sure the frame doesn't go somewhere where the bar can't be clicked
  363.     return x, math_max(y, 0)
  364. end
  365. function Methods.SetFramePosition(devConsole, x, y)
  366.     x, y = devConsole:BoundFramePosition(x, y)
  367.     devConsole.Frame.Position = UDim2_new(0, x, 0, y)
  368. end
  369.  
  370. -- Open/Close the console
  371. function Methods.SetVisible(devConsole, visible, animate)
  372.     if devConsole.Visible == visible then
  373.         return
  374.     end
  375.     devConsole.Visible = visible
  376.     devConsole.VisibleChanged:fire(visible)
  377.     if devConsole.Frame then
  378.         devConsole.Frame.Visible = visible
  379.     end
  380.     if visible then -- Open the console
  381.         devConsole:ResetFrameDimensions()
  382.  
  383.         local tab = devConsole:GetCurrentOpenTab()
  384.         if (tab ~= nil) then
  385.             tab:RecordInitialOpen()
  386.         end
  387.     end
  388.     if VRService.VREnabled then
  389.         if visible then
  390.             UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceShow
  391.         else
  392.             UserInputService.OverrideMouseIconBehavior = Enum.OverrideMouseIconBehavior.ForceHide
  393.         end
  394.     end
  395. end
  396.  
  397. -----------------
  398. -- Constructor --
  399. -----------------
  400. function DeveloperConsole.new(screenGui, permissions, messagesAndStats)
  401.  
  402.     local visibleChanged = CreateSignal()
  403.    
  404.     local devConsole = {
  405.         ScreenGui = screenGui;
  406.         Permissions = permissions;
  407.         MessagesAndStats = messagesAndStats;
  408.         Initialized = false;
  409.         Visible = false;
  410.         Tabs = {};
  411.         CurrentOpenedTab = nil; -- save last tab opened to set SelectedCoreObject for TenFootInterfaces
  412.         VisibleChanged = visibleChanged; -- Created by :Initialize(); It's used to stop and disconnect things when the window is hidden
  413.     }
  414.    
  415.     setmetatable(devConsole, Metatable)
  416.  
  417.     devConsole:EnableGUIMouse()
  418.    
  419.     -- It's a button so it catches mouse events
  420.     local frame = Primitives.Button(screenGui, 'DeveloperConsole')
  421.     frame.AutoButtonColor = false
  422.     --frame.ClipsDescendants = true
  423.     frame.Visible = devConsole.Visible
  424.     frame.Selectable = not isTenFootInterface
  425.  
  426.     local function onVREnabled()
  427.         frame.Modal = VRService.VREnabled
  428.     end
  429.     onVREnabled()
  430.     VRService:GetPropertyChangedSignal("VREnabled"):connect(onVREnabled)
  431.  
  432.     devConsole.Frame = frame
  433.     devConsole:ResetFrameDimensions()
  434.    
  435.     -- The bar at the top that you can drag around
  436.     local handle = Primitives.Button(frame, 'Handle')
  437.     handle.Size = UDim2_new(1, -(Style.HandleHeight + Style.BorderSize), 0, Style.HandleHeight)
  438.     handle.Selectable = not isTenFootInterface
  439.     handle.Modal = true -- Unlocks mouse
  440.     handle.AutoButtonColor = false
  441.    
  442.    
  443.     do -- Title
  444.         local title = Primitives.InvisibleTextLabel(handle, 'Title', "Developer Console")
  445.         title.Size = UDim2_new(1, -5, 1, 0)
  446.         title.Position = UDim2_new(0, 5, 0, 0) 
  447.         title.FontSize = Enum.FontSize.Size18
  448.         title.TextXAlignment = Enum.TextXAlignment.Left
  449.     end
  450.    
  451.     local function setCornerButtonImageSize(buttonImage, buttonImageSize)
  452.         buttonImage.Size = UDim2_new(buttonImageSize, 0, buttonImageSize, 0)
  453.         buttonImage.Position = UDim2_new((1 - buttonImageSize) / 2, 0, (1 - buttonImageSize) / 2, 0)       
  454.     end
  455.  
  456.     -- This is used for creating the square exit button and the square window resize button
  457.     local function createCornerButton(name, x, y, image, buttonImageSize)
  458.         -- Corners (x, y):
  459.         -- (0, 0) (1, 0)
  460.         -- (0, 1) (1, 1)
  461.        
  462.         local button = Primitives.Button(frame, name)
  463.         button.Size = UDim2_new(0, Style.HandleHeight, 0, Style.HandleHeight)
  464.         button.Position = UDim2_new(x, -x * Style.HandleHeight, y, -y * Style.HandleHeight)
  465.        
  466.         local buttonImage = Primitives.InvisibleImageLabel(button, 'Image', image)
  467.         setCornerButtonImageSize(buttonImage, buttonImageSize)
  468.        
  469.         return button, buttonImage
  470.     end
  471.    
  472.     do -- Create top right exit button
  473.         local exitButton, exitButtonImage = createCornerButton('Exit', 1, 0, 'https://www.roblox.com/asset/?id=261878266', 2/3)
  474.         exitButton.AutoButtonColor = false
  475.         exitButton.Visible = not isTenFootInterface
  476.         exitButton.Selectable = not isTenFootInterface
  477.        
  478.         local buttonEffectFunction = devConsole:CreateButtonEffectFunction(exitButton)
  479.        
  480.         devConsole:ConnectButtonHover(exitButton, function(clicking, hovering)
  481.             if hovering and not clicking then
  482.                 setCornerButtonImageSize(exitButtonImage, 3/4)
  483.             else
  484.                 setCornerButtonImageSize(exitButtonImage, 2/3)
  485.             end
  486.             buttonEffectFunction(clicking, hovering)
  487.         end)
  488.        
  489.         exitButton.MouseButton1Click:connect(function()
  490.             devConsole:SetVisible(false, true)
  491.         end)
  492.     end
  493.    
  494.     do -- Repositioning and Resizing
  495.        
  496.         do -- Create bottom right window resize button and activate resize dragging
  497.             local resizeButton, resizeButtonImage = createCornerButton('Resize', 1, 1, 'https://www.roblox.com/asset/?id=261880743', 1)
  498.             resizeButtonImage.Position = UDim2_new(0, 0, 0, 0)
  499.             resizeButtonImage.Size = UDim2_new(1, 0, 1, 0)
  500.             resizeButton.Selectable = not isTenFootInterface
  501.  
  502.             local dragging = false
  503.            
  504.             local buttonEffectFunction = devConsole:CreateButtonEffectFunction(resizeButton)
  505.            
  506.             devConsole:ConnectButtonDragging(resizeButton, function()
  507.                 local x0, y0 = frame.AbsoluteSize.X, frame.AbsoluteSize.Y
  508.                 return function(dx, dy)
  509.                     devConsole:SetFrameSize(x0 + dx, y0 + dy)
  510.                 end
  511.             end, function(clicking, hovering)
  512.                 dragging = clicking
  513.                 buttonEffectFunction(clicking, hovering)
  514.             end)
  515.            
  516.         end
  517.        
  518.         do -- Activate top handle dragging
  519.             local frame = devConsole.Frame
  520.             local handle = frame.Handle
  521.            
  522.             local buttonEffectFunction = devConsole:CreateButtonEffectFunction(handle)
  523.            
  524.             devConsole:ConnectButtonDragging(handle, function()
  525.                 local x, y = frame.AbsolutePosition.X, frame.AbsolutePosition.Y
  526.                 return function(dx, dy)
  527.                     devConsole:SetFramePosition(x + dx, y + dy)
  528.                 end
  529.                 --deltaCallback_Resize(-dx, -dy) -- Used if they are grabbing both at the same time
  530.             end, buttonEffectFunction)
  531.         end
  532.     end
  533.    
  534.     -- interiorFrame contains tabContainer and window
  535.     local interiorFrame = Primitives.FolderFrame(frame, 'Interior')
  536.     interiorFrame.Position = UDim2_new(0, 0, 0, Style.HandleHeight)
  537.     interiorFrame.Size = UDim2_new(1, -(Style.HandleHeight + Style.BorderSize * 2), 1, -(Style.HandleHeight + Style.BorderSize))
  538.    
  539.     local windowContainer = Primitives.FolderFrame(interiorFrame, 'WindowContainer')
  540.     windowContainer.Size = UDim2_new(1, 0, 1, -(Style.TabHeight))
  541.     windowContainer.Position = UDim2_new(0, Style.BorderSize, 0, Style.TabHeight)
  542.    
  543.     -- This is what applies ClipsDescendants to tab contents
  544.     local window = Primitives.Frame(windowContainer, 'Window')
  545.     window.Size = UDim2_new(1, 0, 1, 0) -- The tab open/close methods, and the consoles also set this
  546.     window.Position = UDim2_new(0, 0, 0, 0)
  547.     window.ClipsDescendants = true
  548.    
  549.     -- This is the frame that moves around with the scroll bar
  550.     local body = Primitives.FolderFrame(window, 'Body')
  551.    
  552.     do -- Scrollbars
  553.         local scrollbar = devConsole:CreateScrollbar()
  554.         devConsole.WindowScrollbar = scrollbar
  555.         local scrollbarFrame = scrollbar.Frame
  556.         scrollbarFrame.Parent = frame
  557.         scrollbarFrame.Size = UDim2_new(0, Style.HandleHeight, 1, -(Style.HandleHeight + Style.BorderSize) * 2)
  558.         scrollbarFrame.Position = UDim2_new(1, -Style.HandleHeight, 0, Style.HandleHeight + Style.BorderSize)
  559.        
  560.         devConsole:ApplyScrollbarToFrame(scrollbar, window, body, frame)
  561.     end
  562.    
  563.     local tabContainer = Primitives.FolderFrame(interiorFrame, 'Tabs') -- Shouldn't this be named 'tabFrame'?
  564.     tabContainer.Size = UDim2_new(1, -(Style.GearSize + Style.BorderSize), 0, Style.TabHeight)
  565.     tabContainer.Position = UDim2_new(0, 0, 0, 0)
  566.     tabContainer.ClipsDescendants = true
  567.    
  568.     -- Options button
  569.     local optionsButton = Primitives.InvisibleButton(frame, 'OptionsButton')
  570.    
  571.     local optionsClippingFrame = Primitives.FolderFrame(interiorFrame, 'OptionsClippingFrame')
  572.     optionsClippingFrame.ClipsDescendants = true
  573.     optionsClippingFrame.Position = UDim2_new(0, 0, 0, 0)
  574.     optionsClippingFrame.Size = UDim2_new(1, 0, 0, 0)
  575.     local optionsFrame = Primitives.FolderFrame(optionsClippingFrame, 'OptionsFrame')
  576.     optionsFrame.Size = UDim2_new(1, 0, 0, Style.OptionAreaHeight)
  577.     optionsFrame.Position = UDim2_new(0, 0, 0, Style.OptionAreaHeight)
  578.     --optionsFrame.BackgroundColor3 = Style.OptionsFrameColor
  579.     do -- Options animation
  580.        
  581.         local gearSize = Style.GearSize
  582.         local tabHeight = Style.TabHeight
  583.         local offset = (tabHeight - gearSize) / 2
  584.         optionsButton.Size = UDim2_new(0, Style.GearSize, 0, Style.GearSize)
  585.         optionsButton.Position = UDim2_new(1, -(Style.GearSize + offset + Style.HandleHeight), 0, Style.HandleHeight + offset)
  586.         local gear = Primitives.InvisibleImageLabel(optionsButton, 'Image', 'https://www.roblox.com/asset/?id=261882463')
  587.         --gear.ZIndex = ZINDEX + 1
  588.         local animationToggle = devConsole:GenerateOptionButtonAnimationToggle(interiorFrame, optionsButton, gear, tabContainer, optionsClippingFrame, optionsFrame)
  589.         local open = false
  590.         optionsButton.MouseButton1Click:connect(function()
  591.             open = not open
  592.             animationToggle(open)
  593.         end)
  594.        
  595.     end
  596.    
  597.     -- Console/Log and Stats options
  598.     local setShownOptionTypes; -- Toggles what options to show: setOptionType({Log = true})
  599.    
  600.     local textFilter, scriptStatFilter;
  601.     local textFilterChanged, scriptStatFilterChanged;
  602.    
  603.     local messageFilter;
  604.     local messageFilterChanged, messageTextWrappedChanged;
  605.     do -- Options contents/filters
  606.        
  607.         local function createCheckbox(color, callback)
  608.             local this = {
  609.                 Value = true;
  610.             }
  611.            
  612.             local frame = Primitives.FolderFrame(nil, 'Checkbox')
  613.             this.Frame = frame
  614.             frame.Size = UDim2_new(0, Style.CheckboxSize, 0, Style.CheckboxSize)
  615.             frame.BackgroundColor3 = color
  616.            
  617.             local padding = 2
  618.            
  619.             local function f(xs, xp, yp) -- quick way to get an opaque border around a transparent center
  620.                 local ys = 1 - xs
  621.                 local f = Primitives.Frame(frame, 'Border')
  622.                 f.BackgroundColor3 = color
  623.                 f.BackgroundTransparency = 0
  624.                 f.Size = UDim2_new(xs, ys * padding, ys, xs * padding)
  625.                 f.Position = UDim2_new(xp, -xp * padding, yp, -yp * padding)
  626.             end
  627.             f(1, 0, 0)
  628.             f(1, 0, 1)
  629.             f(0, 0, 0)
  630.             f(0, 1, 0)
  631.            
  632.             local button = Primitives.Button(frame, 'Button')
  633.             button.Size = UDim2_new(1, -padding * 2, 1, -padding * 2)
  634.             button.Position = UDim2_new(0, padding, 0, padding)
  635.            
  636.             local buttonEffectFunction = devConsole:CreateButtonEffectFunction(button)
  637.            
  638.             local check = Primitives.Frame(button, 'Check')
  639.            
  640.             local padding = 4
  641.             check.Size = UDim2_new(1, -padding * 2, 1, -padding * 2)
  642.             check.Position = UDim2_new(0, padding, 0, padding)
  643.             check.BackgroundColor3 = color
  644.             check.BackgroundTransparency = 0
  645.            
  646.             devConsole:ConnectButtonHover(button, buttonEffectFunction)
  647.            
  648.             function this.SetValue(this, value)
  649.                 if value == this.Value then
  650.                     return
  651.                 end
  652.                 this.Value = value
  653.                 check.Visible = value
  654.                 this.Value = value
  655.                 callback(value)
  656.             end
  657.            
  658.             button.MouseButton1Click:connect(function()
  659.                 this:SetValue(not this.Value)
  660.             end)
  661.            
  662.             return this
  663.         end
  664.        
  665.         local string_find = string.find
  666.         local containsString; -- the text typed into the search textBox, nil if equal to ""
  667.        
  668.         function textFilter(text)
  669.             return not containsString or string_find(text:lower(), containsString)
  670.         end
  671.        
  672.         local filterLookup = {} -- filterLookup[Enum.MessageType.x.Value] = true or false
  673.         function messageFilter(message)
  674.             return filterLookup[message.Type] and (not containsString or string_find(message.Message:lower(), containsString))
  675.         end
  676.        
  677.         -- Events
  678.         textFilterChanged = CreateSignal()
  679.         scriptStatFilterChanged = CreateSignal()
  680.        
  681.         messageFilterChanged = CreateSignal()
  682.         messageTextWrappedChanged = CreateSignal()
  683.        
  684.         local optionTypeContainers = {
  685.             --[OptionType] = Frame
  686.             --Log = Frame;
  687.             --Scripts = Frame;
  688.         }
  689.         function setShownOptionTypes(shownOptionTypes)
  690.             -- Example showOptionTypes:
  691.             -- {Log = true}
  692.             for optionType, container in pairs(optionTypeContainers) do
  693.                 container.Visible = shownOptionTypes[optionType] or false
  694.             end
  695.         end
  696.        
  697.         do -- Log options
  698.             local container = Primitives.FolderFrame(optionsFrame, 'Log')
  699.             container.Visible = false
  700.             optionTypeContainers.Log = container
  701.  
  702.             local label = Primitives.InvisibleTextLabel(container, 'FilterLabel', "Filters")
  703.             label.FontSize = 'Size18'
  704.             label.TextXAlignment = 'Left'
  705.             label.Size = UDim2_new(0, 54, 0, Style.CheckboxSize)
  706.             label.Position = UDim2_new(0, 4, 0, 2)
  707.    
  708.             do
  709.                 local x = label.Size.X.Offset
  710.                 local messageColors = Style.MessageColors
  711.                 for i = 0, #messageColors do -- 0, 3 initially
  712.                     local checkbox = createCheckbox(messageColors[i], function(value)
  713.                         filterLookup[i] = value
  714.                         messageFilterChanged:fire()
  715.                     end)
  716.                     filterLookup[i] = checkbox.Value
  717.                     checkbox.Frame.Parent = container
  718.                     checkbox.Frame.Position = UDim2_new(0, x, 0, 4)
  719.                     x = x + Style.CheckboxSize + 4
  720.                 end
  721.                
  722.                 do -- Word wrap
  723.                     x = x + 8
  724.                    
  725.                     local label = Primitives.InvisibleTextLabel(container, 'WrapLabel', "Word Wrap")
  726.                     label.FontSize = 'Size18'
  727.                     label.TextXAlignment = 'Left'
  728.                     label.Size = UDim2_new(0, 54 + Style.CheckboxSize, 0, Style.CheckboxSize)
  729.                     label.Position = UDim2_new(0, x + 4, 0, 2)
  730.                    
  731.                     local checkbox = createCheckbox(Color3.new(0.65, 0.65, 0.65), function(value)
  732.                         messageTextWrappedChanged:fire(value) -- an event isn't ideal here
  733.                     end)
  734.                     checkbox:SetValue(false)
  735.                     checkbox.Frame.Parent = container
  736.                     checkbox.Frame.Position = UDim2_new(0, x + label.Size.X.Offset, 0, 4)
  737.                 end
  738.             end
  739.         end
  740.        
  741.         do -- Scripts options
  742.             local container = Primitives.FolderFrame(optionsFrame, 'Stats')
  743.             container.Visible = false
  744.             optionTypeContainers.Scripts = container
  745.  
  746.             do
  747.                 local x = 0
  748.                
  749.                 do -- Show inactive
  750.                     x = x + 4
  751.                     local label = Primitives.InvisibleTextLabel(container, 'FilterLabel', "Show inactive")
  752.                     label.FontSize = 'Size18'
  753.                     label.TextXAlignment = 'Left'
  754.                     label.Size = UDim2_new(0, label.TextBounds.X + 6, 0, Style.CheckboxSize)
  755.                     label.Position = UDim2_new(0, x, 0, 2)
  756.                     x = x + label.Size.X.Offset
  757.                    
  758.                     local showInactive;
  759.                     local function getScriptCurrentlyActive(chartStat)
  760.                         local stats = chartStat.Stats
  761.                         if stats then
  762.                             local stat = stats[#stats]
  763.                             if stat then
  764.                                 return stat[1] > 0.000001 or stat[2] > 0.000001
  765.                             end
  766.                         end
  767.                         return false
  768.                     end
  769.                     function scriptStatFilter(chartStat)
  770.                         return (showInactive or getScriptCurrentlyActive(chartStat))
  771.                             and (not containsString or string_find(chartStat.Name:lower(), containsString))
  772.                     end
  773.                        
  774.                     local checkbox = createCheckbox(Color3_new(1, 1, 1), function(value)
  775.                         showInactive = value
  776.                         scriptStatFilterChanged:fire()
  777.                     end)
  778.                     showInactive = checkbox.Value
  779.                     checkbox.Frame.Parent = container
  780.                     checkbox.Frame.Position = UDim2_new(0, x, 0, 4)
  781.                     x = x + Style.CheckboxSize + 4
  782.                
  783.                 end
  784.  
  785.                 x = x + 8
  786.             end
  787.         end
  788.        
  789.         do -- Search/filter/contains textbox
  790.            
  791.             local container = Primitives.FolderFrame(optionsFrame, 'Search')
  792.             container.Visible = false
  793.             optionTypeContainers.Search = container
  794.      
  795.             local label = Primitives.InvisibleTextLabel(container, 'FilterLabel', "Contains:")
  796.             label.FontSize = 'Size18'
  797.             label.TextXAlignment = 'Left'
  798.             label.Size = UDim2_new(0, 60, 0, Style.CheckboxSize)
  799.             label.Position = UDim2_new(0, 4, 0, 4 + Style.CheckboxSize + 4)
  800.            
  801.             local textBox = Primitives.TextBox(container, 'ContainsFilter')
  802.             textBox.ClearTextOnFocus = true
  803.             textBox.FontSize = 'Size18'
  804.             textBox.TextXAlignment = 'Left'
  805.             textBox.Size = UDim2_new(0, 150, 0, Style.CheckboxSize)
  806.             textBox.Position = UDim2_new(0, label.Position.X.Offset + label.Size.X.Offset + 4, 0, 4 + Style.CheckboxSize + 4)
  807.             textBox.Text = ""
  808.        
  809.             local runningColor = Color3.new(0, 0.5, 0)
  810.             local normalColor = textBox.BackgroundColor3
  811.            
  812.             connectPropertyChanged(textBox, 'Text', function(text)
  813.                 text = text:lower()
  814.                 if text == "" then
  815.                     text = nil
  816.                 end
  817.                 if text == containsString then
  818.                     return
  819.                 end
  820.                 textBox.BackgroundColor3 = text and runningColor or normalColor
  821.                 containsString = text
  822.                 messageFilterChanged:fire()
  823.                 textFilterChanged:fire()
  824.             end)
  825.            
  826.             connectPropertyChanged(textBox, 'TextBounds', function(textBounds)
  827.                 textBox.Size = UDim2_new(0, math.max(textBounds.X, 150), 0, Style.CheckboxSize)
  828.             end)
  829.         end
  830.     end
  831.    
  832.     ----------
  833.     -- Tabs --
  834.     ----------
  835.     do -- Console/Log tabs
  836.        
  837.         -- Wrapper for :AddTab
  838.         local function createConsoleTab(name,
  839.         text,
  840.         outputMessageSync,
  841.         commandLineVisible,
  842.         commandInputtedCallback,
  843.         openCallback)
  844.             local tabBody = Primitives.FolderFrame(body, name)
  845.             local output, commandLine;
  846.             local disconnector = CreateDisconnectSignal()
  847.            
  848.             local tab = devConsole:AddTab(text, tabBody, function(open)
  849.                 if commandLine then
  850.                     commandLine.Frame.Visible = open
  851.                 end
  852.                
  853.                 if open then
  854.                     setShownOptionTypes({
  855.                         Log = true;
  856.             Search = true;
  857.                     })
  858.                    
  859.                     if not output then
  860.                         output = devConsole:CreateOutput(outputMessageSync:GetMessages(), messageFilter)
  861.                         output.Frame.Parent = tabBody
  862.                     end
  863.                    
  864.                     output:SetVisible(true)
  865.                    
  866.                     if commandLineVisible then
  867.                         if open and not commandLine then
  868.                             commandLine = devConsole:CreateCommandLine()
  869.                             commandLine.Frame.Parent = frame
  870.                             commandLine.Frame.Size = UDim2_new(1,
  871.                 -(Style.HandleHeight + Style.BorderSize * 2),
  872.                 0,
  873.                 Style.CommandLineHeight)
  874.                             commandLine.Frame.Position = UDim2_new(0,
  875.                 Style.BorderSize,
  876.                 1,
  877.                 -(Style.CommandLineHeight + Style.BorderSize))
  878.                             commandLine.CommandInputted:connect(commandInputtedCallback)
  879.                         end
  880.                     end
  881.  
  882.                     window.Size = commandLineVisible
  883.                         and UDim2_new(1, 0, 1, -(Style.HandleHeight))
  884.                         or  UDim2_new(1, 0, 1, 0)
  885.  
  886.                    
  887.                     local messages = outputMessageSync:GetMessages()
  888.                    
  889.                     local height = output:RefreshMessages()
  890.                     body.Size = UDim2_new(1, 0, 0, height)
  891.                    
  892.                     disconnector:connect(output.HeightChanged:connect(function(height)
  893.                         body.Size = UDim2_new(1, 0, 0, height)
  894.                     end))
  895.                     body.Size = UDim2_new(1, 0, 0, output.Height)
  896.                    
  897.                     disconnector:connect(outputMessageSync.MessageAdded:connect(function(message)
  898.                         output:RefreshMessages(#messages)
  899.                     end))
  900.                    
  901.                     disconnector:connect(messageFilterChanged:connect(function()
  902.                         output:RefreshMessages()
  903.                     end))
  904.                     disconnector:connect(messageTextWrappedChanged:connect(function(enabled)
  905.                         output:SetTextWrappedEnabled(enabled)
  906.                     end))
  907.                 else
  908.                     if output then
  909.                         output:SetVisible(false)
  910.                     end
  911.                     window.Size = UDim2_new(1, 0, 1, 0)
  912.                     disconnector:fire()
  913.                 end
  914.                 if openCallback then
  915.                     openCallback(open)
  916.                 end
  917.             end)
  918.  
  919.             return tab
  920.         end
  921.        
  922.         -- Client Log tab --
  923.         if permissions.MayViewClientLog then
  924.             local tab = createConsoleTab(
  925.                 'ClientLog', "Client Log",
  926.                 devConsole.MessagesAndStats.OutputMessageSyncLocal,
  927.                 true,
  928.                 function(text)
  929.                     if #text <= 1 then
  930.                         return
  931.                     end
  932.                     local f, err = loadstring(text)
  933.                     if err then
  934.                         warn(err)
  935.                         return
  936.                     end
  937.                     f();
  938.                 end
  939.             )
  940.             tab:SetVisible(true)      
  941.             tab:SetOpen(true)
  942.         end
  943.     end
  944.  
  945.  
  946.     return devConsole  
  947. end
  948.  
  949. ----------------------
  950. -- Backup GUI Mouse --
  951. ----------------------
  952. do -- This doesn't support multiple windows very well
  953.     function Methods.EnableGUIMouse(devConsole)
  954.         local label = Instance.new("ImageLabel")
  955.         label.BackgroundTransparency = 1
  956.         label.BorderSizePixel = 0
  957.         label.Size = UDim2.new(0, 64, 0, 64)
  958.         label.Image = "rbxasset://Textures/ArrowFarCursor.png"
  959.         label.Name = "BackupMouse"
  960.         label.ZIndex = Style.ZINDEX + 2
  961.        
  962.         local disconnector = CreateDisconnectSignal()
  963.        
  964.         local enabled = false
  965.        
  966.         local mouse = game:GetService("Players").LocalPlayer:GetMouse()
  967.        
  968.         local function Refresh()
  969.             local enabledNew = devConsole.Visible and not UserInputService.MouseIconEnabled
  970.             if enabledNew == enabled then
  971.                 return
  972.             end
  973.             enabled = enabledNew
  974.             label.Visible = enabled
  975.             label.Parent = enabled and devConsole.ScreenGui or nil
  976.             disconnector:fire()
  977.             if enabled then
  978.                 label.Position = UDim2.new(0, mouse.X - 32, 0, mouse.Y - 32)
  979.                 disconnector:connect(UserInputService.InputChanged:connect(function(input)
  980.                     if input.UserInputType == Enum.UserInputType.MouseMovement then
  981.                         --local p = input.Position
  982.                         --if p then
  983.                         label.Position = UDim2.new(0, mouse.X - 32, 0, mouse.Y - 32)
  984.                         --end
  985.                     end
  986.                 end))
  987.             end
  988.         end
  989.        
  990.         Refresh()
  991.         local userInputServiceListener;
  992.         devConsole.VisibleChanged:connect(function(visible)
  993.             if userInputServiceListener then
  994.                 userInputServiceListener:disconnect()
  995.                 userInputServiceListener = nil
  996.             end
  997.            
  998.             userInputServiceListener = UserInputService.Changed:connect(Refresh)
  999.            
  1000.             Refresh()
  1001.         end)
  1002.        
  1003.     end
  1004. end
  1005.  
  1006. --------------------
  1007. -- Output console --
  1008. --------------------
  1009. do
  1010.     function Methods.CreateCommandLine(devConsole)
  1011.         local this = {
  1012.             CommandInputted = CreateSignal();
  1013.         }
  1014.        
  1015.         local frame = Primitives.FolderFrame(nil, 'CommandLine')
  1016.         this.Frame = frame
  1017.         frame.Size = UDim2_new(1, 0, 0, Style.CommandLineHeight)
  1018.        
  1019.         local textBoxFrame = Primitives.Frame(frame, 'TextBoxFrame')
  1020.         textBoxFrame.Size = UDim2_new(1, 0, 0, Style.CommandLineHeight)
  1021.         textBoxFrame.Position = UDim2_new(0, 0, 0, 0)
  1022.         textBoxFrame.ClipsDescendants = true
  1023.        
  1024.         local label = Primitives.InvisibleTextLabel(textBoxFrame, 'Label', ">")
  1025.         label.Position = UDim2_new(0, 4, 0, 0)
  1026.         label.Size = UDim2_new(0, 12, 1, -1)
  1027.         label.FontSize = 'Size14'
  1028.        
  1029.         local DEFAULT_COMMAND_BAR_TEXT = "Type command here"
  1030.        
  1031.         local textBox = Primitives.TextBox(textBoxFrame, 'TextBox')
  1032.         --textBox.TextWrapped = true -- This needs to auto-resize
  1033.         textBox.BackgroundTransparency = 1
  1034.         textBox.Text = DEFAULT_COMMAND_BAR_TEXT
  1035.         textBox.ClearTextOnFocus = false
  1036.         local padding = 2
  1037.         textBox.Size = UDim2_new(1, -(padding * 2) - 4 - 12, 0, 500)
  1038.         textBox.Position = UDim2_new(0, 4 + 12 + padding, 0, 0)
  1039.         textBox.TextXAlignment = 'Left'
  1040.         textBox.TextYAlignment = 'Top'
  1041.         textBox.FontSize = 'Size18'
  1042.         textBox.TextWrapped = true
  1043.  
  1044.         -- override SelectionImageObject to better fit
  1045.         if isTenFootInterface then
  1046.             local selectionImage = Instance.new('ImageLabel')
  1047.             selectionImage.Name = "SelectionImage"
  1048.             selectionImage.Size = UDim2.new(1, textBoxFrame.AbsoluteSize.x + 36, 0, Style.CommandLineHeight + 24)
  1049.             selectionImage.Position = UDim2.new(0, -18, 0, -12)
  1050.             selectionImage.Image = 'rbxasset://textures/ui/SelectionBox.png'
  1051.             selectionImage.ScaleType = Enum.ScaleType.Slice
  1052.             selectionImage.SliceCenter = Rect.new(21,21,41,41)
  1053.             selectionImage.BackgroundTransparency = 1
  1054.  
  1055.             textBox.SelectionImageObject = selectionImage
  1056.         end
  1057.        
  1058.         do
  1059.             local defaultSize = UDim2_new(1, 0, 0, Style.CommandLineHeight)
  1060.             local first = true
  1061.            
  1062.             textBox.Changed:connect(function(property)
  1063.                 if property == 'TextBounds' or property == 'AbsoluteSize' then
  1064.                     if first then -- There's a glitch that only occurs on the first change
  1065.                         first = false
  1066.                         return
  1067.                     end
  1068.                     local textBounds = textBox.TextBounds
  1069.                     if textBounds.Y > Style.CommandLineHeight then
  1070.                         textBoxFrame.Size = UDim2_new(1, 0, 0, textBounds.Y + 2)
  1071.                     else
  1072.                         textBoxFrame.Size = defaultSize
  1073.                     end
  1074.                 end
  1075.             end)
  1076.         end
  1077.        
  1078.         local disconnector = CreateDisconnectSignal()
  1079.        
  1080.         local backtrackPosition = 0
  1081.         local inputtedText = {}
  1082.         local isLastWeak = false
  1083.         local function addInputtedText(text, weak)
  1084.             -- weak means it gets overwritten by the next text that's inputted
  1085.             if isLastWeak then
  1086.                 table.remove(inputtedText, 1)
  1087.             end
  1088.             if inputtedText[1] == text then
  1089.                 isLastWeak = isLastWeak and weak
  1090.                 return
  1091.             end
  1092.             isLastWeak = weak
  1093.             if not weak then
  1094.                 for i = #inputtedText, 1, -1 do
  1095.                     if inputtedText[i] == text then
  1096.                         table.remove(inputtedText, i)
  1097.                     end
  1098.                 end
  1099.             end
  1100.             table.insert(inputtedText, 1, text)
  1101.         end
  1102.         local function backtrack(direction)
  1103.             backtrackPosition = backtrackPosition + direction
  1104.             if backtrackPosition < 1 then
  1105.                 backtrackPosition = 1
  1106.             elseif backtrackPosition > #inputtedText then
  1107.                 backtrackPosition = #inputtedText
  1108.             end
  1109.             if inputtedText[backtrackPosition] then
  1110.                 -- Setting the text doesn't always work, especially after losing focus without pressing enter, then clicking back
  1111.                 textBox.Text = inputtedText[backtrackPosition]
  1112.             end
  1113.         end
  1114.        
  1115.         local focusLostWithoutEnter = false
  1116.        
  1117.         textBox.Focused:connect(function()
  1118.             if textBox.Text == DEFAULT_COMMAND_BAR_TEXT then
  1119.                 textBox.Text = ""
  1120.             end
  1121.             disconnector:fire()
  1122.             backtrackPosition = 0
  1123.             disconnector:connect(UserInputService.InputBegan:connect(function(input)
  1124.                 if input.KeyCode == Enum.KeyCode.Up then
  1125.                     if backtrackPosition == 0 and not focusLostWithoutEnter then
  1126.                         -- They typed something, then pressed up. They might want what they typed back, so we store it
  1127.                         --  after they input the next thing, we know they meant to discard this, which is why it's "weak" (second arg is true)
  1128.                         addInputtedText(textBox.Text, true)
  1129.                         backtrackPosition = 1
  1130.                     end
  1131.                     backtrack(1)
  1132.                 elseif input.KeyCode == Enum.KeyCode.Down then
  1133.                     backtrack(-1)
  1134.                 end
  1135.             end))
  1136.         end)
  1137.        
  1138.         textBox.FocusLost:connect(function(enterPressed)
  1139.             disconnector:fire()
  1140.             if enterPressed then
  1141.                 focusLostWithoutEnter = false
  1142.                
  1143.                 local text = textBox.Text
  1144.                 addInputtedText(text, false)
  1145.                 this.CommandInputted:fire(text)
  1146.                 textBox.Text = ""
  1147.  
  1148.                 -- let's not spam the popup keyboard after text is entered
  1149.                 if not isTenFootInterface then
  1150.                     textBox:CaptureFocus()
  1151.                 end
  1152.             else
  1153.                 backtrackPosition = 0
  1154.                 focusLostWithoutEnter = true
  1155.                 addInputtedText(textBox.Text, true)
  1156.                 if textBox.Text == "" then
  1157.                     textBox.Text = DEFAULT_COMMAND_BAR_TEXT
  1158.                 end
  1159.             end
  1160.         end)
  1161.  
  1162.         return this
  1163.     end
  1164. end
  1165.  
  1166. do
  1167.     local padding = 5
  1168.     local LabelSize = UDim2_new(1, -padding, 0, 2048)
  1169.    
  1170.     local TextColors = Style.MessageColors
  1171.     local TextColorUnknown = Color3_new(0.5, 0, 1)
  1172.    
  1173.     local function isHidden(message)
  1174.         return false
  1175.     end
  1176.  
  1177.     function Methods.CreateOutput(devConsole, messages, messageFilter)
  1178.  
  1179.         -- AKA 'Log'
  1180.         local heightChanged = CreateSignal()
  1181.         local output = {
  1182.             Visible = false;
  1183.             Height = 0;
  1184.             HeightChanged = heightChanged;
  1185.             MessagesDirty = false;
  1186.             MessagesDirtyPosition = 1;
  1187.         }
  1188.        
  1189.         local function setHeight(height)
  1190.             height = height + 4
  1191.             output.Height = height
  1192.             heightChanged:fire(height)
  1193.         end
  1194.  
  1195.         -- The label container
  1196.         local frame = Primitives.FolderFrame(nil, 'Output')
  1197.         frame.ClipsDescendants = true
  1198.         output.Frame = frame
  1199.  
  1200.         local textWrappedEnabled = false
  1201.  
  1202.         do
  1203.             local lastX = 0
  1204.             connectPropertyChanged(frame, 'AbsoluteSize', function(size)
  1205.                 local currentX = size.X
  1206.                 --currentY = currentY - currentY
  1207.                 if currentX ~= lastX then
  1208.                     lastX = currentX
  1209.                     output:RefreshMessages()
  1210.                 end
  1211.             end)
  1212.         end
  1213.  
  1214.         local labels = {}
  1215.         local labelPositions = {}
  1216.  
  1217.         local function RefreshTextWrapped()
  1218.             if not output.Visible then
  1219.                 return
  1220.             end
  1221.             local y = 1
  1222.             for i = 1, #labels do
  1223.                 local label = labels[i]
  1224.                 label.TextWrapped = textWrappedEnabled
  1225.                 local height = label.TextBounds.Y
  1226.                 label.Size = LabelSize -- UDim2_new(1, 0, 0, height)
  1227.                 label.Position = UDim2_new(0, padding, 0, y)
  1228.                 y = y + height
  1229.                 if height > 16 then
  1230.                     y = y + 4
  1231.                 end
  1232.             end
  1233.             setHeight(y)
  1234.         end
  1235.         local MAX_LINES = 2048
  1236.  
  1237.         local function RefreshMessagesForReal(messageStartPosition)
  1238.             if not output.Visible then
  1239.                 return
  1240.             end
  1241.  
  1242.             local y = 1
  1243.             local labelPosition = 0 -- position of last used label
  1244.  
  1245.             -- Failed optimization:
  1246.             messageStartPosition = nil
  1247.             if messageStartPosition then
  1248.                 local labelPositionLast;
  1249.                 for i = messageStartPosition, math_max(1, #messages - MAX_LINES), -1 do
  1250.                     if labelPositions[i] then
  1251.                         labelPositionLast = labelPositions[i]
  1252.                         break
  1253.                     end
  1254.                 end
  1255.                 if labels[labelPositionLast] then
  1256.                     labelPosition = labelPositionLast
  1257.                     local label = labels[labelPositionLast]
  1258.                     y = label.Position.Y.Offset + label.Size.Y.Offset
  1259.                 else
  1260.                     messageStartPosition = nil
  1261.                 end
  1262.             end
  1263.  
  1264.             for i = messageStartPosition or math_max(1, #messages - MAX_LINES), #messages do
  1265.                 local message = messages[i]
  1266.                 if messageFilter(message) then
  1267.                     labelPosition = labelPosition + 1
  1268.                     labelPositions[i] = labelPosition
  1269.                     local label = labels[labelPosition]
  1270.                     if not label then
  1271.                         label = Instance_new('TextLabel', frame)
  1272.                         label.ZIndex = Style.ZINDEX
  1273.                         label.BackgroundTransparency = 1
  1274.                         --label.Font = Style.Font
  1275.                         label.FontSize = 'Size10'
  1276.                         label.TextXAlignment = 'Left'
  1277.                         label.TextYAlignment = 'Top'
  1278.                         labels[labelPosition] = label
  1279.                     end
  1280.                     label.TextWrapped = textWrappedEnabled
  1281.                     label.Size = LabelSize
  1282.                     label.TextColor3 = TextColors[message.Type] or TextColorUnknown
  1283.                     label.Text = message.Time .. " -- " .. message.Message
  1284.                    
  1285.                     local height = label.TextBounds.Y
  1286.                     label.Size = LabelSize -- UDim2_new(1, -padding, 0, height)
  1287.                     label.Position = UDim2_new(0, padding, 0, y)
  1288.  
  1289.                     y = y + height
  1290.                    
  1291.                     if height > 16 then
  1292.                         y = y + 4
  1293.                     end
  1294.                 else
  1295.                     labelPositions[i] = false
  1296.                 end
  1297.             end
  1298.  
  1299.             -- Destroy extra labels
  1300.             for i = #labels, labelPosition + 1, -1 do
  1301.                 labels[i]:Destroy()
  1302.                 labels[i] = nil
  1303.             end
  1304.            
  1305.             setHeight(y)
  1306.         end
  1307.        
  1308.         function output.SetMessagesDirty(output, messageStartPosition)
  1309.             if output.MessagesDirty then
  1310.                 return
  1311.             end
  1312.             output.MessagesDirty = true
  1313.             output.MessagesDirtyPosition = messageStartPosition
  1314.         end
  1315.  
  1316.         local refreshHandle;
  1317.         function output.RefreshMessages(output, messageStartPosition)
  1318.             if not output.Visible then
  1319.                 return
  1320.             end
  1321.             if not refreshHandle then
  1322.                 refreshHandle = true
  1323.                 coroutine.wrap(function() -- Not ideal
  1324.                     wait()
  1325.                     refreshHandle = false
  1326.                     RefreshMessagesForReal()
  1327.                 end)()
  1328.             end
  1329.         end
  1330.        
  1331.         function output.SetTextWrappedEnabled(output, textWrappedEnabledNew)
  1332.             if textWrappedEnabledNew == textWrappedEnabled then
  1333.                 return
  1334.             end
  1335.             textWrappedEnabled = textWrappedEnabledNew
  1336.             RefreshTextWrapped()
  1337.         end
  1338.        
  1339.         function output.SetVisible(output, visible)
  1340.             if visible == output.Visible then
  1341.                 return
  1342.             end
  1343.             output.Visible = visible
  1344.             if visible then
  1345.                 RefreshMessagesForReal()
  1346.             else
  1347.                 for i = #labels, 1, -1 do
  1348.                     labels[i]:Destroy()
  1349.                     labels[i] = nil
  1350.                 end
  1351.             end
  1352.         end
  1353.  
  1354.         return output
  1355.     end
  1356. end
  1357.  
  1358. ----------
  1359. -- Tabs --
  1360. ----------
  1361. function Methods.GetCurrentOpenTab(devConsole)
  1362.     local tabs = devConsole.Tabs
  1363.     if tabs == nil then
  1364.         return nil
  1365.     end
  1366.  
  1367.     for i = 1, #tabs do
  1368.         local tab = tabs[i]
  1369.         if tab.Open then
  1370.             return tab
  1371.         end
  1372.     end
  1373.     return nil
  1374. end
  1375.  
  1376.  
  1377. function Methods.RefreshTabs(devConsole)
  1378.     -- Go through and reposition them
  1379.     local x = Style.BorderSize
  1380.     local tabs = devConsole.Tabs
  1381.     for i = 1, #tabs do
  1382.         local tab = tabs[i]
  1383.         if tab.ButtonFrame.Visible then
  1384.             x = x + 3
  1385.             tab.ButtonFrame.Position = UDim2_new(0, x, 0, 0)
  1386.             x = x + tab.ButtonFrame.AbsoluteSize.X + 3
  1387.         end
  1388.     end
  1389. end
  1390.  
  1391. function Methods.AddTab(devConsole, text, body, openCallback, visibleCallback)
  1392.     -- Body is a frame that contains the tab contents
  1393.     body.Visible = false
  1394.    
  1395.     local tab = {
  1396.         Open = false; -- If the tab is open
  1397.         Visible = false; -- If the tab is shown
  1398.         OpenCallback = openCallback;
  1399.         VisibleCallback = visibleCallback;
  1400.         Body = body;
  1401.     }
  1402.  
  1403.     local nominalSize = TextService:GetTextSize(text, TAB_TEXT_SIZE, Enum.Font.SourceSans, Vector2.new(1e3, 1e3))
  1404.     local width = nominalSize.x + (TAB_TEXT_PADDING * 2)
  1405.    
  1406.     local buttonFrame = Primitives.InvisibleButton(devConsole.Frame.Interior.Tabs, 'Tab_' .. text)
  1407.     tab.ButtonFrame = buttonFrame
  1408.     buttonFrame.Size = UDim2_new(0, width, 0, Style.TabHeight)
  1409.     buttonFrame.Visible = false
  1410.    
  1411.     local textLabel = Primitives.TextLabel(buttonFrame, 'Label', text)
  1412.     textLabel.TextSize = TAB_TEXT_SIZE
  1413.     --textLabel.TextYAlignment = Enum.TextYAlignment.Top
  1414.    
  1415.     devConsole:ConnectButtonHover(buttonFrame, devConsole:CreateButtonEffectFunction(textLabel))
  1416.  
  1417.     -- These are the dimensions when the tab is closed
  1418.     local size0 = UDim2_new(1, 0, 1, -7)
  1419.     local position0 = UDim2_new(0, 0, 0, 4)
  1420.     -- There are the dimensions when the tab is open
  1421.     local size1 = UDim2_new(1, 0, 1, -4)
  1422.     local position1 = UDim2_new(0, 0, 0, 4)
  1423.     -- It starts closed
  1424.     textLabel.Size = size0
  1425.     textLabel.Position = position0
  1426.    
  1427.     function tab.SetVisible(tab, visible)
  1428.         if visible == tab.Visible then
  1429.             return
  1430.         end
  1431.         tab.Visible = visible
  1432.         tab:SetOpen(false)
  1433.         if tab.VisibleCallback then
  1434.             tab.VisibleCallback(visible)
  1435.         end
  1436.         buttonFrame.Visible = visible
  1437.         devConsole:RefreshTabs()
  1438.         if not visible then
  1439.             tab.SetOpen(false)
  1440.         end
  1441.     end
  1442.  
  1443.     function tab.RecordInitialOpen(tab)
  1444.     end
  1445.  
  1446.     function tab.RecordClickToOpen(tab)
  1447.     end
  1448.  
  1449.     function tab.SetOpen(tab, open)
  1450.         if open == tab.Open then
  1451.             return
  1452.         end
  1453.         tab.Open = open
  1454.  
  1455.         if open then
  1456.             if tab.SavedScrollbarValue then
  1457.                 devConsole.WindowScrollbar:SetValue(tab.SavedScrollbarValue)  -- This doesn't load correctly?
  1458.             end
  1459.             local tabs = devConsole.Tabs
  1460.             for i = 1, #tabs do
  1461.                 if tabs[i] ~= tab then
  1462.                     tabs[i]:SetOpen(false)
  1463.                 end
  1464.             end
  1465.             if body then
  1466.                 body.Visible = true
  1467.             end
  1468.             devConsole:RefreshTabs()
  1469.             -- Set dimensions for folder effect
  1470.             textLabel.Size = size1
  1471.             textLabel.Position = position1
  1472.             devConsole.CurrentOpenedTab = buttonFrame
  1473.         else
  1474.             tab.SavedScrollbarValue = devConsole.WindowScrollbar:GetValue() -- This doesn't save correctly
  1475.  
  1476.             if body then
  1477.                 body.Visible = false
  1478.                 -- todo: (not essential) these 2 lines should instead exist during open (above block) after going through tabs
  1479.                 devConsole.Frame.Interior.WindowContainer.Window.Body.Size = UDim2_new(1, 0, 1, 0)
  1480.                 devConsole.Frame.Interior.WindowContainer.Window.Body.Position = UDim2_new(0, 0, 0, 0)
  1481.             end
  1482.            
  1483.             -- Set dimensions for folder effect
  1484.             textLabel.Size = size0
  1485.             textLabel.Position = position0
  1486.         end
  1487.        
  1488.         if tab.OpenCallback then
  1489.             tab.OpenCallback(open)
  1490.         end
  1491.        
  1492.     end
  1493.    
  1494.     buttonFrame.MouseButton1Click:connect(function()
  1495.             if tab.Visible then
  1496.                 tab:RecordClickToOpen()
  1497.                 tab:SetOpen(true)
  1498.             end
  1499.         end)
  1500.    
  1501.     table.insert(devConsole.Tabs, tab)
  1502.    
  1503.     return tab
  1504.    
  1505. end
  1506.  
  1507. ----------------
  1508. -- Scroll bar --
  1509. ----------------
  1510. function Methods.ApplyScrollbarToFrame(devConsole,
  1511.     scrollbar,
  1512.     window,
  1513.     body,
  1514.     frame)  
  1515.     local windowHeight, bodyHeight
  1516.     local height = scrollbar:GetHeight()
  1517.     local value = scrollbar:GetValue()
  1518.     local function getHeights()
  1519.         return window.AbsoluteSize.Y, body.AbsoluteSize.Y
  1520.     end
  1521.     local function refreshDimension()
  1522.         local windowHeightNew, bodyHeightNew = getHeights()
  1523.  
  1524.         if bodyHeight ~= bodyHeightNew or windowHeight ~= windowHeightNew then
  1525.             bodyHeight, windowHeight = bodyHeightNew, windowHeightNew
  1526.             height = windowHeight / bodyHeight
  1527.             scrollbar:SetHeight(height)
  1528.            
  1529.             local yOffset = (bodyHeight - windowHeight) * value
  1530.       -- Never let yOffset go negative.
  1531.       -- Without this line, things that are smaller than the containing scroll
  1532.       -- window start at the bottom and grow up.
  1533.       -- It's a better UX to have things start at top and grow down.
  1534.       if (yOffset < 0) then
  1535.         yOffset = 0
  1536.       end
  1537.      
  1538.             local x = body.Position.X
  1539.             local y = body.Position.Y
  1540.      
  1541.             body.Position = UDim2_new(x.Scale, x.Offset, y.Scale, -math.floor(yOffset))
  1542.         end
  1543.  
  1544.     end
  1545.    
  1546.     local function setValue(valueNew)
  1547.         value = valueNew
  1548.         refreshDimension()
  1549.         local yOffset = (bodyHeight - windowHeight) * value
  1550.         local x = body.Position.X
  1551.         local y = body.Position.Y
  1552.         body.Position = UDim2_new(x.Scale, x.Offset, y.Scale, -math.floor(yOffset))
  1553.     end
  1554.     scrollbar.ValueChanged:connect(setValue)
  1555.     setValue(scrollbar:GetValue())
  1556.  
  1557.     local scrollDistance = 120
  1558.    
  1559.     window.Active = true
  1560.  
  1561.     scrollbar.ButtonUp.MouseButton1Click:connect(function()
  1562.         scrollbar:Scroll(-scrollDistance, getHeights())
  1563.     end)
  1564.     scrollbar.ButtonDown.MouseButton1Click:connect(function()
  1565.         scrollbar:Scroll(scrollDistance, getHeights())
  1566.     end)
  1567.  
  1568.     connectPropertyChanged(window, 'AbsoluteSize', refreshDimension)
  1569.     connectPropertyChanged(body, 'AbsoluteSize', function()
  1570.         local windowHeight, bodyHeight = getHeights()
  1571.         local value = scrollbar:GetValue()
  1572.         if value ~= 1 and value ~= 0 then
  1573.             local value = -body.Position.Y.Offset / (bodyHeight - windowHeight)
  1574.             scrollbar:SetValue(value)
  1575.         end
  1576.         refreshDimension()
  1577.     end)
  1578.  
  1579.     window.MouseWheelForward:connect(function()
  1580.         scrollbar:Scroll(-scrollDistance, getHeights())
  1581.     end)
  1582.     window.MouseWheelBackward:connect(function()
  1583.         scrollbar:Scroll(scrollDistance, getHeights())
  1584.     end)
  1585.     window.TouchPan:connect(function(positions, delta, velocity, userInputState)
  1586.         scrollbar:Scroll(-delta.y, getHeights())
  1587.     end)
  1588. end
  1589.  
  1590. function Methods.CreateScrollbar(devConsole, rotation)
  1591.     local scrollbar = {}
  1592.  
  1593.     local main = nil
  1594.     main = Primitives.FolderFrame(main, 'Scrollbar')
  1595.     scrollbar.Frame = main
  1596.  
  1597.     local frame = Primitives.Button(main, 'Frame')
  1598.     frame.AutoButtonColor = false
  1599.     frame.Size = UDim2_new(1, 0, 1, -(Style.HandleHeight) * 2 - 2)
  1600.     frame.Position = UDim2_new(0, 0, 0, Style.HandleHeight + 1)
  1601.     -- frame.BackgroundTransparency = 0.75
  1602.    
  1603.     -- This replaces the scrollbar when it's not being used
  1604.     local frame2 = Primitives.Frame(main, 'Frame')
  1605.     frame2.Size = UDim2_new(1, 0, 1, 0)
  1606.     frame2.Position = UDim2_new(0, 0, 0, 0)
  1607.    
  1608.     function scrollbar.SetVisible(scrollbar, visible)
  1609.         frame.Visible = visible
  1610.         frame2.Visible = not visible
  1611.     end
  1612.    
  1613.     local buttonUp = Primitives.ImageButton(frame, 'Up', 'https://www.roblox.com/asset/?id=261880783')
  1614.     scrollbar.ButtonUp = buttonUp
  1615.     buttonUp.Size = UDim2_new(1, 0, 0, Style.HandleHeight)
  1616.     buttonUp.Position = UDim2_new(0, 0, 0, -Style.HandleHeight - 1)
  1617.     buttonUp.AutoButtonColor = false
  1618.     devConsole:ConnectButtonHover(buttonUp, devConsole:CreateButtonEffectFunction(buttonUp))
  1619.  
  1620.     local buttonDown = Primitives.ImageButton(frame, 'Down', 'https://www.roblox.com/asset/?id=261880783')
  1621.     scrollbar.ButtonDown = buttonDown
  1622.     buttonDown.Size = UDim2_new(1, 0, 0, Style.HandleHeight)
  1623.     buttonDown.Position = UDim2_new(0, 0, 1, 1)
  1624.     buttonDown.Rotation = 180
  1625.     buttonDown.AutoButtonColor = false
  1626.     devConsole:ConnectButtonHover(buttonDown, devConsole:CreateButtonEffectFunction(buttonDown))
  1627.  
  1628.     local bar = Primitives.Button(frame, 'Bar')
  1629.     bar.Size = UDim2_new(1, 0, 0.5, 0)
  1630.     bar.Position = UDim2_new(0, 0, 0.25, 0)
  1631.    
  1632.     bar.AutoButtonColor = false
  1633.    
  1634.     local grip = Primitives.InvisibleImageLabel(bar, 'Image', 'https://www.roblox.com/asset/?id=261904959')
  1635.     grip.Size = UDim2_new(0, 16, 0, 16)
  1636.     grip.Position = UDim2_new(0.5, -8, 0.5, -8)
  1637.  
  1638.     local buttonEffectFunction = devConsole:CreateButtonEffectFunction(bar, nil, bar.BackgroundColor3, bar.BackgroundColor3)
  1639.    
  1640.     -- Inertial scrolling would be added around here
  1641.    
  1642.     local value = 1
  1643.     local valueChanged = CreateSignal()
  1644.     scrollbar.ValueChanged = valueChanged
  1645.     -- value = 0: at very top
  1646.     -- value = 1: at very bottom
  1647.    
  1648.     local height = 0.25
  1649.     local heightChanged = CreateSignal()
  1650.     scrollbar.HeightChanged = heightChanged
  1651.     -- height = 0: infinite page size
  1652.     -- height = 1: bar fills frame completely, no need to scroll
  1653.    
  1654.     local function getValueAtPosition(pos)
  1655.         return ((pos - main.AbsolutePosition.Y) / main.AbsoluteSize.Y) / (1 - height)
  1656.     end
  1657.    
  1658.     -- Refreshes the position and size of the scrollbar
  1659.     local function refresh()
  1660.         local y = height
  1661.         bar.Size = UDim2_new(1, 0, y, 0)
  1662.         bar.Position = UDim2_new(0, 0, value * (1 - y), 0)
  1663.     end
  1664.     refresh()
  1665.    
  1666.     function scrollbar.SetValue(scrollbar, valueNew)
  1667.         if valueNew < 0 then
  1668.             valueNew = 0
  1669.         elseif valueNew > 1 then
  1670.             valueNew = 1
  1671.         end
  1672.         if valueNew ~= value then
  1673.             value = valueNew
  1674.             refresh()
  1675.             valueChanged:fire(valueNew)
  1676.         end
  1677.     end
  1678.     function scrollbar.GetValue(scrollbar)
  1679.         return value
  1680.     end
  1681.    
  1682.     function scrollbar.Scroll(scrollbar, direction, windowHeight, bodyHeight)
  1683.         scrollbar:SetValue(value + direction / bodyHeight) -- needs to be adjusted
  1684.     end
  1685.    
  1686.     function scrollbar.SetHeight(scrollbar, heightNew)
  1687.         if heightNew < 0 then
  1688.             heightNew = 0 -- this is still an awkward case of divide-by-zero that shouldn't happen
  1689.         elseif heightNew > 1 then
  1690.             heightNew = 1
  1691.         end
  1692.         heightNew = math.max(heightNew, 0.1) -- Minimum scroll bar size, from that point on it is not the actual ratio
  1693.         if heightNew ~= height then
  1694.             height = heightNew
  1695.             scrollbar:SetVisible(heightNew < 1)
  1696.             refresh()
  1697.             heightChanged:fire(heightNew)
  1698.         end
  1699.     end
  1700.     function scrollbar.GetHeight(scrollbar)
  1701.         return height
  1702.     end
  1703.  
  1704.     devConsole:ConnectButtonDragging(bar, function()
  1705.         local value0 = value -- starting value
  1706.         return function(dx, dy)
  1707.             local dposition = dy -- net position change relative to the bar's axis (could support rotated scroll bars)
  1708.             local dvalue = (dposition / frame.AbsoluteSize.Y) / (1 - height) -- net value change
  1709.             scrollbar:SetValue(value0 + dvalue)
  1710.         end
  1711.     end, buttonEffectFunction)
  1712.    
  1713.     return scrollbar
  1714. end
  1715.  
  1716. ----------------------
  1717. -- Fancy color lerp --
  1718. ----------------------
  1719. local RenderLerpAnimation; do
  1720.     local math_cos = math.cos
  1721.     local math_pi = math.pi
  1722.     function RenderLerpAnimation(disconnectSignal, length, callback)
  1723.         disconnectSignal:fire()
  1724.         local timeStamp = tick()
  1725.         local listener = RunService.RenderStepped:connect(function()
  1726.             local t = (tick() - timeStamp) / length
  1727.             if t >= 1 then
  1728.                 t = 1
  1729.                 disconnectSignal:fire()
  1730.             else
  1731.                 t = (1 - math_cos(t * math_pi)) / 2 -- cosine interpolation aka 'Sine' in :TweenSizeAndPosition
  1732.             end
  1733.             callback(t)
  1734.         end)
  1735.         disconnectSignal:connect(listener)
  1736.         return listener
  1737.     end
  1738. end
  1739.  
  1740. if EYECANDY_ENABLED then
  1741.     -- This is the pretty version
  1742.     function Methods.CreateButtonEffectFunction(devConsole, button, normalColor, clickingColor, hoveringColor)
  1743.         normalColor = normalColor or button.BackgroundColor3
  1744.         clickingColor = clickingColor or Style.GetButtonDownColor(normalColor)
  1745.         hoveringColor = hoveringColor or Style.GetButtonHoverColor(normalColor)
  1746.         local disconnectSignal = CreateDisconnectSignal()
  1747.         return function(clicking, hovering)
  1748.             local color0 = button.BackgroundColor3
  1749.             local color1 = clicking and clickingColor or (hovering and hoveringColor or normalColor)
  1750.             local r0, g0, b0 = color0.r, color0.g, color0.b
  1751.             local r1, g1, b1 = color1.r, color1.g, color1.b
  1752.             local r2, g2, b2 = r1 - r0, g1 - g0, b1 - b0
  1753.             RenderLerpAnimation(disconnectSignal, clicking and 0.125 or 0.25, function(t)
  1754.                 button.BackgroundColor3 = Color3_new(r0 + r2 * t, g0 + g2 * t, b0 + b2 * t)
  1755.             end)
  1756.         end
  1757.     end
  1758. else
  1759.     -- This is the simple version
  1760.     function Methods.CreateButtonEffectFunction(devConsole, button, normalColor, clickingColor, hoveringColor)
  1761.         normalColor = normalColor or button.BackgroundColor3
  1762.         clickingColor = clickingColor or Style.GetButtonDownColor(normalColor)
  1763.         hoveringColor = hoveringColor or Style.GetButtonHoverColor(normalColor)
  1764.         return function(clicking, hovering)
  1765.             button.BackgroundColor3 = clicking and clickingColor or (hovering and hoveringColor or normalColor)
  1766.         end
  1767.     end
  1768. end
  1769.  
  1770. function Methods.GenerateOptionButtonAnimationToggle(devConsole, interior, button, gear, tabContainer, optionsClippingFrame, optionsFrame)
  1771.    
  1772.     local tabContainerSize0 = tabContainer.Size
  1773.     local tabContainerSize1 = UDim2_new(
  1774.         tabContainerSize0.X.Scale, tabContainerSize0.X.Offset + (Style.GearSize + 2) + Style.BorderSize,
  1775.         tabContainerSize0.Y.Scale, tabContainerSize0.Y.Offset)
  1776.        
  1777.     local gearRotation0 = gear.Rotation
  1778.     local gearRotation1 = gear.Rotation - 90
  1779.     local interiorSize0 = interior.Size
  1780.     local interiorSize1 = UDim2_new(interiorSize0.X.Scale, interiorSize0.X.Offset, interiorSize0.Y.Scale, interiorSize0.Y.Offset - Style.OptionAreaHeight)
  1781.     local interiorPosition0 = interior.Position
  1782.     local interiorPosition1 = UDim2_new(interiorPosition0.X.Scale, interiorPosition0.X.Offset, interiorPosition0.Y.Scale, interiorPosition0.Y.Offset + Style.OptionAreaHeight)
  1783.    
  1784.     local length = 0.5
  1785.     local disconnector = CreateDisconnectSignal()
  1786.     return function(open)
  1787.         if open then
  1788.             interior:TweenSizeAndPosition(interiorSize1, interiorPosition1, 'Out', 'Sine', length, true)
  1789.             tabContainer:TweenSize(tabContainerSize1, 'Out', 'Sine', length, true)
  1790.             optionsClippingFrame:TweenSizeAndPosition(
  1791.                 UDim2_new(1, 0, 0, Style.OptionAreaHeight),
  1792.                 UDim2_new(0, 0, 0, -Style.OptionAreaHeight),
  1793.                 'Out', 'Sine', length, true
  1794.             )
  1795.             optionsFrame:TweenPosition(
  1796.                 UDim2_new(0, 0, 0, 0),-- -Style.OptionAreaHeight),
  1797.                 'Out', 'Sine', length, true
  1798.             )
  1799.             local gearRotation = gear.Rotation
  1800.             RenderLerpAnimation(disconnector, length, function(t)
  1801.                 gear.Rotation = gearRotation1 * t + gearRotation * (1 - t)
  1802.             end)
  1803.         else
  1804.             interior:TweenSizeAndPosition(interiorSize0, interiorPosition0, 'Out', 'Sine', length, true)
  1805.             tabContainer:TweenSize(tabContainerSize0, 'Out', 'Sine', length, true)
  1806.             optionsClippingFrame:TweenSizeAndPosition(
  1807.                 UDim2_new(1, 0, 0, 0),
  1808.                 UDim2_new(0, 0, 0, 0),
  1809.                 'Out', 'Sine', length, true
  1810.             )
  1811.             optionsFrame:TweenPosition(
  1812.                 UDim2_new(0, 0, 0, Style.OptionAreaHeight),
  1813.                 'Out', 'Sine', length, true
  1814.             )
  1815.             local gearRotation = gear.Rotation
  1816.             RenderLerpAnimation(disconnector, length, function(t)
  1817.                 gear.Rotation = gearRotation0 * t + gearRotation * (1 - t)
  1818.             end)
  1819.         end
  1820.     end
  1821. end
  1822.  
  1823. ------------------------------
  1824. -- Events for color effects --
  1825. ------------------------------
  1826. do
  1827.     local globalInteractEvent = CreateSignal()
  1828.     function Methods.ConnectButtonHover(devConsole, button, mouseInteractCallback)
  1829.         -- void mouseInteractCallback(bool clicking, bool hovering)
  1830.        
  1831.         local this = {}
  1832.        
  1833.         local clicking = false
  1834.         local hovering = false
  1835.         local function set(clickingNew, hoveringNew)
  1836.             if hoveringNew and TouchEnabled then
  1837.                 hoveringNew = false -- Touch screens don't hover
  1838.             end
  1839.             if clickingNew ~= clicking or hoveringNew ~= hovering then
  1840.                 clicking, hovering = clickingNew, hoveringNew
  1841.                 mouseInteractCallback(clicking, hovering)
  1842.             end
  1843.         end
  1844.        
  1845.         button.MouseButton1Down:connect(function()
  1846.             set(true, true)
  1847.         end)
  1848.         button.MouseButton1Up:connect(function()
  1849.             set(false, true)
  1850.         end)
  1851.         button.MouseEnter:connect(function()
  1852.             set(clicking, true)
  1853.         end)
  1854.         button.MouseLeave:connect(function()
  1855.             set(false, false)
  1856.         end)
  1857.         --[[ these might cause memory leakes (when creating temporary buttons)
  1858.         -- This solves the case in which the user presses F9 while hovering over a button
  1859.         devConsole.VisibleChanged:connect(function()
  1860.             set(false, false)
  1861.         end)
  1862.        
  1863.         globalInteractEvent:connect(function()
  1864.             set(false, false)
  1865.         end)
  1866.         --]]
  1867.     end
  1868. end
  1869.  
  1870. -------------------------
  1871. -- Events for draggers -- (for the window's top handle, the resize button, and scrollbars)
  1872. -------------------------
  1873. function Methods.ConnectButtonDragging(devConsole, button, dragCallback, mouseInteractCallback)
  1874.    
  1875.     -- How dragCallback is called: local deltaCallback = dragCallback(xPositionAtMouseDown, yPositionAtMouseDown)
  1876.     -- How deltaCallback is called: deltaCallback(netChangeInAbsoluteXPositionSinceMouseDown, netChangeInAbsoluteYPositionSinceMouseDown)
  1877.    
  1878.     local dragging = false -- AKA 'clicking'
  1879.     local hovering = false
  1880.    
  1881.     local listeners = {}
  1882.    
  1883.     local disconnectCallback;
  1884.    
  1885.     local function stopDragging()
  1886.         if not dragging then
  1887.             return
  1888.         end
  1889.         dragging = false
  1890.         mouseInteractCallback(dragging, hovering)
  1891.         for i = #listeners, 1, -1 do
  1892.             listeners[i]:disconnect()
  1893.             listeners[i] = nil
  1894.         end
  1895.     end
  1896.    
  1897.     local ButtonUserInputTypes = {
  1898.         [Enum.UserInputType.MouseButton1] = true;
  1899.         [Enum.UserInputType.Touch] = true; -- I'm not sure if touch actually works here
  1900.     }
  1901.    
  1902.     local mouse = game:GetService("Players").LocalPlayer:GetMouse()
  1903.  
  1904.     local function startDragging(startP)
  1905.         if dragging then
  1906.             return
  1907.         end
  1908.         dragging = true
  1909.        
  1910.         mouseInteractCallback(dragging, hovering)
  1911.         local deltaCallback;
  1912.        
  1913.         local x0, y0 = startP.X, startP.Y
  1914.         --[[
  1915.         listeners[#listeners + 1] = UserInputService.InputBegan:connect(function(input)
  1916.             if ButtonUserInputTypes[input.UserInputType] then
  1917.                 local position = input.Position
  1918.                 if position and not x0 then
  1919.                     x0, y0 = position.X, position.Y -- The same click
  1920.                 end
  1921.             end
  1922.         end)
  1923.         --]]
  1924.         listeners[#listeners + 1] = UserInputService.InputEnded:connect(function(input)
  1925.             if ButtonUserInputTypes[input.UserInputType] then
  1926.                 stopDragging()
  1927.             end
  1928.         end)
  1929.         listeners[#listeners + 1] = UserInputService.InputChanged:connect(function(input)
  1930.        
  1931.             if not (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch)then -- added in a touch check
  1932.                 return
  1933.             end
  1934.            
  1935.             local p1 = input.Position
  1936.  
  1937.             if not p1 then
  1938.                 return
  1939.             end
  1940.             local x1, y1 = p1.X, p1.Y
  1941.             if not deltaCallback then
  1942.                 deltaCallback, disconnectCallback = dragCallback(x0 or x1, y0 or y1)
  1943.             end
  1944.             if x0 then
  1945.                 deltaCallback(x1 - x0, y1 - y0)
  1946.             end
  1947.         end)
  1948.     end
  1949.    
  1950.     --button.MouseButton1Down:connect(startDragging)
  1951.     --button.MouseButton1Up:connect(stopDragging)
  1952.    
  1953.     button.InputBegan:connect(function(iobj)
  1954.         if iobj.UserInputType == Enum.UserInputType.Touch or iobj.UserInputType == Enum.UserInputType.MouseButton1 then
  1955.             startDragging(iobj.Position)
  1956.         end
  1957.     end)
  1958.    
  1959.     button.InputEnded:connect(function(iobj)
  1960.         if iobj.UserInputType == Enum.UserInputType.Touch or iobj.UserInputType == Enum.UserInputType.MouseButton1 then
  1961.             stopDragging()
  1962.         end
  1963.     end)   
  1964.    
  1965.     button.MouseEnter:connect(function()
  1966.         if not hovering then
  1967.             hovering = true
  1968.             mouseInteractCallback(dragging, hovering)
  1969.         end
  1970.     end)
  1971.     button.MouseLeave:connect(function()
  1972.         if hovering then
  1973.             hovering = false
  1974.             mouseInteractCallback(dragging, hovering)
  1975.         end
  1976.     end)
  1977.    
  1978.     devConsole.VisibleChanged:connect(stopDragging)
  1979. end
  1980.  
  1981. -----------------
  1982. -- Permissions --
  1983. -----------------
  1984. do
  1985.     local permissionsLoading, permissions = false;
  1986.     function DeveloperConsole.GetPermissions()
  1987.         while permissionsLoading do wait() end
  1988.        
  1989.         if permissions then
  1990.             return permissions
  1991.         end
  1992.        
  1993.         permissions = {}
  1994.         permissionsLoading = true
  1995.         permissions.IsCreator = false
  1996.  
  1997.         local success, result = pcall(function()
  1998.             local url = string.format("/users/%d/canmanage/%d", game:GetService("Players").LocalPlayer.UserId, game.PlaceId)
  1999.             return game:GetService('HttpRbxApiService'):GetAsync(url, Enum.ThrottlingPriority.Default, Enum.HttpRequestType.Default, true)
  2000.         end)
  2001.         if success and type(result) == "string" then
  2002.             -- API returns: {"Success":BOOLEAN,"CanManage":BOOLEAN}
  2003.             -- Convert from JSON to a table
  2004.             -- pcall in case of invalid JSON
  2005.             success, result = pcall(function()
  2006.                 return game:GetService('HttpService'):JSONDecode(result)
  2007.             end)
  2008.             if success and result.CanManage == true then
  2009.                 permissions.IsCreator = result.CanManage
  2010.             end
  2011.         end
  2012.        
  2013.         permissions.ClientCodeExecutionEnabled = false
  2014.         pcall(function()
  2015.             permissions.ServerCodeExecutionEnabled = permissions.IsCreator and (not settings():GetFFlag("DebugDisableLogServiceExecuteScript"))
  2016.         end)
  2017.        
  2018.         if DEBUG or (RunService:IsStudio()) then
  2019.             permissions.IsCreator = true
  2020.             permissions.ServerCodeExecutionEnabled = true
  2021.         end
  2022.        
  2023.         permissions.MayViewServerLog = permissions.IsCreator
  2024.         permissions.MayViewClientLog = true
  2025.        
  2026.         permissions.MayViewServerStats = permissions.IsCreator
  2027.         permissions.MayViewServerMemory = permissions.IsCreator
  2028.         permissions.MayViewServerScripts = permissions.IsCreator
  2029.         permissions.MayViewServerJobs = permissions.IsCreator
  2030.  
  2031.         permissions.MayViewDataStoreBudget = false
  2032.         pcall(function()
  2033.             permissions.MayViewDataStoreBudget = permissions.IsCreator
  2034.         end)
  2035.         permissions.MayViewHttpResultClient = false
  2036.         permissions.MayViewHttpResultClient = permissions.IsCreator
  2037.         permissions.MayViewHttpResultServer = false
  2038.         permissions.MayViewHttpResultServer = permissions.IsCreator
  2039.  
  2040.         permissions.MayViewContextActionBindings = permissions.IsCreator
  2041.        
  2042.         permissionsLoading = false
  2043.        
  2044.         return permissions
  2045.     end
  2046. end
  2047.  
  2048. ----------------------
  2049. -- Output interface --
  2050. ----------------------
  2051. do
  2052.     local messagesAndStats;
  2053.     function DeveloperConsole.GetMessagesAndStats(permissions)
  2054.        
  2055.         if messagesAndStats then
  2056.             return messagesAndStats
  2057.         end
  2058.    
  2059.         local function NewOutputMessageSync(getMessages)
  2060.             local this;
  2061.             this = {
  2062.                 Messages = nil; -- Private member, DeveloperConsole should use :GetMessages()
  2063.                 MessageAdded = CreateSignal();
  2064.                 GetMessages = function()
  2065.                     local messages = this.Messages
  2066.                     if not messages then
  2067.                         -- If it errors while getting messages, it skip it next time
  2068.                         if this.Attempted then
  2069.                             messages = {}
  2070.                         else
  2071.                             this.Attempted = true
  2072.                             messages = getMessages(this)
  2073.                             this.Messages = messages
  2074.                         end
  2075.        
  2076.                     end
  2077.                     return messages
  2078.                 end;
  2079.             }
  2080.             return this
  2081.         end
  2082.        
  2083.         local ConvertTimeStamp; do
  2084.             -- Easy, fast, and working nicely
  2085.             local function numberWithZero(num)
  2086.                 return (num < 10 and "0" or "") .. num
  2087.             end
  2088.             local string_format = string.format -- optimization
  2089.             function ConvertTimeStamp(timeStamp)
  2090.                 local localTime = timeStamp - os_time() + math.floor(tick())
  2091.                 local dayTime = localTime % 86400
  2092.                        
  2093.                 local hour = math.floor(dayTime/3600)
  2094.                
  2095.                 dayTime = dayTime - (hour * 3600)
  2096.                 local minute = math.floor(dayTime/60)
  2097.                
  2098.                 dayTime = dayTime - (minute * 60)
  2099.                 local second = dayTime
  2100.    
  2101.                 local h = numberWithZero(hour)
  2102.                 local m = numberWithZero(minute)
  2103.                 local s = numberWithZero(dayTime)
  2104.        
  2105.                 return string_format("%s:%s:%s", h, m, s)
  2106.             end
  2107.         end
  2108.        
  2109.         local warningsToFilter = {"ClassDescriptor failed to learn", "EventDescriptor failed to learn", "Type failed to learn"}
  2110.        
  2111.         -- Filter "ClassDescriptor failed to learn" errors
  2112.         local function filterMessageOnAdd(message)
  2113.             if message.Type ~= Enum.MessageType.MessageWarning.Value then
  2114.                 return false
  2115.             end
  2116.             local found = false
  2117.             for _, filterString in ipairs(warningsToFilter) do
  2118.                 if string.find(message.Message, filterString) ~= nil then
  2119.                     found = true
  2120.                     break
  2121.                 end
  2122.             end
  2123.             return found
  2124.         end
  2125.    
  2126.         local outputMessageSyncLocal;
  2127.         if permissions.MayViewClientLog then
  2128.             outputMessageSyncLocal = NewOutputMessageSync(function(this)
  2129.                 local messages = {}
  2130.                
  2131.                 local LogService = game:GetService("LogService")
  2132.                 do -- This do block keeps history from sticking around in memory
  2133.                     local history = LogService:GetLogHistory()
  2134.                     for i = 1, #history do
  2135.                         local msg = history[i]
  2136.                         local message = {
  2137.                             Message = msg.message or "[DevConsole Error 1]";
  2138.                             Time = ConvertTimeStamp(msg.timestamp);
  2139.                             Type = msg.messageType.Value;
  2140.                         }
  2141.                         if not filterMessageOnAdd(message) then
  2142.                             messages[#messages + 1] = message
  2143.                         end
  2144.                     end
  2145.                 end
  2146.                
  2147.                 LogService.MessageOut:connect(function(text, messageType)
  2148.                     local message = {
  2149.                         Message = text or "[DevConsole Error 2]";
  2150.                         Time = ConvertTimeStamp(os_time());
  2151.                         Type = messageType.Value;
  2152.                     }
  2153.                     if not filterMessageOnAdd(message) then
  2154.                         messages[#messages + 1] = message
  2155.                         this.MessageAdded:fire(message)
  2156.                     end
  2157.                 end)
  2158.            
  2159.                 return messages
  2160.             end)
  2161.         end
  2162.        
  2163.         local outputMessageSyncServer;
  2164.         if permissions.MayViewServerLog then
  2165.             outputMessageSyncServer = NewOutputMessageSync(function(this)
  2166.                 local messages = {}
  2167.                
  2168.                 local LogService = game:GetService("LogService")
  2169.                
  2170.                 LogService.ServerMessageOut:connect(function(text, messageType, timestamp)
  2171.                     local message = {
  2172.                         Message = text or "[DevConsole Error 3]";
  2173.                         Time = ConvertTimeStamp(timestamp);
  2174.                         Type = messageType.Value;
  2175.                     }
  2176.                     if not filterMessageOnAdd(message) then
  2177.                         messages[#messages + 1] = message
  2178.                         this.MessageAdded:fire(message)
  2179.                     end
  2180.                 end)
  2181.                 LogService:RequestServerOutput()
  2182.                
  2183.                 return messages
  2184.             end)
  2185.         end
  2186.    
  2187.         local statsSyncServer;
  2188.         if (permissions.MayViewServerStats or
  2189.             permissions.MayViewServerScripts or
  2190.             permissions.MayViewServerMemory) then
  2191.            
  2192.             statsSyncServer = {
  2193.                 Stats = nil; -- Private member, use GetStats instead
  2194.                 StatsReceived = CreateSignal();
  2195.             }
  2196.             local statsListenerConnection;
  2197.             function statsSyncServer.GetStats(statsSyncServer)
  2198.                 local stats = statsSyncServer.Stats
  2199.                 if not stats then
  2200.                     stats = {}
  2201.                     pcall(function()
  2202.                         local clientReplicator = game:FindService("NetworkClient"):GetChildren()[1]
  2203.                             if clientReplicator then
  2204.                             statsListenerConnection = clientReplicator.StatsReceived:connect(function(stat)
  2205.                                 statsSyncServer.StatsReceived:fire(stat)
  2206.                             end)
  2207.                             clientReplicator:RequestServerStats(true)
  2208.                         end
  2209.                     end)
  2210.                     statsSyncServer.Stats = stats
  2211.                 end
  2212.                 return stats
  2213.             end
  2214.            
  2215.         end
  2216.         --]]
  2217.        
  2218.         messagesAndStats = {
  2219.             OutputMessageSyncLocal = outputMessageSyncLocal;
  2220.             OutputMessageSyncServer = outputMessageSyncServer;
  2221.             StatsSyncServer = statsSyncServer;
  2222.         }
  2223.        
  2224.         return messagesAndStats
  2225.     end
  2226. end
  2227.  
  2228. --[[ Module Table ]]--
  2229. -- We only create the dev console if we need it; user toggles visibility.
  2230.  
  2231. local DevConsoleModuleTable = {}
  2232. local myDeveloperConsole = nil
  2233.  
  2234. -- Tenfoot Interface set up
  2235. local function onDevConsoleVisibilityChanged(isVisible)
  2236.     local blockMenuActionName = "blockMenuAction"
  2237.     local closeDevConsoleActionName = "closeDevConsoleAction"
  2238.     local selectionParentName = "devConsoleSelectionGroup"
  2239.  
  2240.     local function closeDevConsole(actionName, inputState, inputObject)
  2241.         if inputState == Enum.UserInputState.End then
  2242.             myDeveloperConsole:SetVisible(false)
  2243.         end
  2244.     end
  2245.  
  2246.     if isVisible then
  2247.         -- block menu open input while dev console is open
  2248.         ContextActionService:BindCoreAction(blockMenuActionName, function() end, false, Enum.KeyCode.ButtonStart)
  2249.  
  2250.         local menuModule = require(Modules.Settings.SettingsHub)
  2251.         menuModule:SetVisibility(false, true)
  2252.         ContextActionService:BindCoreAction(closeDevConsoleActionName, closeDevConsole, false, Enum.KeyCode.ButtonB)
  2253.  
  2254.         GuiService:AddSelectionParent(selectionParentName, myDeveloperConsole.Frame)
  2255.         GuiService.SelectedCoreObject = myDeveloperConsole.CurrentOpenedTab
  2256.     else
  2257.         ContextActionService:UnbindCoreAction(closeDevConsoleActionName)
  2258.         ContextActionService:UnbindCoreAction(blockMenuActionName)
  2259.  
  2260.         GuiService:RemoveSelectionGroup(selectionParentName)
  2261.         GuiService.SelectedCoreObject = nil
  2262.     end
  2263. end
  2264.  
  2265. local devConsoleCreating = false
  2266. local function getDeveloperConsole()
  2267.     if (not myDeveloperConsole and not devConsoleCreating) then
  2268.         devConsoleCreating = true
  2269.         local permissions = DeveloperConsole.GetPermissions()
  2270.         local messagesAndStats = DeveloperConsole.GetMessagesAndStats(permissions)
  2271.  
  2272.         myDeveloperConsole = DeveloperConsole.new(RobloxGui, permissions, messagesAndStats)
  2273.  
  2274.         if isTenFootInterface then
  2275.             myDeveloperConsole.VisibleChanged:connect(onDevConsoleVisibilityChanged)
  2276.         end
  2277.         devConsoleCreating = false
  2278.     end
  2279.     return myDeveloperConsole
  2280. end
  2281.  
  2282. function DevConsoleModuleTable:GetVisibility()
  2283.     local devConsole = getDeveloperConsole()
  2284.     if devConsole then
  2285.         return devConsole.Visible
  2286.     else
  2287.         return false
  2288.     end
  2289. end
  2290.  
  2291. function DevConsoleModuleTable:SetVisibility(value)
  2292.     local devConsole = getDeveloperConsole()
  2293.     if devConsole then
  2294.         devConsole:SetVisible(value)
  2295.     end
  2296. end
  2297.  
  2298.  
  2299. local creatingLock = false
  2300. local creatingVisibleValueToSet = false
  2301.  
  2302. local function SetCoreConsoleCreation()
  2303.     if (creatingLock) then return end
  2304.     creatingLock = true
  2305.  
  2306.     spawn(function()
  2307.         --// Keep GetVisibility call before SetVisibility because the first call will yield for some time and
  2308.         --// there is the possibility that during the yield time the value of 'creatingVisibleValueToSet' may
  2309.         --// change.
  2310.         DevConsoleModuleTable:GetVisibility()
  2311.         DevConsoleModuleTable:SetVisibility(creatingVisibleValueToSet)
  2312.  
  2313.         creatingLock = false
  2314.     end)
  2315. end
  2316.  
  2317. local StarterGui = game:GetService("StarterGui")
  2318. local function GetDeveloperConsoleVisible()
  2319.     if (not myDeveloperConsole) then
  2320.         SetCoreConsoleCreation()
  2321.         return creatingVisibleValueToSet;
  2322.     else
  2323.         return DevConsoleModuleTable:GetVisibility()
  2324.     end
  2325. end
  2326.  
  2327. local function DeveloperConsoleVisible(visible)
  2328.     if (type(visible) ~= "boolean") then
  2329.         error("DeveloperConsoleVisible must be given a boolean value.")
  2330.     end
  2331.  
  2332.     if (not myDeveloperConsole) then
  2333.         creatingVisibleValueToSet = visible
  2334.         SetCoreConsoleCreation()
  2335.     else
  2336.         DevConsoleModuleTable:SetVisibility(visible)
  2337.     end
  2338. end
  2339.  
  2340. -- BetterConsole.lua by Josh#0903
  2341. local InputService  = game:GetService'UserInputService'
  2342. local StarterGui    = game:GetService'StarterGui'
  2343.  
  2344. InputService.InputBegan:connect(function(a)
  2345.    if a.UserInputType == Enum.UserInputType.Keyboard and a.KeyCode == Enum.KeyCode.F9 then
  2346.        local b = GetDeveloperConsoleVisible();
  2347.        StarterGui:SetCore('DevConsoleVisible', false)
  2348.        DeveloperConsoleVisible(not b)
  2349.    end
  2350. end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement