Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local start = tick()
- local client = game:GetService('Players').LocalPlayer;
- local set_identity = (type(syn) == 'table' and syn.set_thread_identity) or setidentity or setthreadcontext
- local executor = identifyexecutor and identifyexecutor() or 'Unknown'
- local function fail(r) return client:Kick(r) end
- -- gracefully handle errors when loading external scripts
- -- added a cache to make hot reloading a bit faster
- local usedCache = shared.__urlcache and next(shared.__urlcache) ~= nil
- shared.__urlcache = shared.__urlcache or {}
- local function urlLoad(url)
- local success, result
- if shared.__urlcache[url] then
- success, result = true, shared.__urlcache[url]
- else
- success, result = pcall(game.HttpGet, game, url)
- end
- if (not success) then
- return fail(string.format('Failed to GET url %q for reason: %q', url, tostring(result)))
- end
- local fn, err = loadstring(result)
- if (type(fn) ~= 'function') then
- return fail(string.format('Failed to loadstring url %q for reason: %q', url, tostring(err)))
- end
- local results = { pcall(fn) }
- if (not results[1]) then
- return fail(string.format('Failed to initialize url %q for reason: %q', url, tostring(results[2])))
- end
- shared.__urlcache[url] = result
- return unpack(results, 2)
- end
- -- attempt to block imcompatible exploits
- -- rewrote because old checks literally did not work
- if type(set_identity) ~= 'function' then return fail('Unsupported exploit (missing "set_thread_identity")') end
- if type(getconnections) ~= 'function' then return fail('Unsupported exploit (missing "getconnections")') end
- if type(getloadedmodules) ~= 'function' then return fail('Unsupported exploit (misssing "getloadedmodules")') end
- if type(getgc) ~= 'function' then return fail('Unsupported exploit (misssing "getgc")') end
- local getinfo = debug.getinfo or getinfo;
- local getupvalue = debug.getupvalue or getupvalue;
- local getupvalues = debug.getupvalues or getupvalues;
- local setupvalue = debug.setupvalue or setupvalue;
- if type(setupvalue) ~= 'function' then return fail('Unsupported exploit (misssing "debug.setupvalue")') end
- if type(getupvalue) ~= 'function' then return fail('Unsupported exploit (misssing "debug.getupvalue")') end
- if type(getupvalues) ~= 'function' then return fail('Unsupported exploit (missing "debug.getupvalues")') end
- -- free exploit bandaid fix
- if type(getinfo) ~= 'function' then
- local debug_info = debug.info;
- if type(debug_info) ~= 'function' then
- -- if your exploit doesnt have getrenv you have no hope
- if type(getrenv) ~= 'function' then return fail('Unsupported exploit (missing "getrenv")') end
- debug_info = getrenv().debug.info
- end
- getinfo = function(f)
- assert(type(f) == 'function', string.format('Invalid argument #1 to debug.getinfo (expected %s got %s', 'function', type(f)))
- local results = { debug.info(f, 'slnfa') }
- local _, upvalues = pcall(getupvalues, f)
- if type(upvalues) ~= 'table' then
- upvalues = {}
- end
- local nups = 0
- for k in next, upvalues do
- nups = nups + 1
- end
- -- winning code
- return {
- source = '@' .. results[1],
- short_src = results[1],
- what = results[1] == '[C]' and 'C' or 'Lua',
- currentline = results[2],
- name = results[3],
- func = results[4],
- numparams = results[5],
- is_vararg = results[6], -- 'a' argument returns 2 values :)
- nups = nups,
- }
- end
- end
- local UI = urlLoad("https://pastebin.com/raw/CbwPnQAm")
- local themeManager = urlLoad("https://pastebin.com/raw/htXNsWyz")
- local metadata = urlLoad("https://pastebin.com/raw/Nh7NkHZk")
- local httpService = game:GetService('HttpService')
- local framework, scrollHandler, network
- local counter = 0
- while true do
- for _, obj in next, getgc(true) do
- if type(obj) == 'table' then
- if rawget(obj, 'GameUI') then
- framework = obj;
- elseif type(rawget(obj, 'Server')) == 'table' then
- network = obj;
- end
- end
- if network and framework then break end
- end
- for _, module in next, getloadedmodules() do
- if module.Name == 'ScrollHandler' then
- scrollHandler = module;
- break;
- end
- end
- if (type(framework) == 'table' and typeof(scrollHandler) == 'Instance' and type(network) == 'table') then
- break
- end
- counter = counter + 1
- if counter > 6 then
- fail(string.format('Failed to load game dependencies. Details: %s, %s, %s', type(framework), typeof(scrollHandler), type(network)))
- end
- wait(1)
- end
- local runService = game:GetService('RunService')
- local userInputService = game:GetService('UserInputService')
- local virtualInputManager = game:GetService('VirtualInputManager')
- local random = Random.new()
- local task = task or getrenv().task;
- local fastWait, fastSpawn = task.wait, task.spawn;
- -- firesignal implementation
- -- hitchance rolling
- local fireSignal, rollChance do
- -- updated for script-ware or whatever
- -- attempted to update for krnl
- function fireSignal(target, signal, ...)
- -- getconnections with InputBegan / InputEnded does not work without setting Synapse to the game's context level
- set_identity(2)
- local didFire = false
- for _, signal in next, getconnections(signal) do
- if type(signal.Function) == 'function' and islclosure(signal.Function) then
- local scr = rawget(getfenv(signal.Function), 'script')
- if scr == target then
- didFire = true
- pcall(signal.Function, ...)
- end
- end
- end
- -- if not didFire then fail"couldnt fire input signal" end
- set_identity(7)
- end
- -- uses a weighted random system
- -- its a bit scuffed rn but it works good enough
- function rollChance()
- -- if (//library.flags.autoPlayerMode == 'Manual') then
- if Options.AutoplayerMode.Value == 'Manual' then
- if (Options.SickBind:GetState()) then return 'Sick' end
- if (Options.GoodBind:GetState()) then return 'Good' end
- if (Options.OkayBind:GetState()) then return 'Ok' end
- if (Options.BadBind:GetState()) then return 'Bad' end
- return 'Bad' -- incase if it cant find one
- end
- local chances = {
- { 'Sick', Options.SickChance.Value },
- { 'Good', Options.GoodChance.Value },
- { 'Ok', Options.OkChance.Value },
- { 'Bad', Options.BadChance.Value },
- { 'Miss' , Options.MissChance.Value },
- }
- table.sort(chances, function(a, b)
- return a[2] > b[2]
- end)
- local sum = 0;
- for i = 1, #chances do
- sum += chances[i][2]
- end
- if sum == 0 then
- return chances[random:NextInteger(1, #chances)][1]
- end
- local initialWeight = random:NextInteger(0, sum)
- local weight = 0;
- for i = 1, #chances do
- weight = weight + chances[i][2]
- if weight > initialWeight then
- return chances[i][1]
- end
- end
- return 'Sick'
- end
- end
- -- autoplayer
- local chanceValues do
- chanceValues = {
- Sick = 96,
- Good = 92,
- Ok = 87,
- Bad = 75,
- }
- local keyCodeMap = {}
- for _, enum in next, Enum.KeyCode:GetEnumItems() do
- keyCodeMap[enum.Value] = enum
- end
- if shared._unload then
- pcall(shared._unload)
- end
- function shared._unload()
- if shared._id then
- pcall(runService.UnbindFromRenderStep, runService, shared._id)
- end
- UI:Unload()
- for i = 1, #shared.threads do
- coroutine.close(shared.threads[i])
- end
- for i = 1, #shared.callbacks do
- task.spawn(shared.callbacks[i])
- end
- end
- shared.threads = {}
- shared.callbacks = {}
- shared._id = httpService:GenerateGUID(false)
- local function pressKey(keyCode, state)
- if Options.PressMode.Value == 'virtual input' then
- virtualInputManager:SendKeyEvent(state, keyCode, false, nil)
- else
- fireSignal(scrollHandler, userInputService[state and 'InputBegan' or 'InputEnded'], { KeyCode = keyCode, UserInputType = Enum.UserInputType.Keyboard }, false)
- end
- end
- local rng = Random.new()
- runService:BindToRenderStep(shared._id, 1, function()
- --if (not library.flags.autoPlayer) then return end
- if (not Toggles.Autoplayer) or (not Toggles.Autoplayer.Value) then
- return
- end
- local currentlyPlaying = framework.SongPlayer.CurrentlyPlaying
- if typeof(currentlyPlaying) ~= 'Instance' or not currentlyPlaying:IsA('Sound') then
- return
- end
- local arrows = framework.UI:GetNotes()
- local count = framework.SongPlayer:GetKeyCount()
- local mode = count .. 'Key'
- local arrowData = framework.ArrowData[mode].Arrows
- for i, arrow in next, arrows do
- -- todo: switch to this (https://i.imgur.com/pEVe6Tx.png)
- local ignoredNoteTypes = { Death = true, Mechanic = true, Poison = true }
- if type(arrow.NoteDataConfigs) == 'table' then
- if ignoredNoteTypes[arrow.NoteDataConfigs.Type] then
- continue
- end
- end
- if (arrow.Side == framework.UI.CurrentSide) and (not arrow.Marked) and currentlyPlaying.TimePosition > 0 then
- local position = (arrow.Data.Position % count) .. ''
- local hitboxOffset = 0
- do
- local settings = framework.Settings;
- local offset = type(settings) == 'table' and settings.HitboxOffset;
- local value = type(offset) == 'table' and offset.Value;
- if type(value) == 'number' then
- hitboxOffset = value;
- end
- hitboxOffset = hitboxOffset / 1000
- end
- local songTime = framework.SongPlayer.CurrentTime
- do
- local configs = framework.SongPlayer.CurrentSongConfigs
- local playbackSpeed = type(configs) == 'table' and configs.PlaybackSpeed
- if type(playbackSpeed) ~= 'number' then
- playbackSpeed = 1
- end
- songTime = songTime / playbackSpeed
- end
- local noteTime = math.clamp((1 - math.abs(arrow.Data.Time - (songTime + hitboxOffset))) * 100, 0, 100)
- local result = rollChance()
- arrow._hitChance = arrow._hitChance or result;
- local hitChance = (Options.AutoplayerMode.Value == 'Manual' and result or arrow._hitChance)
- if hitChance ~= "Miss" and noteTime >= chanceValues[arrow._hitChance] then
- fastSpawn(function()
- arrow.Marked = true;
- local keyCode = keyCodeMap[arrowData[position].Keybinds.Keyboard[1]]
- pressKey(keyCode, true)
- local arrowLength = arrow.Data.Length or 0
- local isHeld = arrowLength > 0
- local delayMode = Options.DelayMode.Value
- local minDelay = isHeld and Options.HeldDelayMin or Options.NoteDelayMin;
- local maxDelay = isHeld and Options.HeldDelayMax or Options.NoteDelayMax;
- local noteDelay = isHeld and Options.HeldDelay or Options.ReleaseDelay
- local delay = delayMode == 'Random' and rng:NextNumber(minDelay.Value, maxDelay.Value) or noteDelay.Value
- task.wait(arrowLength + (delay / 1000))
- pressKey(keyCode, false)
- arrow.Marked = nil;
- end)
- end
- end
- end
- end)
- end
- local ActivateUnlockables do
- -- Note: I know you can do this with UserId but it only works if you run it before opening the notes menu
- -- My script should work no matter the order of which you run things :)
- local loadStyle = nil
- local function loadStyleProxy(...)
- -- This forces the styles to reload every time
- local upvalues = getupvalues(loadStyle)
- for i, upvalue in next, upvalues do
- if type(upvalue) == 'table' and rawget(upvalue, 'Style') then
- rawset(upvalue, 'Style', nil);
- setupvalue(loadStyle, i, upvalue)
- end
- end
- return loadStyle(...)
- end
- local function applyLoadStyleProxy(...)
- local gc = getgc()
- for i = 1, #gc do
- local obj = gc[i]
- if type(obj) == 'function' then
- -- goodbye nups numeric loop because script-ware is weird
- local upvalues = getupvalues(obj)
- for i, upv in next, upvalues do
- if type(upv) == 'function' and getinfo(upv).name == 'LoadStyle' then
- -- ugly but it works, we don't know every name for is_synapse_function and similar
- local function isGameFunction(fn)
- return getinfo(fn).source:match('%.ArrowSelector%.Customize$')
- end
- if isGameFunction(obj) and isGameFunction(upv) then
- -- avoid non-game functions :)
- loadStyle = loadStyle or upv
- setupvalue(obj, i, loadStyleProxy)
- table.insert(shared.callbacks, function()
- assert(pcall(setupvalue, obj, i, loadStyle))
- end)
- end
- end
- end
- end
- end
- end
- local success, error = pcall(applyLoadStyleProxy)
- if not success then
- return fail(string.format('Failed to hook LoadStyle function. Error(%q)\nExecutor(%q)\n', error, executor))
- end
- function ActivateUnlockables()
- local idx = table.find(framework.SongsWhitelist, client.UserId)
- if idx then return end
- UI:Notify('Developer arrows have been unlocked!', 3)
- table.insert(framework.SongsWhitelist, client.UserId)
- end
- end
- -- UpdateScore hook
- do
- while type(roundManager) ~= 'table' do
- task.wait()
- roundManager = network.Server.RoundManager
- end
- local oldUpdateScore = roundManager.UpdateScore;
- function roundManager.UpdateScore(...)
- local args = { ... }
- local score = args[2]
- if type(score) == 'number' and Options.ScoreModifier then
- -- table.foreach(args, warn)
- if Options.ScoreModifier.Value == 'No decrease on miss' then
- args[2] = 0
- elseif Options.ScoreModifier.Value == 'Increase score on miss' then
- args[2] = math.abs(score)
- end
- end
- return oldUpdateScore(unpack(args))
- end
- table.insert(shared.callbacks, function()
- roundManager.UpdateScore = oldUpdateScore
- end)
- end
- -- Auto ring collector
- do
- local thread = task.spawn(function()
- local map = workspace:waitForChild('Map', 5)
- local buildings = map and map:waitForChild('FunctionalBuildings', 5)
- local spawners = buildings and buildings:waitForChild('RingSpawners', 5)
- if spawners == nil then return end
- if type(firetouchinterest) ~= 'function' then return end
- while true do
- task.wait()
- if Toggles.AutoClaimRings and Toggles.AutoClaimRings.Value then
- local character = client.Character
- local rootPart = character and character:findFirstChild('HumanoidRootPart')
- if rootPart == nil then continue end
- for i, spawner in next, spawners:GetChildren() do
- for _, ring in next, spawner:GetChildren() do
- if ring.Name ~= 'GoldenRing' then continue end
- local ring = ring:findFirstChild('ring')
- if not (ring and ring:IsA('BasePart')) then continue end
- if ring.Transparency == 1 then continue end
- firetouchinterest(ring, rootPart, 0)
- firetouchinterest(ring, rootPart, 1)
- end
- end
- end
- end
- end)
- table.insert(shared.callbacks, function()
- pcall(task.cancel, thread)
- end)
- end
- local SaveManager = {} do
- SaveManager.Ignore = {}
- SaveManager.Parser = {
- Toggle = {
- Save = function(idx, object)
- return { type = 'Toggle', idx = idx, value = object.Value }
- end,
- Load = function(idx, data)
- if Toggles[idx] then
- Toggles[idx]:SetValue(data.value)
- end
- end,
- },
- Slider = {
- Save = function(idx, object)
- return { type = 'Slider', idx = idx, value = tostring(object.Value) }
- end,
- Load = function(idx, data)
- if Options[idx] then
- Options[idx]:SetValue(data.value)
- end
- end,
- },
- Dropdown = {
- Save = function(idx, object)
- return { type = 'Dropdown', idx = idx, value = object.Value, mutli = object.Multi }
- end,
- Load = function(idx, data)
- if Options[idx] then
- Options[idx]:SetValue(data.value)
- end
- end,
- },
- ColorPicker = {
- Save = function(idx, object)
- return { type = 'ColorPicker', idx = idx, value = object.Value:ToHex() }
- end,
- Load = function(idx, data)
- if Options[idx] then
- Options[idx]:SetValueRGB(Color3.fromHex(data.value))
- end
- end,
- },
- KeyPicker = {
- Save = function(idx, object)
- return { type = 'KeyPicker', idx = idx, mode = object.Mode, key = object.Value }
- end,
- Load = function(idx, data)
- if Options[idx] then
- Options[idx]:SetValue({ data.key, data.mode })
- end
- end,
- }
- }
- function SaveManager:Save(name)
- local fullPath = 'funky_friday_autoplayer/configs/' .. name .. '.json'
- local data = {
- version = 2,
- objects = {}
- }
- for idx, toggle in next, Toggles do
- if self.Ignore[idx] then continue end
- table.insert(data.objects, self.Parser[toggle.Type].Save(idx, toggle))
- end
- for idx, option in next, Options do
- if not self.Parser[option.Type] then continue end
- if self.Ignore[idx] then continue end
- table.insert(data.objects, self.Parser[option.Type].Save(idx, option))
- end
- local success, encoded = pcall(httpService.JSONEncode, httpService, data)
- if not success then
- return false, 'failed to encode data'
- end
- writefile(fullPath, encoded)
- return true
- end
- function SaveManager:Load(name)
- local file = 'funky_friday_autoplayer/configs/' .. name .. '.json'
- if not isfile(file) then return false, 'invalid file' end
- local success, decoded = pcall(httpService.JSONDecode, httpService, readfile(file))
- if not success then return false, 'decode error' end
- if decoded.version ~= 2 then return false, 'invalid version' end
- for _, option in next, decoded.objects do
- if self.Parser[option.type] then
- self.Parser[option.type].Load(option.idx, option)
- end
- end
- return true
- end
- function SaveManager.Refresh()
- local list = listfiles('funky_friday_autoplayer/configs')
- local out = {}
- for i = 1, #list do
- local file = list[i]
- if file:sub(-5) == '.json' then
- -- i hate this but it has to be done ...
- local pos = file:find('.json', 1, true)
- local start = pos
- local char = file:sub(pos, pos)
- while char ~= '/' and char ~= '\\' and char ~= '' do
- pos = pos - 1
- char = file:sub(pos, pos)
- end
- if char == '/' or char == '\\' then
- table.insert(out, file:sub(pos + 1, start - 1))
- end
- end
- end
- Options.ConfigList.Values = out;
- Options.ConfigList:SetValues()
- Options.ConfigList:Display()
- return out
- end
- function SaveManager:Delete(name)
- local file = 'funky_friday_autoplayer/configs/' .. name .. '.json'
- if not isfile(file) then return false, string.format('Config %q does not exist', name) end
- local succ, err = pcall(delfile, file)
- if not succ then
- return false, string.format('error occured during file deletion: %s', err)
- end
- return true
- end
- function SaveManager:SetIgnoreIndexes(list)
- for i = 1, #list do
- table.insert(self.Ignore, list[i])
- end
- end
- function SaveManager.Check()
- local list = listfiles('funky_friday_autoplayer/configs')
- for _, file in next, list do
- if isfolder(file) then continue end
- local data = readfile(file)
- local success, decoded = pcall(httpService.JSONDecode, httpService, data)
- if success and type(decoded) == 'table' and decoded.version ~= 2 then
- pcall(delfile, file)
- end
- end
- end
- end
- local Window = UI:CreateWindow({
- Title = string.format('ShadyHub | 🎄 XMAS Event | Funky Friday AutoPlayer - version %s | updated: %s', metadata.version, metadata.updated),
- AutoShow = true,
- Center = true,
- Size = UDim2.fromOffset(550, 627),
- })
- local Tabs = {}
- local Groups = {}
- Tabs.Main = Window:AddTab('Main')
- Tabs.Miscellaneous = Window:AddTab('Miscellaneous')
- Groups.Autoplayer = Tabs.Main:AddLeftGroupbox('Autoplayer')
- Groups.Autoplayer:AddToggle('Autoplayer', { Text = 'Autoplayer' }):AddKeyPicker('AutoplayerBind', { Default = 'End', NoUI = true, SyncToggleState = true })
- Groups.Autoplayer:AddDropdown('PressMode', {
- Text = 'Input mode',
- Compact = true,
- Default = 'firesignal',
- Values = { 'firesignal', 'virtual input' },
- Tooltip = 'Input method used to press arrows.\n* firesignal: calls input functions directly.\n* virtual input: emulates key presses. use if "firesignal" does not work.',
- })
- Groups.HitChances = Tabs.Main:AddLeftGroupbox('Hit chances')
- Groups.HitChances:AddDropdown('AutoplayerMode', {
- Text = 'Autoplayer mode',
- Compact = true,
- Default = 1,
- Values = { 'Automatic', 'Manual' },
- Tooltip = 'Mode to use for deciding when to hit notes.\n* Automatic: hits notes based on chance sliders\n* Manual: hits notes based on held keybinds',
- })
- Groups.HitChances:AddSlider('SickChance', { Text = 'Sick chance', Min = 0, Max = 100, Default = 100, Suffix = '%', Rounding = 0, Compact = true })
- Groups.HitChances:AddSlider('GoodChance', { Text = 'Good chance', Min = 0, Max = 100, Default = 0, Suffix = '%', Rounding = 0, Compact = true })
- Groups.HitChances:AddSlider('OkChance', { Text = 'Ok chance', Min = 0, Max = 100, Default = 0, Suffix = '%', Rounding = 0, Compact = true })
- Groups.HitChances:AddSlider('BadChance', { Text = 'Bad chance', Min = 0, Max = 100, Default = 0, Suffix = '%', Rounding = 0, Compact = true })
- Groups.HitChances:AddSlider('MissChance', { Text = 'Miss chance', Min = 0, Max = 100, Default = 0, Suffix = '%', Rounding = 0, Compact = true })
- Groups.HitTiming = Tabs.Main:AddLeftGroupbox('Hit timing')
- Groups.HitTiming:AddDropdown('DelayMode', {
- Text = 'Delay mode',
- Default = 1,
- Values = { 'Manual', 'Random' },
- Tooltip = 'Adjustable timing for when to release notes.\n* Manual releases the note after a fixed amount of time.\n* Random releases the note after a random amount of time.',
- })
- Groups.HitTiming:AddLabel('Manual delay')
- Groups.HitTiming:AddSlider('ReleaseDelay', { Text = 'Note delay', Min = 0, Max = 500, Default = 20, Rounding = 0, Compact = true, Suffix = 'ms' })
- Groups.HitTiming:AddSlider('HeldDelay', { Text = 'Held note delay', Min = -20, Max = 100, Default = 0, Rounding = 0, Compact = true, Suffix = 'ms' })
- Groups.HitTiming:AddLabel('Random delay')
- Groups.HitTiming:AddSlider('NoteDelayMin', { Text = 'Min note delay', Min = 0, Max = 100, Default = 0, Rounding = 0, Compact = true, Suffix = 'ms' })
- Groups.HitTiming:AddSlider('NoteDelayMax', { Text = 'Max note delay', Min = 0, Max = 500, Default = 20, Rounding = 0, Compact = true, Suffix = 'ms' })
- Groups.HitTiming:AddSlider('HeldDelayMin', { Text = 'Min held note delay', Min = 0, Max = 100, Default = 0, Rounding = 0, Compact = true, Suffix = 'ms' })
- Groups.HitTiming:AddSlider('HeldDelayMax', { Text = 'Max held note delay', Min = 0, Max = 500, Default = 20, Rounding = 0, Compact = true, Suffix = 'ms' })
- Groups.Misc = Tabs.Main:AddRightGroupbox('Misc')
- Groups.Misc:AddButton('Unlock developer notes', ActivateUnlockables)
- Groups.Misc:AddToggle('AutoClaimRings', { Text = 'Auto claim rings' })
- Groups.Keybinds = Tabs.Main:AddRightGroupbox('Keybinds')
- Groups.Keybinds:AddLabel('Sick'):AddKeyPicker('SickBind', { Default = 'One', NoUI = true })
- Groups.Keybinds:AddLabel('Good'):AddKeyPicker('GoodBind', { Default = 'Two', NoUI = true })
- Groups.Keybinds:AddLabel('Ok'):AddKeyPicker('OkayBind', { Default = 'Three', NoUI = true })
- Groups.Keybinds:AddLabel('Bad'):AddKeyPicker('BadBind', { Default = 'Four', NoUI = true })
- Groups.Configs = Tabs.Miscellaneous:AddRightGroupbox('Configs')
- Groups.Credits = Tabs.Miscellaneous:AddRightGroupbox('Credits')
- Groups.Credits:AddLabel('<font color="#3da5ff">wally</font> - script')
- Groups.Credits:AddLabel('<font color="#de6cff">Sezei</font> - contributor')
- Groups.Credits:AddLabel('Inori - ui library')
- Groups.Credits:AddLabel('Jan - old ui library')
- Groups.Misc = Tabs.Miscellaneous:AddRightGroupbox('Miscellaneous')
- Groups.Misc:AddLabel(metadata.message or 'no message found!', true)
- Groups.Misc:AddDivider()
- Groups.Misc:AddButton('Unload script', function() pcall(shared._unload) end)
- Groups.Misc:AddButton('Copy discord', function()
- if pcall(setclipboard, "https://wally.cool/discord") then
- UI:Notify('Successfully copied discord link to your clipboard!', 5)
- end
- end)
- Groups.Misc:AddLabel('Menu toggle'):AddKeyPicker('MenuToggle', { Default = 'Delete', NoUI = true })
- UI.ToggleKeybind = Options.MenuToggle
- if type(readfile) == 'function' and type(writefile) == 'function' and type(makefolder) == 'function' and type(isfolder) == 'function' then
- makefolder('funky_friday_autoplayer')
- makefolder('funky_friday_autoplayer\\configs')
- Groups.Configs:AddDropdown('ConfigList', { Text = 'Config list', Values = {} })
- Groups.Configs:AddInput('ConfigName', { Text = 'Config name' })
- Groups.Configs:AddDivider()
- Groups.Configs:AddButton('Save config', function()
- local name = Options.ConfigName.Value;
- if name:gsub(' ', '') == '' then
- return UI:Notify('Invalid config name.', 3)
- end
- local success, err = SaveManager:Save(name)
- if not success then
- return UI:Notify(tostring(err), 5)
- end
- UI:Notify(string.format('Saved config %q', name), 5)
- task.defer(SaveManager.Refresh)
- end)
- Groups.Configs:AddButton('Load', function()
- local name = Options.ConfigList.Value
- local success, err = SaveManager:Load(name)
- if not success then
- return UI:Notify(tostring(err), 5)
- end
- UI:Notify(string.format('Loaded config %q', name), 5)
- end):AddButton('Delete', function()
- local name = Options.ConfigList.Value
- if name:gsub(' ', '') == '' then
- return UI:Notify('Invalid config name.', 3)
- end
- local success, err = SaveManager:Delete(name)
- if not success then
- return UI:Notify(tostring(err), 5)
- end
- UI:Notify(string.format('Deleted config %q', name), 5)
- task.spawn(Options.ConfigList.SetValue, Options.ConfigList, nil)
- task.defer(SaveManager.Refresh)
- end)
- Groups.Configs:AddButton('Refresh list', SaveManager.Refresh)
- task.defer(SaveManager.Refresh)
- task.defer(SaveManager.Check)
- else
- Groups.Configs:AddLabel('Your exploit is missing file functions so you are unable to use configs.', true)
- --UI:Notify('Failed to create configs tab due to your exploit missing certain file functions.', 2)
- end
- -- Themes
- do
- local latestThemeIndex = 0
- for i, theme in next, themeManager.BuiltInThemes do
- if theme[1] > latestThemeIndex then
- latestThemeIndex = theme[1]
- end
- end
- latestThemeIndex = latestThemeIndex + 1
- local linoriaTheme = themeManager.BuiltInThemes.Default[2]
- local funkyFridayTheme = table.clone(themeManager.BuiltInThemes.Default[2])
- funkyFridayTheme.AccentColor = Color3.fromRGB(255, 65, 65):ToHex()
- themeManager.BuiltInThemes['Linoria'] = { latestThemeIndex, linoriaTheme }
- themeManager.BuiltInThemes['Default'] = { 1, funkyFridayTheme }
- themeManager:SetLibrary(UI)
- themeManager:SetFolder('funky_friday_autoplayer')
- themeManager:ApplyToGroupbox(Tabs.Miscellaneous:AddLeftGroupbox('Themes'))
- SaveManager:SetIgnoreIndexes({
- "BackgroundColor", "MainColor", "AccentColor", "OutlineColor", "FontColor", -- themes
- "ThemeManager_ThemeList", 'ThemeManager_CustomThemeList', 'ThemeManager_CustomThemeName', -- themes
- })
- end
- UI:Notify(string.format('Loaded script in %.4f second(s)!', tick() - start), 3)
Advertisement
Advertisement