0% found this document useful (0 votes)
108 views16 pages

Friday

This document summarizes code for an autoplayer for the game Funky Friday on Roblox. It loads external scripts, checks for required functions, initializes dependencies, and contains the main autoplay logic that presses keys based on a chance system in each frame while the song is playing.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
108 views16 pages

Friday

This document summarizes code for an autoplayer for the game Funky Friday on Roblox. It loads external scripts, checks for required functions, initializes dependencies, and contains the main autoplay logic that presses keys based on a chance system in each frame while the song is playing.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 16

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://raw.githubusercontent.com/wally-rblx/LinoriaLib/main/
Library.lua")
local themeManager =
urlLoad("https://raw.githubusercontent.com/wally-rblx/LinoriaLib/main/addons/
ThemeManager.lua")

local metadata = urlLoad("https://raw.githubusercontent.com/wally-rblx/funky-


friday-autoplay/main/metadata.lua")
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('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)

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy