Advertisement
coastsss

Old Roblox Developer Console

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