Advertisement
CoronaHUB

Untitled

Jun 4th, 2024
138
-1
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 31.83 KB | None | 0 1
  1. print("Credits to LuisMODS for the ported UI lib of Linoria")
  2. print("Credits to CoronaHUB-RBLX and his devs for making this amazing script")
  3. print("I DO NOT OWN ANY OF THIS CODE. CREDITS TO THIS CODE AND UI LIBS ETC GO TO THEIR OWNERS.")
  4. print(" ")
  5. print("Wally RBLX's Funky Friday AutoPlay script mobile port. (READ LINES ABOVE!)")
  6.  
  7. local start = tick()
  8. local client = game:GetService('Players').LocalPlayer;
  9. local set_identity = (type(syn) == 'table' and syn.set_thread_identity) or setidentity or setthreadcontext
  10. local executor = identifyexecutor and identifyexecutor() or 'Unknown'
  11.  
  12. local function fail(r) return client:Kick(r) end
  13.  
  14. -- gracefully handle errors when loading external scripts
  15. -- added a cache to make hot reloading a bit faster
  16. local usedCache = shared.__urlcache and next(shared.__urlcache) ~= nil
  17.  
  18. shared.__urlcache = shared.__urlcache or {}
  19. local function urlLoad(url)
  20. local success, result
  21.  
  22. if shared.__urlcache[url] then
  23. success, result = true, shared.__urlcache[url]
  24. else
  25. success, result = pcall(game.HttpGet, game, url)
  26. end
  27.  
  28. if (not success) then
  29. return fail(string.format('Failed to GET url %q for reason: %q', url, tostring(result)))
  30. end
  31.  
  32. local fn, err = loadstring(result)
  33. if (type(fn) ~= 'function') then
  34. return fail(string.format('Failed to loadstring url %q for reason: %q', url, tostring(err)))
  35. end
  36.  
  37. local results = { pcall(fn) }
  38. if (not results[1]) then
  39. return fail(string.format('Failed to initialize url %q for reason: %q', url, tostring(results[2])))
  40. end
  41.  
  42. shared.__urlcache[url] = result
  43. return unpack(results, 2)
  44. end
  45.  
  46. -- attempt to block imcompatible exploits
  47. -- rewrote because old checks literally did not work
  48. if type(set_identity) ~= 'function' then return fail('Unsupported exploit (missing "set_thread_identity")') end
  49. if type(getconnections) ~= 'function' then return fail('Unsupported exploit (missing "getconnections")') end
  50. if type(getloadedmodules) ~= 'function' then return fail('Unsupported exploit (misssing "getloadedmodules")') end
  51. if type(getgc) ~= 'function' then return fail('Unsupported exploit (misssing "getgc")') end
  52.  
  53. local getinfo = debug.getinfo or getinfo;
  54. local getupvalue = debug.getupvalue or getupvalue;
  55. local getupvalues = debug.getupvalues or getupvalues;
  56. local setupvalue = debug.setupvalue or setupvalue;
  57.  
  58. if type(setupvalue) ~= 'function' then return fail('Unsupported exploit (misssing "debug.setupvalue")') end
  59. if type(getupvalue) ~= 'function' then return fail('Unsupported exploit (misssing "debug.getupvalue")') end
  60. if type(getupvalues) ~= 'function' then return fail('Unsupported exploit (missing "debug.getupvalues")') end
  61.  
  62. -- free exploit bandaid fix
  63. if type(getinfo) ~= 'function' then
  64. local debug_info = debug.info;
  65. if type(debug_info) ~= 'function' then
  66. -- if your exploit doesnt have getrenv you have no hope
  67. if type(getrenv) ~= 'function' then return fail('Unsupported exploit (missing "getrenv")') end
  68. debug_info = getrenv().debug.info
  69. end
  70. getinfo = function(f)
  71. assert(type(f) == 'function', string.format('Invalid argument #1 to debug.getinfo (expected %s got %s', 'function', type(f)))
  72. local results = { debug.info(f, 'slnfa') }
  73. local _, upvalues = pcall(getupvalues, f)
  74. if type(upvalues) ~= 'table' then
  75. upvalues = {}
  76. end
  77. local nups = 0
  78. for k in next, upvalues do
  79. nups = nups + 1
  80. end
  81. -- winning code
  82. return {
  83. source = '@' .. results[1],
  84. short_src = results[1],
  85. what = results[1] == '[C]' and 'C' or 'Lua',
  86. currentline = results[2],
  87. name = results[3],
  88. func = results[4],
  89. numparams = results[5],
  90. is_vararg = results[6], -- 'a' argument returns 2 values :)
  91. nups = nups,
  92. }
  93. end
  94. end
  95.  
  96. local IsMobile = false;
  97. local DevicePlatform = Enum.Platform.None;
  98. pcall(function() DevicePlatform = UserInputService:GetPlatform(); end);
  99. IsMobile = (DevicePlatform == Enum.Platform.Android or DevicePlatform == Enum.Platform.IOS);
  100. local UIRepo = 'https://raw.githubusercontent.com/Bart3kk/LinLib/main/'
  101. local ScriptRepo = 'https://raw.githubusercontent.com/Bart3kk/funky-friday-autoplay/main/'
  102.  
  103. local UI = nil
  104. if IsMobile then
  105. UI = loadstring(game:HttpGet(UIRepo .. 'TestMobileSupport.lua'))()
  106. else
  107. UI = loadstring(game:HttpGet(UIRepo .. 'Library.lua'))()
  108. end
  109.  
  110. local metadata = loadstring(game:HttpGet(ScriptRepo .. 'metadata.lua'))()
  111. local httpService = game:GetService('HttpService')
  112.  
  113. local framework, scrollHandler, network
  114. local counter = 0
  115.  
  116. while true do
  117. for _, obj in next, getgc(true) do
  118. if type(obj) == 'table' then
  119. if rawget(obj, 'GameUI') then
  120. framework = obj;
  121. elseif type(rawget(obj, 'Server')) == 'table' then
  122. network = obj;
  123. end
  124. end
  125.  
  126. if network and framework then break end
  127. end
  128.  
  129. for _, module in next, getloadedmodules() do
  130. if module.Name == 'ScrollHandler' then
  131. scrollHandler = module;
  132. break;
  133. end
  134. end
  135.  
  136. if (type(framework) == 'table' and typeof(scrollHandler) == 'Instance' and type(network) == 'table') then
  137. break
  138. end
  139.  
  140. counter = counter + 1
  141. if counter > 6 then
  142. fail(string.format('Failed to load game dependencies. Details: %s, %s, %s', type(framework), typeof(scrollHandler), type(network)))
  143. end
  144. wait(1)
  145. end
  146.  
  147. local runService = game:GetService('RunService')
  148. local userInputService = game:GetService('UserInputService')
  149. local virtualInputManager = game:GetService('VirtualInputManager')
  150.  
  151. local random = Random.new()
  152.  
  153. local task = task or getrenv().task;
  154. local fastWait, fastSpawn = task.wait, task.spawn;
  155.  
  156. -- firesignal implementation
  157. -- hitchance rolling
  158. local fireSignal, rollChance do
  159. -- updated for script-ware or whatever
  160. -- attempted to update for krnl
  161.  
  162. function fireSignal(target, signal, ...)
  163. -- getconnections with InputBegan / InputEnded does not work without setting Synapse to the game's context level
  164. set_identity(2)
  165. local didFire = false
  166. for _, signal in next, getconnections(signal) do
  167. if type(signal.Function) == 'function' and islclosure(signal.Function) then
  168. local scr = rawget(getfenv(signal.Function), 'script')
  169. if scr == target then
  170. didFire = true
  171. pcall(signal.Function, ...)
  172. end
  173. end
  174. end
  175. -- if not didFire then fail"couldnt fire input signal" end
  176. set_identity(7)
  177. end
  178.  
  179. -- uses a weighted random system
  180. -- its a bit scuffed rn but it works good enough
  181.  
  182. function rollChance()
  183. -- if (//library.flags.autoPlayerMode == 'Manual') then
  184. if Options.AutoplayerMode.Value == 'Manual' then
  185. if (Options.SickBind:GetState()) then return 'Sick' end
  186. if (Options.GoodBind:GetState()) then return 'Good' end
  187. if (Options.OkayBind:GetState()) then return 'Ok' end
  188. if (Options.BadBind:GetState()) then return 'Bad' end
  189.  
  190. return 'Bad' -- incase if it cant find one
  191. end
  192.  
  193. local chances = {
  194. { 'Sick', Options.SickChance.Value },
  195. { 'Good', Options.GoodChance.Value },
  196. { 'Ok', Options.OkChance.Value },
  197. { 'Bad', Options.BadChance.Value },
  198. { 'Miss' , Options.MissChance.Value },
  199. }
  200.  
  201. table.sort(chances, function(a, b)
  202. return a[2] > b[2]
  203. end)
  204.  
  205. local sum = 0;
  206. for i = 1, #chances do
  207. sum += chances[i][2]
  208. end
  209.  
  210. if sum == 0 then
  211. return chances[random:NextInteger(1, #chances)][1]
  212. end
  213.  
  214. local initialWeight = random:NextInteger(0, sum)
  215. local weight = 0;
  216.  
  217. for i = 1, #chances do
  218. weight = weight + chances[i][2]
  219.  
  220. if weight > initialWeight then
  221. return chances[i][1]
  222. end
  223. end
  224.  
  225. return 'Sick'
  226. end
  227. end
  228.  
  229. -- autoplayer
  230. local chanceValues do
  231. chanceValues = {
  232. Sick = 96,
  233. Good = 92,
  234. Ok = 87,
  235. Bad = 75,
  236. }
  237.  
  238. local keyCodeMap = {}
  239. for _, enum in next, Enum.KeyCode:GetEnumItems() do
  240. keyCodeMap[enum.Value] = enum
  241. end
  242.  
  243. if shared._unload then
  244. pcall(shared._unload)
  245. end
  246.  
  247. function shared._unload()
  248. if shared._id then
  249. pcall(runService.UnbindFromRenderStep, runService, shared._id)
  250. end
  251.  
  252. UI:Unload()
  253.  
  254. for i = 1, #shared.threads do
  255. coroutine.close(shared.threads[i])
  256. end
  257.  
  258. for i = 1, #shared.callbacks do
  259. task.spawn(shared.callbacks[i])
  260. end
  261. end
  262.  
  263. shared.threads = {}
  264. shared.callbacks = {}
  265.  
  266. shared._id = httpService:GenerateGUID(false)
  267.  
  268. local function pressKey(keyCode, state)
  269. if Options.PressMode.Value == 'virtual input' then
  270. virtualInputManager:SendKeyEvent(state, keyCode, false, nil)
  271. else
  272. fireSignal(scrollHandler, userInputService[state and 'InputBegan' or 'InputEnded'], { KeyCode = keyCode, UserInputType = Enum.UserInputType.Keyboard }, false)
  273. end
  274. end
  275.  
  276. local rng = Random.new()
  277. runService:BindToRenderStep(shared._id, 1, function()
  278. --if (not library.flags.autoPlayer) then return end
  279.  
  280. if (not Toggles.Autoplayer) or (not Toggles.Autoplayer.Value) then
  281. return
  282. end
  283.  
  284. local currentlyPlaying = framework.SongPlayer.CurrentlyPlaying
  285.  
  286. if typeof(currentlyPlaying) ~= 'Instance' or not currentlyPlaying:IsA('Sound') then
  287. return
  288. end
  289.  
  290. local arrows = framework.UI:GetNotes()
  291. local count = framework.SongPlayer:GetKeyCount()
  292. local mode = count .. 'Key'
  293.  
  294. local arrowData = framework.ArrowData[mode].Arrows
  295. for i, arrow in next, arrows do
  296. -- todo: switch to this (https://i.imgur.com/pEVe6Tx.png)
  297. local ignoredNoteTypes = { Death = true, Mechanic = true, Poison = true }
  298.  
  299. if type(arrow.NoteDataConfigs) == 'table' then
  300. if ignoredNoteTypes[arrow.NoteDataConfigs.Type] then
  301. continue
  302. end
  303. end
  304.  
  305. if (arrow.Side == framework.UI.CurrentSide) and (not arrow.Marked) and currentlyPlaying.TimePosition > 0 then
  306. local position = (arrow.Data.Position % count) .. ''
  307.  
  308. local hitboxOffset = 0
  309. do
  310. local settings = framework.Settings;
  311. local offset = type(settings) == 'table' and settings.HitboxOffset;
  312. local value = type(offset) == 'table' and offset.Value;
  313.  
  314. if type(value) == 'number' then
  315. hitboxOffset = value;
  316. end
  317.  
  318. hitboxOffset = hitboxOffset / 1000
  319. end
  320.  
  321. local songTime = framework.SongPlayer.CurrentTime
  322. local playbackSpeed = 1
  323. do
  324. local configs = framework.SongPlayer.CurrentSongConfigs
  325. if type(configs) == 'table' and type(rawget(configs, "PlaybackSpeed")) == "number" then
  326. playbackSpeed = configs.PlaybackSpeed
  327. end
  328.  
  329. songTime = songTime / playbackSpeed
  330. end
  331.  
  332. local noteTime = math.clamp((1 - math.abs(arrow.Data.Time - (songTime + hitboxOffset))) * 100, 0, 100)
  333.  
  334. local result = rollChance()
  335. arrow._hitChance = arrow._hitChance or result;
  336.  
  337. local hitChance = (Options.AutoplayerMode.Value == 'Manual' and result or arrow._hitChance)
  338. if hitChance ~= "Miss" and noteTime >= chanceValues[arrow._hitChance] then
  339. fastSpawn(function()
  340. arrow.Marked = true;
  341. local keyCode = keyCodeMap[arrowData[position].Keybinds.Keyboard[1]]
  342.  
  343. pressKey(keyCode, true)
  344.  
  345. local arrowLength = arrow.Data.Length or 0
  346. local isHeld = arrowLength > 0
  347.  
  348. if isHeld then arrowLength = arrowLength / playbackSpeed end
  349.  
  350. local delayMode = Options.DelayMode.Value
  351.  
  352. local minDelay = isHeld and Options.HeldDelayMin or Options.NoteDelayMin;
  353. local maxDelay = isHeld and Options.HeldDelayMax or Options.NoteDelayMax;
  354. local noteDelay = isHeld and Options.HeldDelay or Options.ReleaseDelay
  355.  
  356. local delay = delayMode == 'Random' and rng:NextNumber(minDelay.Value, maxDelay.Value) or noteDelay.Value
  357. task.wait(arrowLength + (delay / 1000))
  358.  
  359. pressKey(keyCode, false)
  360. arrow.Marked = nil;
  361. end)
  362. end
  363. end
  364. end
  365. end)
  366. end
  367.  
  368. local ActivateUnlockables do
  369. -- Note: I know you can do this with UserId but it only works if you run it before opening the notes menu
  370. -- My script should work no matter the order of which you run things :)
  371.  
  372. local loadStyle = nil
  373. local function loadStyleProxy(...)
  374. -- This forces the styles to reload every time
  375.  
  376. local upvalues = getupvalues(loadStyle)
  377. for i, upvalue in next, upvalues do
  378. if type(upvalue) == 'table' and rawget(upvalue, 'Style') then
  379. rawset(upvalue, 'Style', nil);
  380. setupvalue(loadStyle, i, upvalue)
  381. end
  382. end
  383.  
  384. return loadStyle(...)
  385. end
  386.  
  387. local function applyLoadStyleProxy(...)
  388. local gc = getgc()
  389. for i = 1, #gc do
  390. local obj = gc[i]
  391. if type(obj) == 'function' then
  392. -- goodbye nups numeric loop because script-ware is weird
  393. local upvalues = getupvalues(obj)
  394. for i, upv in next, upvalues do
  395. if type(upv) == 'function' and getinfo(upv).name == 'LoadStyle' then
  396. -- ugly but it works, we don't know every name for is_synapse_function and similar
  397. local function isGameFunction(fn)
  398. return getinfo(fn).source:match('%.ArrowSelector%.Customize$')
  399. end
  400.  
  401. if isGameFunction(obj) and isGameFunction(upv) then
  402. -- avoid non-game functions :)
  403. loadStyle = loadStyle or upv
  404. setupvalue(obj, i, loadStyleProxy)
  405.  
  406. table.insert(shared.callbacks, function()
  407. assert(pcall(setupvalue, obj, i, loadStyle))
  408. end)
  409. end
  410. end
  411. end
  412. end
  413. end
  414. end
  415.  
  416. local success, error = pcall(applyLoadStyleProxy)
  417. if not success then
  418. return fail(string.format('Failed to hook LoadStyle function. Error(%q)\nExecutor(%q)\n', error, executor))
  419. end
  420.  
  421. function ActivateUnlockables()
  422. local idx = table.find(framework.SongsWhitelist, client.UserId)
  423. if idx then return end
  424.  
  425. UI:Notify('Developer arrows have been unlocked!', 3)
  426. table.insert(framework.SongsWhitelist, client.UserId)
  427. end
  428. end
  429.  
  430. -- UpdateScore hook
  431. do
  432. while type(roundManager) ~= 'table' do
  433. task.wait()
  434. roundManager = network.Server.RoundManager
  435. end
  436.  
  437. local oldUpdateScore = roundManager.UpdateScore;
  438. function roundManager.UpdateScore(...)
  439. local args = { ... }
  440. local score = args[2]
  441.  
  442. if type(score) == 'number' and Options.ScoreModifier then
  443. -- table.foreach(args, warn)
  444. if Options.ScoreModifier.Value == 'No decrease on miss' then
  445. args[2] = 0
  446. elseif Options.ScoreModifier.Value == 'Increase score on miss' then
  447. args[2] = math.abs(score)
  448. end
  449. end
  450.  
  451. return oldUpdateScore(unpack(args))
  452. end
  453.  
  454. table.insert(shared.callbacks, function()
  455. roundManager.UpdateScore = oldUpdateScore
  456. end)
  457. end
  458.  
  459. -- Auto ring collector
  460. do
  461. local thread = task.spawn(function()
  462. local map = workspace:waitForChild('Map', 5)
  463. local buildings = map and map:waitForChild('FunctionalBuildings', 5)
  464. local spawners = buildings and buildings:waitForChild('RingSpawners', 5)
  465.  
  466. if spawners == nil then return end
  467. if type(firetouchinterest) ~= 'function' then return end
  468.  
  469. while true do
  470. task.wait()
  471. if Toggles.AutoClaimRings and Toggles.AutoClaimRings.Value then
  472. local character = client.Character
  473. local rootPart = character and character:findFirstChild('HumanoidRootPart')
  474.  
  475. if rootPart == nil then continue end
  476.  
  477. for i, spawner in next, spawners:GetChildren() do
  478. for _, ring in next, spawner:GetChildren() do
  479. if ring.Name ~= 'GoldenRing' then continue end
  480.  
  481. local ring = ring:findFirstChild('ring')
  482. if not (ring and ring:IsA('BasePart')) then continue end
  483. if ring.Transparency == 1 then continue end
  484.  
  485. firetouchinterest(ring, rootPart, 0)
  486. firetouchinterest(ring, rootPart, 1)
  487. end
  488. end
  489. end
  490. end
  491. end)
  492. table.insert(shared.callbacks, function()
  493. pcall(task.cancel, thread)
  494. end)
  495. end
  496.  
  497. local SaveManager = {} do
  498. SaveManager.Ignore = {}
  499. SaveManager.Parser = {
  500. Toggle = {
  501. Save = function(idx, object)
  502. return { type = 'Toggle', idx = idx, value = object.Value }
  503. end,
  504. Load = function(idx, data)
  505. if Toggles[idx] then
  506. Toggles[idx]:SetValue(data.value)
  507. end
  508. end,
  509. },
  510. Slider = {
  511. Save = function(idx, object)
  512. return { type = 'Slider', idx = idx, value = tostring(object.Value) }
  513. end,
  514. Load = function(idx, data)
  515. if Options[idx] then
  516. Options[idx]:SetValue(data.value)
  517. end
  518. end,
  519. },
  520. Dropdown = {
  521. Save = function(idx, object)
  522. return { type = 'Dropdown', idx = idx, value = object.Value, mutli = object.Multi }
  523. end,
  524. Load = function(idx, data)
  525. if Options[idx] then
  526. Options[idx]:SetValue(data.value)
  527. end
  528. end,
  529. },
  530. ColorPicker = {
  531. Save = function(idx, object)
  532. return { type = 'ColorPicker', idx = idx, value = object.Value:ToHex() }
  533. end,
  534. Load = function(idx, data)
  535. if Options[idx] then
  536. Options[idx]:SetValueRGB(Color3.fromHex(data.value))
  537. end
  538. end,
  539. },
  540. KeyPicker = {
  541. Save = function(idx, object)
  542. return { type = 'KeyPicker', idx = idx, mode = object.Mode, key = object.Value }
  543. end,
  544. Load = function(idx, data)
  545. if Options[idx] then
  546. Options[idx]:SetValue({ data.key, data.mode })
  547. end
  548. end,
  549. }
  550. }
  551.  
  552. function SaveManager:Save(name)
  553. local fullPath = 'funky_friday_autoplayer/configs/' .. name .. '.json'
  554.  
  555. local data = {
  556. version = 2,
  557. objects = {}
  558. }
  559.  
  560. for idx, toggle in next, Toggles do
  561. if self.Ignore[idx] then continue end
  562. table.insert(data.objects, self.Parser[toggle.Type].Save(idx, toggle))
  563. end
  564.  
  565. for idx, option in next, Options do
  566. if not self.Parser[option.Type] then continue end
  567. if self.Ignore[idx] then continue end
  568.  
  569. table.insert(data.objects, self.Parser[option.Type].Save(idx, option))
  570. end
  571.  
  572. local success, encoded = pcall(httpService.JSONEncode, httpService, data)
  573. if not success then
  574. return false, 'failed to encode data'
  575. end
  576.  
  577. writefile(fullPath, encoded)
  578. return true
  579. end
  580.  
  581. function SaveManager:Load(name)
  582. local file = 'funky_friday_autoplayer/configs/' .. name .. '.json'
  583. if not isfile(file) then return false, 'invalid file' end
  584.  
  585. local success, decoded = pcall(httpService.JSONDecode, httpService, readfile(file))
  586. if not success then return false, 'decode error' end
  587. if decoded.version ~= 2 then return false, 'invalid version' end
  588.  
  589. for _, option in next, decoded.objects do
  590. if self.Parser[option.type] then
  591. self.Parser[option.type].Load(option.idx, option)
  592. end
  593. end
  594.  
  595. return true
  596. end
  597.  
  598. function SaveManager.Refresh()
  599. local list = listfiles('funky_friday_autoplayer/configs')
  600.  
  601. local out = {}
  602. for i = 1, #list do
  603. local file = list[i]
  604. if file:sub(-5) == '.json' then
  605. -- i hate this but it has to be done ...
  606.  
  607. local pos = file:find('.json', 1, true)
  608. local start = pos
  609.  
  610. local char = file:sub(pos, pos)
  611. while char ~= '/' and char ~= '\\' and char ~= '' do
  612. pos = pos - 1
  613. char = file:sub(pos, pos)
  614. end
  615.  
  616. if char == '/' or char == '\\' then
  617. table.insert(out, file:sub(pos + 1, start - 1))
  618. end
  619. end
  620. end
  621.  
  622. Options.ConfigList.Values = out;
  623. Options.ConfigList:SetValues()
  624. Options.ConfigList:Display()
  625.  
  626. return out
  627. end
  628.  
  629. function SaveManager:Delete(name)
  630. local file = 'funky_friday_autoplayer/configs/' .. name .. '.json'
  631. if not isfile(file) then return false, string.format('Config %q does not exist', name) end
  632.  
  633. local succ, err = pcall(delfile, file)
  634. if not succ then
  635. return false, string.format('error occured during file deletion: %s', err)
  636. end
  637.  
  638. return true
  639. end
  640.  
  641. function SaveManager:SetIgnoreIndexes(list)
  642. for i = 1, #list do
  643. table.insert(self.Ignore, list[i])
  644. end
  645. end
  646.  
  647. function SaveManager.Check()
  648. local list = listfiles('funky_friday_autoplayer/configs')
  649.  
  650. for _, file in next, list do
  651. if isfolder(file) then continue end
  652.  
  653. local data = readfile(file)
  654. local success, decoded = pcall(httpService.JSONDecode, httpService, data)
  655.  
  656. if success and type(decoded) == 'table' and decoded.version ~= 2 then
  657. pcall(delfile, file)
  658. end
  659. end
  660. end
  661. end
  662.  
  663. local Window = UI:CreateWindow({
  664. Title = string.format('funky friday autoplayer - version %s | updated: %s', metadata.version, metadata.updated),
  665. AutoShow = true,
  666.  
  667. Center = true,
  668. Size = UDim2.fromOffset(550, 627),
  669. })
  670.  
  671. local Tabs = {}
  672. local Groups = {}
  673.  
  674. Tabs.Main = Window:AddTab('Main')
  675. Tabs.Miscellaneous = Window:AddTab('Miscellaneous')
  676.  
  677. Groups.Autoplayer = Tabs.Main:AddLeftGroupbox('Autoplayer')
  678. Groups.Autoplayer:AddToggle('Autoplayer', { Text = 'Autoplayer' }):AddKeyPicker('AutoplayerBind', { Default = 'End', NoUI = true, SyncToggleState = true })
  679. Groups.Autoplayer:AddDropdown('PressMode', {
  680. Text = 'Input mode',
  681. Compact = true,
  682. Default = 'firesignal',
  683. Values = { 'firesignal', 'virtual input' },
  684. 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.',
  685. })
  686.  
  687. Groups.HitChances = Tabs.Main:AddLeftGroupbox('Hit chances')
  688. Groups.HitChances:AddDropdown('AutoplayerMode', {
  689. Text = 'Autoplayer mode',
  690. Compact = true,
  691. Default = 1,
  692. Values = { 'Automatic', 'Manual' },
  693. 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',
  694. })
  695.  
  696. Groups.HitChances:AddSlider('SickChance', { Text = 'Sick chance', Min = 0, Max = 100, Default = 100, Suffix = '%', Rounding = 0, Compact = true })
  697. Groups.HitChances:AddSlider('GoodChance', { Text = 'Good chance', Min = 0, Max = 100, Default = 0, Suffix = '%', Rounding = 0, Compact = true })
  698. Groups.HitChances:AddSlider('OkChance', { Text = 'Ok chance', Min = 0, Max = 100, Default = 0, Suffix = '%', Rounding = 0, Compact = true })
  699. Groups.HitChances:AddSlider('BadChance', { Text = 'Bad chance', Min = 0, Max = 100, Default = 0, Suffix = '%', Rounding = 0, Compact = true })
  700. Groups.HitChances:AddSlider('MissChance', { Text = 'Miss chance', Min = 0, Max = 100, Default = 0, Suffix = '%', Rounding = 0, Compact = true })
  701.  
  702. Groups.HitTiming = Tabs.Main:AddLeftGroupbox('Hit timing')
  703. Groups.HitTiming:AddDropdown('DelayMode', {
  704. Text = 'Delay mode',
  705. Default = 1,
  706. Values = { 'Manual', 'Random' },
  707. 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.',
  708. })
  709.  
  710. Groups.HitTiming:AddLabel('Manual delay')
  711. Groups.HitTiming:AddSlider('ReleaseDelay', { Text = 'Note delay', Min = 0, Max = 500, Default = 20, Rounding = 0, Compact = true, Suffix = 'ms' })
  712. Groups.HitTiming:AddSlider('HeldDelay', { Text = 'Held note delay', Min = -20, Max = 100, Default = 0, Rounding = 0, Compact = true, Suffix = 'ms' })
  713.  
  714. Groups.HitTiming:AddLabel('Random delay')
  715. Groups.HitTiming:AddSlider('NoteDelayMin', { Text = 'Min note delay', Min = 0, Max = 100, Default = 0, Rounding = 0, Compact = true, Suffix = 'ms' })
  716. Groups.HitTiming:AddSlider('NoteDelayMax', { Text = 'Max note delay', Min = 0, Max = 500, Default = 20, Rounding = 0, Compact = true, Suffix = 'ms' })
  717.  
  718. Groups.HitTiming:AddSlider('HeldDelayMin', { Text = 'Min held note delay', Min = 0, Max = 100, Default = 0, Rounding = 0, Compact = true, Suffix = 'ms' })
  719. Groups.HitTiming:AddSlider('HeldDelayMax', { Text = 'Max held note delay', Min = 0, Max = 500, Default = 20, Rounding = 0, Compact = true, Suffix = 'ms' })
  720.  
  721. Groups.Misc = Tabs.Main:AddRightGroupbox('Misc')
  722. Groups.Misc:AddButton('Unlock developer notes', ActivateUnlockables)
  723. Groups.Misc:AddToggle('AutoClaimRings', { Text = 'Auto claim rings' })
  724.  
  725. Groups.Keybinds = Tabs.Main:AddRightGroupbox('Keybinds')
  726. Groups.Keybinds:AddLabel('Sick'):AddKeyPicker('SickBind', { Default = 'One', NoUI = true })
  727. Groups.Keybinds:AddLabel('Good'):AddKeyPicker('GoodBind', { Default = 'Two', NoUI = true })
  728. Groups.Keybinds:AddLabel('Ok'):AddKeyPicker('OkayBind', { Default = 'Three', NoUI = true })
  729. Groups.Keybinds:AddLabel('Bad'):AddKeyPicker('BadBind', { Default = 'Four', NoUI = true })
  730.  
  731. Groups.Configs = Tabs.Miscellaneous:AddRightGroupbox('Configs')
  732. Groups.Credits = Tabs.Miscellaneous:AddRightGroupbox('Credits')
  733. local function addRichText(label)
  734. label.TextLabel.RichText = true
  735. end
  736.  
  737. addRichText(Groups.Credits:AddLabel('<font color="#3da5ff">wally</font> - script'))
  738. addRichText(Groups.Credits:AddLabel('<font color="#de6cff">Sezei</font> - contributor'))
  739. Groups.Credits:AddLabel('Inori - ui library')
  740. Groups.Credits:AddLabel('Jan - old ui library')
  741.  
  742.  
  743. Groups.Misc = Tabs.Miscellaneous:AddRightGroupbox('Miscellaneous')
  744. Groups.Misc:AddLabel(metadata.message or 'no message found!', true)
  745.  
  746. Groups.Misc:AddDivider()
  747. Groups.Misc:AddButton('Unload script', function() pcall(shared._unload) end)
  748. Groups.Misc:AddButton('Copy discord', function()
  749. if pcall(setclipboard, "https://wally.cool/discord") then
  750. UI:Notify('Successfully copied discord link to your clipboard!', 5)
  751. end
  752. end)
  753.  
  754. Groups.Misc:AddLabel('Menu toggle'):AddKeyPicker('MenuToggle', { Default = 'Delete', NoUI = true })
  755.  
  756. UI.ToggleKeybind = Options.MenuToggle
  757.  
  758. if type(readfile) == 'function' and type(writefile) == 'function' and type(makefolder) == 'function' and type(isfolder) == 'function' then
  759. makefolder('funky_friday_autoplayer')
  760. makefolder('funky_friday_autoplayer\\configs')
  761.  
  762. Groups.Configs:AddDropdown('ConfigList', { Text = 'Config list', Values = {}, AllowNull = true })
  763. Groups.Configs:AddInput('ConfigName', { Text = 'Config name' })
  764.  
  765. Groups.Configs:AddDivider()
  766.  
  767. Groups.Configs:AddButton('Save config', function()
  768. local name = Options.ConfigName.Value;
  769. if name:gsub(' ', '') == '' then
  770. return UI:Notify('Invalid config name.', 3)
  771. end
  772.  
  773. local success, err = SaveManager:Save(name)
  774. if not success then
  775. return UI:Notify(tostring(err), 5)
  776. end
  777.  
  778. UI:Notify(string.format('Saved config %q', name), 5)
  779. task.defer(SaveManager.Refresh)
  780. end)
  781.  
  782. Groups.Configs:AddButton('Load', function()
  783. local name = Options.ConfigList.Value
  784. local success, err = SaveManager:Load(name)
  785. if not success then
  786. return UI:Notify(tostring(err), 5)
  787. end
  788.  
  789. UI:Notify(string.format('Loaded config %q', name), 5)
  790. end):AddButton('Delete', function()
  791. local name = Options.ConfigList.Value
  792. if name:gsub(' ', '') == '' then
  793. return UI:Notify('Invalid config name.', 3)
  794. end
  795.  
  796. local success, err = SaveManager:Delete(name)
  797. if not success then
  798. return UI:Notify(tostring(err), 5)
  799. end
  800.  
  801. UI:Notify(string.format('Deleted config %q', name), 5)
  802.  
  803. task.spawn(Options.ConfigList.SetValue, Options.ConfigList, nil)
  804. task.defer(SaveManager.Refresh)
  805. end)
  806.  
  807. Groups.Configs:AddButton('Refresh list', SaveManager.Refresh)
  808.  
  809. task.defer(SaveManager.Refresh)
  810. task.defer(SaveManager.Check)
  811. else
  812. Groups.Configs:AddLabel('Your exploit is missing file functions so you are unable to use configs.', true)
  813. --UI:Notify('Failed to create configs tab due to your exploit missing certain file functions.', 2)
  814. end
  815.  
  816. -- Themes
  817. do
  818. local latestThemeIndex = 0
  819. for i, theme in next, themeManager.BuiltInThemes do
  820. if theme[1] > latestThemeIndex then
  821. latestThemeIndex = theme[1]
  822. end
  823. end
  824.  
  825. latestThemeIndex = latestThemeIndex + 1
  826.  
  827. local linoriaTheme = themeManager.BuiltInThemes.Default[2]
  828. local funkyFridayTheme = table.clone(themeManager.BuiltInThemes.Default[2])
  829.  
  830. funkyFridayTheme.AccentColor = Color3.fromRGB(255, 65, 65):ToHex()
  831.  
  832. themeManager.BuiltInThemes['Linoria'] = { latestThemeIndex, linoriaTheme }
  833. themeManager.BuiltInThemes['Default'] = { 1, funkyFridayTheme }
  834.  
  835. themeManager:SetLibrary(UI)
  836. themeManager:SetFolder('funky_friday_autoplayer')
  837. themeManager:ApplyToGroupbox(Tabs.Miscellaneous:AddLeftGroupbox('Themes'))
  838.  
  839. SaveManager:SetIgnoreIndexes({
  840. "BackgroundColor", "MainColor", "AccentColor", "OutlineColor", "FontColor", -- themes
  841. "ThemeManager_ThemeList", 'ThemeManager_CustomThemeList', 'ThemeManager_CustomThemeName', -- themes
  842. })
  843. end
  844.  
  845. UI:Notify(string.format('Loaded script in %.4f second(s)!', tick() - start), 3)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement