fsfb script

An agma.io script, which includes fastsplit, secret bot packs, linesplit lock, and many other amazing features!

当前为 2024-01-06 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name fsfb script
  3. // @namespace http://tampermonkey.net/
  4. // @homepage https://greasyfork.org/scripts/446564/
  5. // @version 1.4.08
  6. // @description An agma.io script, which includes fastsplit, secret bot packs, linesplit lock, and many other amazing features!
  7. // @author fishy & firebone
  8. // @match *://agma.io/*
  9. // @icon data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIGlkPSJhIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNTE1IDUxNSI+PGRlZnM+PHJhZGlhbEdyYWRpZW50IGlkPSJiIiBjeD0iMjg2LjgiIGN5PSIxMDYiIGZ4PSIyODYuOCIgZnk9IjEwNiIgcj0iMTEzLjI4NCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2VmNDYzMCIvPjxzdG9wIG9mZnNldD0iLjEyNSIgc3RvcC1jb2xvcj0iI2VjNGUzMSIvPjxzdG9wIG9mZnNldD0iLjI1IiBzdG9wLWNvbG9yPSIjZTk1ZDM1Ii8+PHN0b3Agb2Zmc2V0PSIuNTIxIiBzdG9wLWNvbG9yPSIjZTk4MjM2Ii8+PHN0b3Agb2Zmc2V0PSIuOSIgc3RvcC1jb2xvcj0iI2ZjYzczOSIvPjxzdG9wIG9mZnNldD0iLjk4MSIgc3RvcC1jb2xvcj0iI2YxZDIzNSIvPjwvcmFkaWFsR3JhZGllbnQ+PHJhZGlhbEdyYWRpZW50IGlkPSJjIiBjeD0iMzIxLjIiIGN5PSIxMzMuMiIgZng9IjMyMS4yIiBmeT0iMTMzLjIiIHI9IjM5LjUyNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2U3NDQyZSIvPjxzdG9wIG9mZnNldD0iLjEyNSIgc3RvcC1jb2xvcj0iI2U1NGMyZiIvPjxzdG9wIG9mZnNldD0iLjI1IiBzdG9wLWNvbG9yPSIjZTM1YjMzIi8+PHN0b3Agb2Zmc2V0PSIuNTIxIiBzdG9wLWNvbG9yPSIjZTk4MjM2Ii8+PHN0b3Agb2Zmc2V0PSIuOSIgc3RvcC1jb2xvcj0iI2ZjYzczOSIvPjxzdG9wIG9mZnNldD0iLjk4MSIgc3RvcC1jb2xvcj0iI2ZjYzg0OSIvPjwvcmFkaWFsR3JhZGllbnQ+PGxpbmVhckdyYWRpZW50IGlkPSJkIiB4MT0iMTg4LjE0NiIgeTE9IjIzMi41NDYiIHgyPSIxNTMuNDc5IiB5Mj0iMjU3LjA3OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iMCIgc3RvcC1jb2xvcj0iI2UzNDIyZSIvPjxzdG9wIG9mZnNldD0iLjExNyIgc3RvcC1jb2xvcj0iI2UyNGEyZiIvPjxzdG9wIG9mZnNldD0iLjI1IiBzdG9wLWNvbG9yPSIjZTI1YjMzIi8+PHN0b3Agb2Zmc2V0PSIuNTIxIiBzdG9wLWNvbG9yPSIjZTk4MjM2Ii8+PHN0b3Agb2Zmc2V0PSIuOSIgc3RvcC1jb2xvcj0iI2ZjYzczOSIvPjxzdG9wIG9mZnNldD0iLjk4MSIgc3RvcC1jb2xvcj0iI2ZjYzg0OSIvPjwvbGluZWFyR3JhZGllbnQ+PHJhZGlhbEdyYWRpZW50IGlkPSJlIiBjeD0iMTA1LjMzMyIgY3k9IjI2Ni42NjciIGZ4PSIxMDUuMzMzIiBmeT0iMjY2LjY2NyIgcj0iMjcwLjMwNCIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPjxzdG9wIG9mZnNldD0iLjExIiBzdG9wLWNvbG9yPSIjZmNjODQ5Ii8+PHN0b3Agb2Zmc2V0PSIuMTE0IiBzdG9wLWNvbG9yPSIjZmNjNzQ3Ii8+PHN0b3Agb2Zmc2V0PSIuMTczIiBzdG9wLWNvbG9yPSIjZmNjNzNjIi8+PHN0b3Agb2Zmc2V0PSIuMjM2IiBzdG9wLWNvbG9yPSIjZmNjNzM5Ii8+PHN0b3Agb2Zmc2V0PSIuNjEyIiBzdG9wLWNvbG9yPSIjZTk4MjM2Ii8+PHN0b3Agb2Zmc2V0PSIuNzgzIiBzdG9wLWNvbG9yPSIjZTk1ZDM1Ii8+PHN0b3Agb2Zmc2V0PSIuODgyIiBzdG9wLWNvbG9yPSIjZWU1NzM0Ii8+PHN0b3Agb2Zmc2V0PSIuODg2IiBzdG9wLWNvbG9yPSIjZWM1NjMzIi8+PHN0b3Agb2Zmc2V0PSIuOTE2IiBzdG9wLWNvbG9yPSIjZTU1MjJmIi8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZTQ1MTJlIi8+PC9yYWRpYWxHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImYiIGN4PSIzNzAiIGN5PSIzMTIuMTMzIiBmeD0iMzcwIiBmeT0iMzEyLjEzMyIgcj0iMzguOTQyIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZDk0MDJiIi8+PHN0b3Agb2Zmc2V0PSIuMTE3IiBzdG9wLWNvbG9yPSIjZGE0ODJkIi8+PHN0b3Agb2Zmc2V0PSIuMjUiIHN0b3AtY29sb3I9IiNkZDU5MzIiLz48c3RvcCBvZmZzZXQ9Ii41MjEiIHN0b3AtY29sb3I9IiNlOTgyMzYiLz48c3RvcCBvZmZzZXQ9Ii45IiBzdG9wLWNvbG9yPSIjZmNjNzM5Ii8+PHN0b3Agb2Zmc2V0PSIuOTgxIiBzdG9wLWNvbG9yPSIjZmNjODQ5Ii8+PC9yYWRpYWxHcmFkaWVudD48cmFkaWFsR3JhZGllbnQgaWQ9ImciIGN4PSIzODEuMiIgY3k9IjI1OCIgZng9IjM4MS4yIiBmeT0iMjU4IiByPSIxMTMuNjAzIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZWY0NjMwIi8+PHN0b3Agb2Zmc2V0PSIuMTI1IiBzdG9wLWNvbG9yPSIjZWM0ZTMxIi8+PHN0b3Agb2Zmc2V0PSIuMjUiIHN0b3AtY29sb3I9IiNlOTVkMzUiLz48c3RvcCBvZmZzZXQ9Ii40IiBzdG9wLWNvbG9yPSIjZTk4MjM2Ii8+PHN0b3Agb2Zmc2V0PSIuNDExIiBzdG9wLWNvbG9yPSIjZTk4NDM2Ii8+PHN0b3Agb2Zmc2V0PSIuNTM2IiBzdG9wLWNvbG9yPSIjZjFhMTM3Ii8+PHN0b3Agb2Zmc2V0PSIuNjYiIHN0b3AtY29sb3I9IiNmN2I2MzgiLz48c3RvcCBvZmZzZXQ9Ii43ODIiIHN0b3AtY29sb3I9IiNmYWMyMzgiLz48c3RvcCBvZmZzZXQ9Ii45IiBzdG9wLWNvbG9yPSIjZmNjNzM5Ii8+PHN0b3Agb2Zmc2V0PSIuOTgxIiBzdG9wLWNvbG9yPSIjZmNjODQ5Ii8+PC9yYWRpYWxHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImgiIHgxPSIyNjkuOTQ0IiB5MT0iMjg3LjM0NSIgeDI9IjI2MC41MzciIHkyPSIyODkuMTE1IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjN2U0NzBjIi8+PHN0b3Agb2Zmc2V0PSIuOTk2IiBzdG9wLWNvbG9yPSIjNWYyZjAwIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImkiIHgxPSIzMzkuNTU2IiB5MT0iMjMzLjgwMSIgeDI9IjMyMi4yMjIiIHkyPSIxNzEuNDY4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHN0b3Agb2Zmc2V0PSIwIiBzdG9wLWNvbG9yPSIjZTlhZTM1Ii8+PHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjZmVkNTczIi8+PC9saW5lYXJHcmFkaWVudD48bGluZWFyR3JhZGllbnQgaWQ9ImoiIHgxPSIyNTQuNDk3IiB5MT0iMTExLjkzNiIgeDI9IjIwMC4xNjQiIHkyPSIxODYuNjAzIiB4bGluazpocmVmPSIjaSIvPjwvZGVmcz48cmVjdCB3aWR0aD0iNTE1IiBoZWlnaHQ9IjUxNSIgcng9IjEwMCIgcnk9IjEwMCIgc3R5bGU9ImZpbGw6Izc1MmIxNjsiLz48cGF0aCBkPSJNMTUwLjYyMiw0NTkuOTU2YzAsOC41MzMsMjEyLjI2NywxMS4yLDIxMi4yNjcsMHMtMjEyLjI2Ny0xMi44LTIxMi4yNjcsMFoiIHN0eWxlPSJmaWxsOiM0ZTFmMTQ7IG9wYWNpdHk6Ljk7Ii8+PHBhdGggZD0iTTI2NS44NjcsNDYuOTMzczMzLjgwMSwxOS4zMzMsMzMuODAxLDQ1LjczM2MwLDMzLjYtMzIuMzM0LDQxLjQ2Ny01MC42MDEsNTYuNzM0LDAsMC00OS43MzMsMzkuNDY3LTM3LjczMyw5My42LDAsMC00MC4yNjctMzcuOTc5LTE1LjkxMS05MS40OSwxMi44LTM5LjY0NCw3Ni45NzgtNjMuNjQ0LDc2Ljk3OC04OC44ODksMCwwLDEuMTU2LTIuNC02LjUzMy0xNS42ODlaIiBzdHlsZT0iZmlsbDp1cmwoI2IpOyIvPjxwYXRoIGQ9Ik0zMjAuOCwxMjUuMmMxNS40LDE1LjQsMzAuMjY3LTEzLjMzMywyNy40NjctMTkuNDY3LTguNjY3LTMuMzMzLTI2LjgtNi4xMzMtMjcuNDY3LDE5LjQ2N1oiIHN0eWxlPSJmaWxsOnVybCgjYyk7Ii8+PHBhdGggZD0iTTE5Mi4zMzQsMjQ5LjczM2MtNy45MzQsMTEuODY3LTM4LjczNCwxOS4zMzMtNDYuNjAxLTguNTMzLDQuNDkxLTQuMzUsMTYuOTU3LTkuNjYxLDI3Ljg2OC05LjQxMiw5LjgyMywuMjI0LDE4LjM4NSwzLjk1MywxOC43MzMsMTcuOTQ1WiIgc3R5bGU9ImZpbGw6dXJsKCNkKTsiLz48cGF0aCBkPSJNMjI3LjcxNSwyNTMuNjhjLTEyLTczLjM3OCw1Ni4zODQtMTA2LjI1OCw5MC44MjgtMTEwLjQ4LDAsMC0zMy41MTEsMjYuNDg5LTMzLjUxMSw2My4wMjJzMzcuNiw2OC44LDM3LjYsNjguOGMwLDAsNDUuMzMzLDMzLjg2Nyw0NS4zMzMsODYuOTMzcy00Mi4zMTEsOTguMjIyLTExMS40MzMsOTguMjIyLTk2LjAzMy01MC44NDQtMTAwLjMtNjAuOTc4Yy00LjI2Ny0xMC4xMzMtMzguNzU2LTE2LTM4Ljc1Ni0xNiwwLDAsMy43MzMtMzQuMzExLDMzLjQyMi01Ni41MzMsMTcuNTExLTUwLjU1NSw1MS43MTgtNjcuOTg3LDc2LjgxNi03Mi45ODYsNi40OTktMS4yOTQsMTIuNDAyLTEuNjgsMTcuMjI4LTEuNjgsMTUuNjQ0LDMuOTExLDE4Ljg0NCw2NS44OSwxOC44NDQsNjUuODksMCwwLTMyLjcxMSwzLjc5OS00NS4zMzMtMi40MjMsOS43NzgsMTEuOTExLDQwLjg4OSwxNC43NTYsNDAuODg5LDE0Ljc1Ni0uNTMzLDIzLjExMS00MS42LDM5LjQ2Ny0zNS4yLDkzLjY4OSw2LjA0NCw5LjI0NCwzNS4wMjIsNy40NjcsMzUuMDIyLDcuNDY3LDQ0LjQ0OS03LjY1Myw0NS4zMzMtNjUuMTMzLDQuNjIyLTExMy40ODhtLTQwLjg4OS00NS4yNjdjLTUuNDQ5LDAtOS44NjcsNC40MTctOS44NjcsOS44NjdzNC40MTcsOS44NjcsOS44NjcsOS44NjcsOS44NjctNC40MTcsOS44NjctOS44NjctNC40MTctOS44NjctOS44NjctOS44NjdaIiBzdHlsZT0iZmlsbDp1cmwoI2UpOyIvPjxwYXRoIGQ9Ik0zNjUuMzMzLDMwNy4wNjdjLjEzMy0xOC44LDEzLjczMy0yNy4zMzMsMjcuNzMzLTE5LjQ2NywxLjMzMywxNi41MzMtMTguMjgxLDMyLjkzMy0yNy43MzMsMTkuNDY3WiIgc3R5bGU9ImZpbGw6dXJsKCNmKTsiLz48cGF0aCBkPSJNMzU2Ljg2NywyNzguNDY3czE2Ljk5OS04Ljg2NywxNi45OTktMjguMDY3LTE4LTM1LjItMTgtMzUuMmMwLDAtMTcuODY3LTE5LjYtMTcuODY3LTM4LjhzOC44LTMyLjgsOC44LTMyLjhjMCwwLTM5LjEyNCwxOC4wMDctMzkuODY3LDU2LjgsMCwwLTEuNiwyNS42LDE0LjUzMyw0NC4xMzMsMCwwLDI4LjEzNSwzMy45MzQsMzUuNDAxLDMzLjkzNFoiIHN0eWxlPSJmaWxsOnVybCgjZyk7Ii8+PHBhdGggZD0iTTI2My43ODgsMzE3Ljg5Yy0xLjQxNi0xNC40MDMtNC41MS01NS41NTYtMTYuNTUtNjUuMDc4LDMxLjkyOSwxMi4zNTUsMjMuMDAzLDczLjI4NCwyMy4wMDMsNzMuMjg0bC02LjQ1My04LjIwNloiIHN0eWxlPSJmaWxsOnVybCgjaCk7IG9wYWNpdHk6LjY7Ii8+PHBhdGggZD0iTTMyMi4xNjcsMzMxLjgzM2MxMi41LDI5LjgzMywxOC4zNTMsODMuODExLTM0LDEwNC44MzMsNDMuODMzLTI3Ljc3OCw0MC43NzgtODQuMjc4LDM0LTEwNC44MzNaIiBzdHlsZT0iZmlsbDojZjU4NjYzOyIvPjxwYXRoIGQ9Ik0yNzUuMjA4LDI2OS4zNjdjLTM1LjkzNC00MS4xNjYtMjQuMTExLTk1LjI3OCw0My4xNjctMTI2LjE2Ny01Ni4xNjcsMzUuNTU2LTcwLjE2Nyw2OC4zMzMtNDMuMTY3LDEyNi4xNjdaIiBzdHlsZT0iZmlsbDojZjlhMzVkOyIvPjxwYXRoIGQ9Ik0yMjQuMjA0LDQyMy44NjRsLS43MDItOC40MTlzLTM3LTE2LjY2Ny0zNy0zOGMwLDMyLjY2NywzNy43MDIsNDYuNDE5LDM3LjcwMiw0Ni40MTlaIiBzdHlsZT0iZmlsbDojZjhhYzVlOyIvPjxwYXRoIGQ9Ik0zNDMuMjIyLDIzMS42NjdjLTguNjA4LTEwLjI3Mi0zMS40NzItMzQuOTE3LTE1LjU1Ni02My40NDQtMy41ODMsMjQuOTQ0LDIuNjY3LDM5LjI3OCwxNS41NTYsNjMuNDQ0WiIgc3R5bGU9ImZpbGw6dXJsKCNpKTsiLz48cGF0aCBkPSJNMjA0LDE5MS44MzNjLTUuNS0xOC42NjcsMjYuMjUtNjIuNTgzLDQ5LjgzMy03Ny00NSw1NS41LTM5LjE2Nyw1MS4zMzMtNDkuODMzLDc3WiIgc3R5bGU9ImZpbGw6dXJsKCNqKTsiLz48L3N2Zz4=
  10. // @run-at document-start
  11. // @require https://greasyfork.org/scripts/459346-fsfb-facts/code/fsfb%20facts.js?version=1145073
  12. // @license GPL-3.0-or-later
  13. // @connect translate.google.com
  14. // @connect greasyfork.org
  15. // @grant unsafeWindow
  16. // @grant GM_setValue
  17. // @grant GM_getValue
  18. // @grant GM_xmlhttpRequest
  19. // @grant GM_registerMenuCommand
  20. // @grant GM_unregisterMenuCommand
  21. // ==/UserScript==
  22.  
  23.  
  24.  
  25. // ~~~~~~~~~ Don't change anything below this unless you know what you are doing ~~~~~~~~~
  26. const version = typeof GM_info != 'undefined' && GM_info?.script?.version || '1.4.08';
  27.  
  28. let settings = {
  29. hotkeys: [
  30. {title: "Shoot 7 Ejected", id: "fsfb-key7Feed", key: 0, active: false}, // 0
  31. {title: "Linesplit Lock", id: "fsfb-linesplit", key: 0, active: false}, // 1
  32. {title: "Macro Split Bots", id: "fsfb-MacroSplitBots", key: 0, active: false}, // 2
  33. {title: "Hide UI", id: "fsfb-hideUI", key: 0, active: false}, // 3
  34. {title: "Toggle Cursor", id: "fsfb-togglecursor", key: 0, active: false}, // 4
  35. {title: "Check Profile", id: "fsfb-checkprofile", key: 0, active: false} // 5
  36. ],
  37. fastsplit_hotkeys: [
  38. {title: "Fast Onesplit", id: "fsfb-fsOne", keyName: "", keyCode: 0, active: false}, // 0
  39. {title: "1st Delay (ms)", id: "fsfb-firstdelay", val: 60, active: false}, // 1
  40. {title: "2nd Delay (ms)", id: "fsfb-secdelay", val: 60, active: false}, // 2
  41. {title: "Fast Doublesplit", id: "fsfb-fsTwo", keyName: "", keyCode: 0, active: false}, // 3
  42. {title: "1st Delay (ms)", id: "fsfb-dubfirstdelay", val: 60, active: false}, // 4
  43. {title: "2nd Delay (ms)", id: "fsfb-dubsecdelay", val: 60, active: false} // 5
  44. ],
  45. frozenvirus: [
  46. {title: "Frozen Virus", id: "fsfb-frzvs", key: 0, active: false},
  47. {title: "Press Delay (ms)", id: "fsfb-fvsdelay", val: 100, active: false}
  48. ],
  49. checkboxes: [
  50. {title: "Chat Copy/Cut/Paste", id: "fsfb-copycutpaste", active: false}, // 0
  51. {title: "Anti-AFK", id: "fsfb-antiAFK", active: false}, // 1
  52. {title: "Anti-Invis", id: "fsfb-anticloak", active: false}, // 2
  53. {title: "Linesplit Toggle", id: "fsfb-linetoggle", active: false}, // 3
  54. {title: "Change Page Title", id: "fsfb-changetitle", active: false}, // 4
  55. {title: "Hide Shouts", id: "fsfb-hideshouts", active: false}, // 5
  56. {title: "Hold To Spam Rec/Spd", id: "fsfb-recospeed", active: false}, // 6
  57. {title: "Show Portal Mass", id: "fsfb-portalmass", active: false}, // 7
  58. {title: "Pow Spawns Overlay", id: "fsfb-powsoverlay", active: false}, // 8
  59. {title: "Mothercell Mass", id: "fsfb-mtchmass", active: false}, // 9
  60. {title: "Inventory One Row", id: "fsfb-pwsonerow", active: false}, // 10
  61. {title: "Quick Buy", id: "fsfb-qBuy", active: false} // 11
  62. ],
  63. slowFeed: [
  64. {title: "Toggle Feed", id: "fsfb-slowFeed", key: 0, active: false},
  65. {title: "Feed Delay (ms)", id: "fsfb-slowfeedtime", val: 100, active: false}
  66. ],
  67. quickSettings: [
  68. {id: "fsfb-quick-hotkey1", id1: "fsfb-quick-select1", set: "cSkins", key: 0, active: false},
  69. {id: "fsfb-quick-hotkey2", id1: "fsfb-quick-select2", set: "cWearables", key: 0, active: false},
  70. {id: "fsfb-quick-hotkey3", id1: "fsfb-quick-select3", set: "cFood", key: 0, active: false},
  71. {id: "fsfb-quick-hotkey4", id1: "fsfb-quick-select4", set: "cBubbleCells", key: 0, active: false},
  72. {id: "fsfb-quick-hotkey5", id1: "fsfb-quick-select5", set: "cNames", key: 0, active: false},
  73. {id: "fsfb-quick-hotkey6", id1: "fsfb-quick-select6", set: "cNameOutlines", key: 0, active: false}
  74. ],
  75. uiScaling: [
  76. // {title: "Chat Size", id: "fsfb-chatSize", level: 5},
  77. {title: "Inventory Size", id: "fsfb-invSize", level: 5}, // 0
  78. {title: "Food Size", id: "fsfb-foodSize", level: 1}, // 1
  79. {title: "Statsbox Size", id: "fsfb-statsSize", level: 5} // 2
  80. ],
  81. theme: [
  82. {title: "Food Color", id: "fsfb-check-foodcolor", id1: "fsfb-color-foodcolor", color: "#FFFFFF", active: false}, // 0
  83. {title: "Virus Color", id: "fsfb-check-viruscolor", id1: "fsfb-color-viruscolor", color: "#00ff00", active: false}, // 1
  84. {title: "Virus Stroke", id: "fsfb-check-virusstroke", id1: "fsfb-color-virusstroke", color: "#00ff00", active: false}, // 2
  85. {title: "Mothercell Color", id: "fsfb-check-msColor", id1: "fsfb-color-msColor", color: "#cd5564", active: false}, // 3
  86. {title: "Mothercell Stroke", id: "fsfb-check-msStroke", id1: "fsfb-color-msStroke", color: "#cd5564", active: false}, // 4
  87. {title: "Border Color", id: "fsfb-check-border", id1: "fsfb-color-border", color: "#CC3030", active: false}, // 5
  88. {title: "BR Hazard Zone", id: "fsfb-check-Hazard", id1: "fsfb-color-hazard", color: "#cc3030", active: false} // 6
  89. ],
  90. theme_boxes: [
  91. {title: "Fancy Bubble Cells", id: "fsfb-bublecell", active: false}, // 0
  92. {title: "Show Player Mass", id: "fsfb-showmass", active: false}, // 1
  93. {title: "Only My Skin", id: "fsfb-myskins", active: false}, // 2
  94. {title: "Only Party Skins", id: "fsfb-partyskins", active: false}, // 3
  95. {title: "Only My Nick", id: "fsfb-mynick", active: false}, // 4
  96. {title: "Only Party Nicks", id: "fsfb-partynicks", active: false}, // 5
  97. {title: "Spiked Cells", id: "fsfb-spikedcells", active: false}, // 6
  98. {title: "Reverse Cell Order", id: "fsfb-revcell", active: false}, // 7
  99. {title: "Render Portals Top", id: "fsfb-portalstop", active: false}, // 8
  100. ],
  101. anti_lag: [
  102. {title: "Hide Small Minions", id: "fsfb-hideminions", active: false}, // 0
  103. {title: "No Minion Skins", id: "fsfb-nominionskins", active: false}, // 1
  104. {title: "Hide All Ejected", id: "fsfb-hideejected", active: false}, // 2
  105. {title: "Hide Static Ejected", id: "fsfb-hidestaticej", active: false}, // 3
  106. {title: "No Eat Anims", id: "fsfb-nodeathanims", active: false} // 4
  107. ],
  108. chat_translate: [
  109. {title: "Translate Chat", id: "fsfb-tranchat", active: false}, // 0
  110. {title: "Translate Server", id: "fsfb-tranplyr", active: false}, // 1
  111. {title: "Show Original", id: "fsfb-tranorig", active: false}, // 2
  112. {title: "Translate From:", id: "fsfb-tran1", set: "auto"}, // 3
  113. {title: "Translate To:", id: "fsfb-tran2", set: "auto"} // 4
  114. ],
  115. export_import: [
  116. {title: "Game Settings", id: "fsfb-game-settings", active: false},
  117. {title: "Game Controls", id: "fsfb-game-controls", active: false},
  118. {title: "Custom Background", id: "fsfb-custom-bg", active: false},
  119. {title: "Script Settings", id: "fsfb-script-settings", active: false},
  120. {title: "Script Theme", id: "fsfb-theme-settings", active: false}
  121. ]
  122. }, misc_settings = {
  123. abil: {},
  124. bots: {},
  125. statsPos: null,
  126. statsSettings: {
  127. xp: {
  128. lvlcomp: true,
  129. rem: true,
  130. projhr: true,
  131. lasthr: true,
  132. lastmin: true,
  133. lastsec: true,
  134. mean: true,
  135. median: true,
  136. sd: true,
  137. sesh: true,
  138. seshlength: true,
  139. lifetime: true
  140. },
  141. coins: {
  142. rem: true,
  143. projhr: true,
  144. lasthr: true,
  145. lastmin: true,
  146. mean: true,
  147. median: true,
  148. sd: true,
  149. sesh: true,
  150. seshlength: true
  151. }
  152. }
  153. }
  154. // Don't edit these settings directly (changing code) unless you aren't using tampermonkey or can't access the settings via the menu commands
  155. let userPreferences = {
  156. hideAds: true,
  157. improvedShop: true,
  158. extraBotPacks: true,
  159. rightClickCopyChat: true,
  160. rightClickCopyInfo: true,
  161. showRemainingAbilityTime: true,
  162. unlockFreeSkins: true,
  163. hoverShowSkinID: true,
  164. coinXPstats: true,
  165. saveStatsBoxPosition: true,
  166. showXPdecimals: true,
  167. whiteBorder4BlackCells: true,
  168. sortWearablesByOwned: true,
  169. linesplitClosestSide: false,
  170. friendDeclineAll: true,
  171. rainbowMapBorder: false,
  172. rainbowBorderSpeed: 5,
  173. rainbowBrHazard: false,
  174. rainbowBrHazardSpeed: 5,
  175. extraChatCommands: true,
  176. chatPrefix: '/f ',
  177. bypassConfirmChatRules: true,
  178. publicSkinSearch: true,
  179. extraOneFastSplitDelay: false,
  180. notifyNewUpdates: true,
  181. preventAutoLowGraphics: true,
  182. stylizeActiveSettings: true,
  183. removeMultiFeedPopup: true,
  184. level1kFix: true,
  185. messageBanWarning: true
  186. }
  187. const keyCodeMappings = { 0: "", 8: "BACKSPACE", 9: "TAB", 12: "CLEAR", 13: "ENTER", 16: "SHIFT", 17: "CTRL", 18: "ALT", 19: "PAUSE", 20: "CAPSLOCK", 27: "ESC", 32: "SPACE", 33: "PAGEUP", 34: "PAGEDOWN", 35: "END", 36: "HOME", 37: "LEFT", 38: "UP", 39: "RIGHT", 40: "DOWN", 44: "PRTSCN", 45: "INS", 46: "DEL", 91: "WIN", 92: "WIN", 93: "CONTEXTMENU", 96: "NUM 0", 97: "NUM 1", 98: "NUM 2", 99: "NUM 3", 100: "NUM 4", 101: "NUM 5", 102: "NUM 6", 103: "NUM 7", 104: "NUM 8", 105: "NUM 9", 106: "NUM *", 107: "NUM +", 109: "NUM -", 110: "NUM .", 111: "NUM /", 112: "F1", 113: "F2", 114: "F3", 115: "F4", 116: "F5", 117: "F6", 118: "F7", 119: "F8", 120: "F9", 121: "F10", 122: "F11", 123: "F12", 124: "F13", 125: "F14", 126: "F15", 127: "F16", 128: "F17", 129: "F18", 130: "F19", 131: "F20", 132: "F21", 133: "F22", 134: "F23", 135: "F24", 144: "NUMLOCK", 145: "SCROLLLOCK", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'"}, cmap = { a: "а", e: "е", i: "і", o: "ο", u: "υ", y: "у", A: "А", E: "Е", I: "Ӏ", O: "Ο", U: "𝗨", Y: "Υ"};
  188.  
  189. const setStorage = (name, obj) => typeof GM_setValue != "function" ? localStorage.setItem(name, JSON.stringify(obj)) : GM_setValue(name, obj),
  190. getStorage = (name, default_obj) => typeof GM_getValue != "function" ? localStorage.getItem(name) != null ? JSON.parse(localStorage.getItem(name)) : setStorage(name, default_obj) : GM_getValue(name, default_obj);
  191.  
  192. if(typeof unsafeWindow === 'undefined') unsafeWindow = window;
  193.  
  194. const saveSettings = () => setStorage("fsfb-scripts", settings);
  195. const getSettings = () => {
  196. let settingsPrev = getStorage("fsfb-scripts", settings);
  197. for(let i in settingsPrev) {
  198. for(let j in settingsPrev[i]) {
  199. for(let x in settings){
  200. for(let y in settings[x]){
  201. if(settingsPrev[i][j].id == settings[x][y].id) settings[x][y] = settingsPrev[i][j];
  202. }
  203. }
  204. }
  205. }
  206. }
  207.  
  208. // ----------
  209. let prevPreferences = getStorage('fsfb-user-settings', userPreferences);
  210.  
  211. for(let prevSetting in prevPreferences) {
  212. for(let currSetting in userPreferences){
  213. if(prevSetting == currSetting) userPreferences[currSetting] = prevPreferences[prevSetting]; //settings[x][y] = settingsPrev[i][j];
  214. }
  215. }
  216.  
  217. const getMiscSettings = () => {
  218. let prevSettings = getStorage("fsfb-misc", misc_settings);
  219. misc_settings = {...misc_settings, ... prevSettings};
  220. }
  221. getSettings();
  222. getMiscSettings();
  223.  
  224. const getName = key => key ? keyCodeMappings[key] ?? String.fromCharCode(key) : '';
  225.  
  226. const levelSum = lvl => lvl * --lvl / 2,
  227. range = arr => Math.max(...arr) - Math.min(...arr),
  228. sigma = arr => arr.reduce((a, b) => a + b),
  229. mean = arr => sigma(arr) / arr.length,
  230. variance = arr => arr.reduce((a, b) => a + (b - mean(arr)) ** 2, 0) / arr.length,
  231. standardDeviation = arr => Math.sqrt(variance(arr)),
  232. ascending = arr => arr.sort((a, b) => a - b),
  233. getIQR = arr => quartile(arr, .75) - quartile(arr, .25),
  234. round = (num, places = 0) => Math.round(num * +("1e" + places)) / +("1e" + places);
  235. const median = arr => {
  236. const mid = ~~(arr.length / 2),
  237. asc = ascending(arr);
  238. return arr.length % 2 !== 0 ? asc[mid] : (asc[mid - 1] + asc[mid]) / 2;
  239. }
  240. const getProperty = (arr, property) => {
  241. let newArr = [];
  242. for (let i of arr) newArr.push(i[property]);
  243. return newArr;
  244. }
  245. const quartile = (arr, q) => {
  246. const sorted = ascending(arr),
  247. pos = (sorted.length - 1) * q,
  248. base = ~~pos,
  249. rest = pos - base;
  250. return sorted[base + 1] !== null ? sorted[base] + rest * (sorted[base + 1] - sorted[base]) : sorted[base]
  251. }
  252. const checkOutliers = (arr, returnOutliers) => {
  253. let nonOutliers = [], outliers = [];
  254. const IQR = getIQR(arr),
  255. Q1 = quartile(arr, .25),
  256. Q3 = quartile(arr, .75);
  257. for (let i of arr) i < Q1 - 1.5 * IQR || i > Q3 + 1.5 * IQR ? outliers.push(i) : nonOutliers.push(i);
  258. return returnOutliers ? outliers : nonOutliers;
  259. }
  260. const msToTime = ms => {
  261. const pad = num => num < 10 ? '0' + ~~num : ~~num;
  262. return `${pad(ms / 36e5)}:${pad(ms / 6e4 % 60)}:${pad(ms / 1e3 % 60)}`; // hrs:mins:secs
  263. }
  264. const changeTitle = title => {
  265. if(document.title != title) document.title = title;
  266. };
  267.  
  268. if(!settings.checkboxes[4].active && document.title == 'Agma.io - A free multiplayer MMO game') changeTitle('Agma.io');
  269.  
  270. if(userPreferences.unlockFreeSkins){
  271. ["", 56, 1657, 2281, 2282, 2297, 2331, 2529, 2626, 2683, 2816, 2832].forEach(id => localStorage.setItem('ytSkin' + id, '1'));
  272. localStorage.setItem('fbSkin', '1');
  273. }
  274.  
  275. const softSanitize = str => {
  276. const map = {
  277. '&': '&amp;',
  278. '<': '&lt;',
  279. '>': '&gt;',
  280. '"': '&quot;',
  281. "'": '&#x27;',
  282. "/": '&#x2F;',
  283. };
  284. return str.replace(/[&<>"'/]/ig, match => map[match]);
  285. }
  286.  
  287. const escapeRegExp = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  288.  
  289. ['paste', 'copy', 'cut'].forEach(a => {
  290. unsafeWindow.addEventListener(a, e => {
  291. if(document.querySelector('#fsfb-copycutpaste')?.checked) e.stopImmediatePropagation();
  292. }, true)
  293. });
  294.  
  295.  
  296. let agmaHotkeys = JSON.parse(localStorage.getItem('hotkeys'));
  297. let agmaSettings = JSON.parse(localStorage.getItem('settings'));
  298.  
  299. Object.defineProperty(unsafeWindow, 'localStorage', {
  300. value: new Proxy(localStorage, {
  301. set: function (ls, prop, val) {
  302. if(prop === 'hotkeys') agmaHotkeys = JSON.parse(val);
  303. else if(prop === 'settings') agmaSettings = JSON.parse(val);
  304. return !void(ls[prop] = val);
  305. },
  306. get: function(ls, prop) {
  307. return prop == 'setItem' || typeof ls[prop] == 'function' ? ls[prop].bind(ls) : ls[prop];
  308. }
  309. }),
  310. configurable: true,
  311. enumerable: true
  312. });
  313.  
  314. const hotkeysMap = {
  315. 360: "W360",
  316. Split: "Space",
  317. DoubleSplit: "D",
  318. TripleSplit: "T",
  319. MacroSplit: "Z",
  320. MacroFeed: "W",
  321. FixedMouse: "C",
  322. Respawn: "M",
  323. MultiFeed: "V",
  324. Recombine: "E",
  325. Speed: "S",
  326. FreezeSelf: "F",
  327. Invisibility: "I",
  328. DropWall: "DW",
  329. ToggleCamera: "Q",
  330. FreezeCamera: "F",
  331. ToggleControlBots: "Q",
  332. SplitBots: "A",
  333. FeedBots: "X"
  334. }
  335. const getKey = id => agmaHotkeys && agmaHotkeys[hotkeysMap?.[id.replace(/^key/gm, '')]]?.c;
  336. unsafeWindow.getKey = getKey;
  337.  
  338. if(userPreferences.bypassConfirmChatRules) localStorage.setItem('crc', 'true');
  339.  
  340.  
  341. const insertCustomjQuery = $ => {
  342. // const { $ } = unsafeWindow;
  343.  
  344. if(userPreferences.extraBotPacks){
  345.  
  346. const newBots = [
  347. {title: "100 Bots", time: "24 HOURS", price: "700,000", id: 9},
  348. {title: "125 Bots", time: "48 HOURS", price: "900,000", id: 10},
  349. {title: "300 Bots", time: "24 HOURS", price: "900,000", id: 7},
  350. {title: "100 MASS Bots", time: "1 HOURS", price: "800,000", id: 8},
  351. {title: "300 Bots", time: "72 HOURS", price: "2,000,000", id: 11},
  352. {title: "100 MASS Bots", time: "24 HOURS", price: "2,600,000", id: 12},
  353. {title: "500 MASS Bots", time: "2 HOURS", price: "30,000,000", id: 17},
  354. {title: "500 MASS Bots", time: "12 HOURS", price: "40,000,000", id: 16}
  355. ]
  356.  
  357.  
  358. if(document.getElementsByClassName('tab-container-section minion scroll')?.[0]?.children?.[0].children.length){ // check if minions page has already been loaded
  359. $('.confirm-minion[item=7], .confirm-minion[item=8]').parent().hide();
  360. for(let i of newBots) createEl(i);
  361. document.getElementById('extraTab').childNodes[0].setAttribute('onclick', '');
  362. $('.confirm-minion.extra-min').click(function(e) {
  363. e.preventDefault(); // Prevent the href from redirecting directly
  364. const { setMinionUi, purchaseMinion } = unsafeWindow;
  365. let linkURL = $(this).attr("href"),
  366. priceK = $(this).attr("price"),
  367. itemId = $(this).attr("item");
  368. setMinionUi(true);
  369. unsafeWindow.swal({
  370. title: "Confirm",
  371. text: 'If you click "Buy", you will purchase these minions. They cost ' + priceK,
  372. type: "warning",
  373. showCancelButton: true,
  374. confirmButtonColor: "#4CAF50",
  375. confirmButtonText: "Yes, confirm purchase",
  376. cancelButtonText: "No, cancel purchase"
  377. }, function() {
  378. purchaseMinion(itemId);
  379. });
  380. });
  381. } else {
  382. const _$click = $.prototype.click;
  383.  
  384. $.prototype.click = function(){
  385. if(this.selector === ".confirm-minion" && typeof arguments[0] === 'function'){
  386. $('.confirm-minion[item=7], .confirm-minion[item=8]').parent().hide();
  387. for(let i of newBots) createEl(i);
  388. document.getElementById('extraTab').childNodes[0].setAttribute('onclick', '');
  389. return _$click.apply($('.confirm-minion'), arguments);
  390. }
  391. return _$click.apply(this, arguments);
  392. }
  393. }
  394. function createEl(i) {
  395. const botEl = document.createElement('li');
  396. botEl.setAttribute('class', 'masterTooltip extra-min');
  397. botEl.setAttribute('title', 'Spawns bots/minions which suicide into your playercell to make you big in no time! Minions follow your mouse and split upon your command! Minions start immediately after you buy them.');
  398. document.getElementsByClassName('tab-container-section minion scroll')[0].children[0].children[0].appendChild(botEl);
  399. botEl.innerHTML = `<div class="title_prch"><img src="img/store/minions/minions_tab.png" width="70px" height="60px"><div class="minionDescription"><h4 style="font-size: 18px;">${i.title}</h4><h3 style="margin-top:10px;color:white;"> ${i.time}</h3></div> <span class="win-price">${i.price}</span></div><a href="#" price="${i.price}" item="${i.id}" class="purchase-btn2 confirm-minion extra-min">Buy</a>`;
  400. }
  401. }
  402. }
  403.  
  404.  
  405. if(typeof unsafeWindow.$ === 'function'){
  406. insertCustomjQuery(unsafeWindow.$);
  407. } else {
  408. let setjQueryVal;
  409. Object.defineProperty(unsafeWindow, '$', { // intercept immediately after jQuery is defined on window
  410. set: function(val) {
  411. setjQueryVal = val;
  412. insertCustomjQuery(val);
  413. },
  414. get: function() {
  415. return setjQueryVal;
  416. },
  417. configurable: true
  418. });
  419. }
  420.  
  421.  
  422. const afterLoaded = () => {
  423.  
  424. unsafeWindow.fsfbStartedLoading = true;
  425. const { $, swal, purchaseItem, hotkeySetDefaults, setDefaults, uisdoa } = unsafeWindow;
  426.  
  427. // attempt to prevent the script from being active on subpages of agma.io
  428. if($ == null || $('#friendResizer').length < 1 || $('#megaholder').length < 1 || $('#preroll').length < 1) return;
  429.  
  430. if(!agmaHotkeys) hotkeySetDefaults();
  431. if(!agmaSettings) setDefaults();
  432.  
  433. if(typeof GM_registerMenuCommand === 'function' && typeof GM_unregisterMenuCommand === 'function'){
  434.  
  435. const addMenuCmd = (name, callback, accessKey) => GM_registerMenuCommand(name, callback, accessKey);
  436. const remMenuCmd = command_id => GM_unregisterMenuCommand(command_id);
  437.  
  438. let expandedUserPref = {
  439. hideAds: { title: 'Hide Ads', type: 'bool' },
  440. improvedShop: { title: 'Improved Shop', type: 'bool' },
  441. extraBotPacks: { title: 'Better Shop', type: 'bool' },
  442. rightClickCopyChat: { title: 'Right-Click Copy Chat', type: 'bool' },
  443. rightClickCopyInfo: { title: 'Right-Click Copy Info', type: 'bool' },
  444. showRemainingAbilityTime: { title: 'Show Remaining Abilities', type: 'bool' },
  445. unlockFreeSkins: { title: 'Unlock Free Skins', type: 'bool' },
  446. hoverShowSkinID: { title: 'Hover Show Skin ID', type: 'bool' },
  447. coinXPstats: { title: 'Coin/XP Statistics', type: 'bool' },
  448. saveStatsBoxPosition: { title: 'Save Stats-Box Position', type: 'bool' },
  449. showXPdecimals: { title: 'Show Decimals in % XP', type: 'bool' },
  450. whiteBorder4BlackCells: { title: 'White Border For Black Cells', type: 'bool' },
  451. sortWearablesByOwned: { title: 'Sort Owned Wearables', type: 'bool' },
  452. linesplitClosestSide: { title: 'Linesplit Closest Side', type: 'bool' },
  453. friendDeclineAll: { title: 'Friend Decline All', type: 'bool' },
  454. rainbowMapBorder: { title: 'Rainbow Map Border', type: 'bool' },
  455. rainbowBorderSpeed: { title: 'Rainbow Border Speed', type: 'num' },
  456. rainbowBrHazard: { title: 'Rainbow BR Hazard', type: 'bool' },
  457. rainbowBrHazardSpeed: { title: 'Rainbow Hazard Speed', type: 'num' },
  458. extraChatCommands: { title: 'FSFB Chat Commands', type: 'bool' },
  459. chatPrefix: { title: 'Chat Command Prefix', type: 'str' },
  460. bypassConfirmChatRules: { title: 'Bypass Chat Confirm', type: 'bool' },
  461. publicSkinSearch: { title: 'Public Skin Search', type: 'bool' },
  462. extraOneFastSplitDelay: { title: 'Extra Fastsplit Delay', type: 'bool' },
  463. notifyNewUpdates: { title: 'Notify New Updates', type: 'bool' },
  464. preventAutoLowGraphics: { title: 'Disable Auto-Low Graphics', type: 'bool' },
  465. stylizeActiveSettings: { title: 'Stylize Active Settings', type: 'bool' },
  466. removeMultiFeedPopup: { title: 'Remove MultiFeed Warning', type: 'bool' },
  467. level1kFix: { title: 'Fix Level 1000 Bug', type: 'bool'},
  468. messageBanWarning: { title: 'Censor Message To Mitigate Ban', type: 'bool' }
  469. }
  470.  
  471. function updateShownSettings(){
  472. for(let keyName in expandedUserPref){
  473. const expObj = expandedUserPref[keyName];
  474.  
  475. if('menu' in expObj){
  476. remMenuCmd(expObj.menu);
  477. }
  478.  
  479. let value = userPreferences[keyName],
  480. typeIndicator = expObj.type == 'bool' ? `[ ${value ? '✅' : '❌'} ] ` : `[ "${value}" ] `;
  481.  
  482. expObj.menu = addMenuCmd(typeIndicator + expObj.title, function(e){
  483. let newVal;
  484. if(expObj.type === 'bool'){
  485. newVal = !value;
  486. userPreferences[keyName] = newVal;
  487. } else {
  488. newVal = prompt('Please input the value you want to set:', value);
  489.  
  490. if(expObj.type === 'num' && !/^[0-9]+$/.test(newVal)){
  491. updateShownSettings();
  492. return alert('Please only enter numbers');
  493. }
  494. userPreferences[keyName] = expObj.type === 'num' ? Number(newVal) : newVal;
  495. }
  496. updateShownSettings();
  497. setStorage('fsfb-user-settings', userPreferences);
  498. alert(`Setting: "${expObj.title}" value changed to: ${expObj.type === 'bool' ? `[ ${newVal ? '✅' : '❌'} ] ` : `[ "${newVal}" ] `}.\nNote that some (not all) settings require a browser reload to take full effect.`);
  499. })
  500. }
  501. addHideAll();
  502. }
  503.  
  504. function addHideAll(){
  505. let hideAllBtn = addMenuCmd('Hide all settings', function(){
  506. for(let keyName in expandedUserPref){
  507. const expObj = expandedUserPref[keyName];
  508. if('menu' in expObj){
  509. remMenuCmd(expObj.menu);
  510. }
  511. }
  512. remMenuCmd(hideAllBtn);
  513. addShowMore();
  514. });
  515. }
  516. function addShowMore(){
  517. let showMoreBtn = addMenuCmd('Show all settings', function(){
  518. remMenuCmd(showMoreBtn);
  519. updateShownSettings();
  520. })
  521. }
  522. addShowMore();
  523. }
  524.  
  525. const _slideDown = $.prototype.slideDown;
  526. $.prototype.slideDown = function () {
  527. if(this.selector === '#curser'){
  528. let content = this[0].textContent;
  529. if(content?.includes("Your FPS seems to be low")) return;
  530. if(content?.includes("Connected to server")){
  531. playerAlive = false;
  532. svSwitch = true;
  533. try{
  534. currentServerId = 0;
  535. for(let { isCurrent, id } of JSON.parse(localStorage.gameservers)){
  536. if(isCurrent) currentServerId = id;
  537. }
  538. } catch {
  539. console.warn("FSFB: Failed to grab server id from localStorage")
  540. currentServerId = parseInt($(".server-tabmenu").find(".active")[0]?.id.slice(9));
  541. }
  542. }
  543. }
  544. return _slideDown.apply(this, arguments);
  545. }
  546. const _$text = $.prototype.text;
  547. $.prototype.text = function(){
  548. if(this.selector === '.exp-bar' && userPreferences.showXPdecimals){
  549. arguments[0] = (~~(+this[0].parentElement.style.width.slice(0, -1) * 100) / 100) + '%';
  550. }
  551. if(this.selector == '#userCoins2' && typeof arguments[0] === 'string') currentUser = arguments[0];
  552. return _$text.apply(this, arguments);
  553. }
  554.  
  555. let swalUserColor = '#FF00D8',
  556. swalRoleTitle = 'Fsfb Developer',
  557. swalRoleColor = '#FF78EA';
  558.  
  559. unsafeWindow.swal = function() {
  560. if(typeof arguments[0] === 'object' && 'title' in arguments[0] && userPreferences.removeMultiFeedPopup && arguments[0].type === "error" && arguments[0].text === "Eject multiple cells (Multi Feed) is only available for users who purchased the Custom Skins Sale"){
  561. return void(curserMsg(arguments[0].text, 'red', 1e4));
  562. }
  563. if (typeof arguments[0] === 'object' && 'title' in arguments[0] && /<img src="((skins\/\d+(_lo)?\.png\?(u=\d+)?)|img\/userprofile\.png)" width="\d+" height="\d+" style="border-radius:50%;"><br><br><span style=".*?">(Fishyyyy|firebonee|kidmaletteo)<\/span>/gm.test(arguments[0].title)) {
  564. arguments[0].title = arguments[0].title
  565. .replace(/(?<=<span style=").*(?=">(Fishyyyy|firebonee|kidmaletteo))/gm, 'color: ' + swalUserColor)
  566. .replace(/(?<=(?:<span style="display:block; margin:-10px 0px 15px; font-size:12px; line-height:normal;"><br>)|(?:<br><span style="padding:2px 5px; font-size:10px; background:#999; color:#000; border-radius:10px;">Hidden<\/span><br><br>))(?=<span)/gm, `<span style="color: ${swalRoleColor};">&#9734;&#9734; ${swalRoleTitle} &#9734;&#9734;</span><br>`);
  567. }
  568. return swal.apply(this, arguments);
  569. }
  570.  
  571. $('.setting-tablink').css({'width' : '30%'});
  572. $('#settingTab2').after(`<button id="settingTab4" class="setting-tablink" onclick="openSettingPage(4);" style="width: 9%; font-size: calc(0.3vw + 7.5px);"><div class="fa fa-cogs fa-lg" style="font-size: 15px; color: lightgray;"></div></button>`);
  573. $('#settingPage3').after(`<div id="settingPage4" class="setting-tabcontent"><div class="row"><div class="col-md-10 col-md-offset-1 stng" id="fsfb-settings-main" style="padding:0"><div id="fsfb-settings-left"><section id="fsfb-sect-checkbox" class="padbot10 fsfb-sect-ch"></section><section id="fsfb-sect-theme" class="fsfb-sect-ch"></section><section id="fsfb-sect-antilag" class="fsfb-sect-ch padbot10"></section><section id="fsfb-sect-translate" style="/* padding-top: 30px; */" class="fsfb-sect-ch"></section></div><div id="fsfb-settings-right"><section id="fsfb-sect-hotkeys" class="padbot10"></section><section id="fsfb-sect-slowfeed" class="padbot10"></section><section id="fsfb-sect-frzvrs" class="padbot10"></section><section id="fsfb-sect-fastsplit" class="padbot10"></section><section id="fsfb-sect-quickSettings" class="padbot10"></section><section id="fsfb-sect-uiScale" class="padbot10"></section><section id="fsfb-sect-imexport" class="fsfb-sect-ch padbot10"></section><section id="fsfb-sect-translate2"></section></div></div></div></div>`);
  574. $('.container').eq(0).css("max-width", "1250px");
  575. $('#fsfb-sect-checkbox').append(`<p class="hotkey-paragraph">Script Features</p>`);
  576.  
  577. // add checkbox HTML
  578. for(let i of settings.checkboxes){
  579. $('#fsfb-sect-checkbox').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  580. $( "#" + i.id).change(function(e) {
  581. changeSettings(this.id, $(this).is(':checked'), e);
  582. });
  583. }
  584.  
  585. // add import/export HTML
  586. $('#fsfb-sect-imexport').append(`<p class="hotkey-paragraph">Import/Export</p>`);
  587. for(let i of settings.export_import){
  588. $('#fsfb-sect-imexport').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  589. $( "#" + i.id).change(function() {
  590. changeSettings(this.id, $(this).is(':checked'));
  591. });
  592. }
  593. $('#fsfb-sect-imexport').append(`<div id="fsfb-ximport-cont"><div id="fsfb-export-btn" class="fsfb-eximport">Export</div><div id="fsfb-import-btn" class="fsfb-eximport">Import</div></div>`);
  594.  
  595. $('#fsfb-sect-theme').append(`<p class="hotkey-paragraph">Game Theme</p>`);
  596. for(let i of settings.theme){
  597. $('#fsfb-sect-theme').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title}</p><div style="background-color: black;"><input id="${i.id1}"type="color"></div></label>`);
  598. $( "#" + i.id).change(function() {
  599. changeSettings(this.id, $(this).is(':checked'));
  600. });
  601. $( "#" + i.id1).on('input', function() {
  602. changeSettings(this.id, this.value);
  603. $(this).parent().css('background-color', this.value); // bcs the regular [input="color"] looks rly shit, change color of overlayed div instead
  604. });
  605. }
  606. for(let i of settings.theme_boxes){
  607. $('#fsfb-sect-theme').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  608. $( "#" + i.id).change(function(e) {
  609. changeSettings(this.id, $(this).is(':checked'), e);
  610. });
  611. }
  612.  
  613. $('#fsfb-sect-antilag').append(`<p class="hotkey-paragraph">Anti-Lag</p>`);
  614. for(let i of settings.anti_lag){
  615. $('#fsfb-sect-antilag').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  616. $( "#" + i.id).change(function(e) {
  617. changeSettings(this.id, $(this).is(':checked'), e);
  618. });
  619. }
  620.  
  621. $('#fsfb-sect-translate').append(`<p class="hotkey-paragraph">Chat Translate</p>`);
  622.  
  623. for(let i of settings.chat_translate){
  624. if(!('set' in i)){ //checkboxes
  625. $('#fsfb-sect-translate').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  626. $( "#" + i.id).change(function(e) {
  627. changeSettings(this.id, $(this).is(':checked'), e);
  628. });
  629. } else {
  630. $('#fsfb-sect-translate2').append(`<p style="margin-top: 2px;">${i.title}</p><select id="${i.id}" class="fsfb-changelang"><option value="auto">Detect language</option><option value="af">Afrikaans</option><option value="sq">Albanian</option><option value="ar">Arabic</option><option value="hy">Armenian</option><option value="az">Aerbaijani</option><option value="eu">Basque</option><option value="be">Belarusian</option><option value="bn">Bengali</option><option value="bg">Bulgarian</option><option value="ca">Catalan</option><option value="zh-CN">Chinese (simpl)</option><option value="zh-TW">Chinese (trad)</option><option value="hr">Croatian</option><option value="cs">Czech</option><option value="da">Danish</option><option value="nl">Dutch</option><option value="en">English</option><option value="et">Estonian</option><option value="tl">Filipino</option><option value="fi">Finnish</option><option value="fr">French</option><option value="gl">Galician</option><option value="ka">Georgian</option><option value="de">German</option><option value="el">Greek</option><option value="ht">Haitian Creole</option><option value="iw">Hebrew</option><option value="hi">Hindi</option><option value="hu">Hungarian</option><option value="is">Icelandic</option><option value="id">Indonesian</option><option value="ga">Irish</option><option value="it">Italian</option><option value="ja">Japanese</option><option value="ko">Korean</option><option value="lv">Latvian</option><option value="lt">Lithuanian</option><option value="mk">Macedonian</option><option value="ms">Malay</option><option value="mt">Maltese</option><option value="no">Norwegian</option><option value="fa">Persian</option><option value="pl">Polish</option><option value="pt">Portuguese</option><option value="ro">Romanian</option><option value="ru">Russian</option><option value="sr">Serbian</option><option value="sk">Slovak</option><option value="sl">Slovenian</option><option value="es">Spanish</option><option value="sw">Swahili</option><option value="sv">Swedish</option><option value="th">Thai</option><option value="tr">Turkish</option><option value="uk">Ukrainian</option><option value="ur">Urdu</option><option value="vi">Vietnamese</option><option value="cy">Welsh</option><option value="yi">Yiddish</option></select>`);
  631. }
  632. };
  633.  
  634. $('.fsfb-changelang').on("change", function(){
  635. for(let i = 0; i < settings.chat_translate.length; i++){
  636. if(settings.chat_translate[i].id == this.id) settings.chat_translate[i].set = this.value;
  637. }
  638. saveSettings();
  639. });
  640.  
  641. $('#fsfb-sect-hotkeys').append(`<p class="hotkey-paragraph">Script Hotkeys</p>`);
  642.  
  643. const checkHotkeyClicked = (e, thing) => {
  644. if (e.target.id == thing.id && !thing.active){
  645. $('#' + thing.id).addClass('selected');
  646. thing.active = true;
  647. keysChanging = true; // prevent features from triggering when setting hotkey
  648. } else {
  649. thing.active = false;
  650. $('#' + thing.id).removeClass('selected');
  651. }
  652. }
  653. const checkNewHotkey = (e, thing) => {
  654. if (!thing.active) return;
  655. thing.key = e.keyCode;
  656. $('#' + thing.id).text(getName(e.keyCode)).removeClass('selected');
  657. thing.active = false;
  658. saveSettings();
  659. styleActiveSettings();
  660. e.preventDefault();
  661. }
  662. const checkFsHotkey = (e, thing) => {
  663. if (!thing.active) return;
  664. thing.key = e.keyCode;
  665. thing.keyName = e.key;
  666. $('#' + thing.id).text(getName(e.keyCode)).removeClass('selected');
  667. thing.active = false;
  668. saveSettings();
  669. styleActiveSettings();
  670. e.preventDefault();
  671. }
  672.  
  673. const checkPowerupClicked = e => {
  674. if(!quickBuying || !e?.originalEvent?.isTrusted || $(e.target).attr('class') == 'purchase-btn confirmation' || $(e.target).attr('class') == 'megaphone-btn') return;
  675. let id = e.target.id;
  676. if (id == 'fsfb-quickbuy-img' || id == 'fsfb-quickbuy') return void(quickBuying = true);
  677. const map = {
  678. Wall: 33,
  679. AntiFreeze: 35,
  680. AntiRecombine: 34,
  681. Shield: 38,
  682. FrozenVirus: 36,
  683. Recombine: 1,
  684. Speed: 2,
  685. Growth: 6,
  686. SpawnVirus: 7,
  687. SpawnMothercell: 8,
  688. SpawnPortal: 9,
  689. SpawnGoldOre: 10,
  690. Freeze: 5,
  691. '360Shot': 30,
  692. minion_nuker: 39,
  693. megaphone_shout: 14,
  694. freeze_yourself: 18
  695. }
  696. let pwID = map[id.replace(/^(inv|fsfb-)/gm, '')] ?? (() => {
  697. quickBuying = false;
  698. $('.inventory-box').removeClass('fsfb-shown').find('p').show();
  699. $('#fsfb-quickbuy').removeClass('activatedInv')
  700. curserMsg('Quick buy deactivated.', 'red');
  701. })();
  702. if (!quickBuying || !pwID) return;
  703. $('.confirm').attr('disabled', 'true'); // disable so user doesn't buy early - swal is slow to load text
  704. const waitUntil1 = (condition) => new Promise(resolve => {
  705. let interval = setInterval(() => {
  706.  
  707. if($('.sweet-alert h2').text() == ' Confirm ') return;
  708.  
  709. $('.confirm')[0].click();
  710. condition() && (clearInterval(interval), resolve());
  711. }, 25);
  712. setTimeout(() => { (clearInterval(interval), resolve()) }, 1e4);
  713. });
  714. setTimeout(() => {
  715. if(pwID == 14) return;
  716. $('.confirm')[0].addEventListener('click', async e => {
  717. if(!e.isTrusted || $('.confirm').text() == 'OK') return;
  718. $('.sweet-alert, .sweet-overlay').addClass('fsfb-hidden');
  719. setTimeout(async() => {
  720. await waitUntil1(() => !$('.sweet-alert').hasClass('visible'));
  721. if($('.sweet-alert').hasClass('visible')) return;
  722. await sleep(100);
  723. $('.sweet-alert, .sweet-overlay').removeClass('fsfb-hidden');
  724. }, 1e3);
  725. })
  726. }, 500);
  727. setTimeout(() => $('.confirm').removeAttr('disabled'), 600);
  728.  
  729. if(pwID == 14) $('.megaphone-btn')[0].click();
  730. else $(`.purchase-btn.confirmation[item="${pwID}"]`)[0].click();
  731.  
  732. $('.inventory-box').removeClass('fsfb-shown').find('p').show();
  733. $('#fsfb-quickbuy').removeClass('activatedInv')
  734. quickBuying = false;
  735. }
  736.  
  737.  
  738. for(let i of settings.hotkeys){
  739. $('#fsfb-sect-hotkeys').append(`<br><p>${i.title}</p><div id="${i.id}" class="fsfb-hotkey"></div>`);
  740. $('#' + i.id).on('contextmenu', e => {
  741. $('#' + i.id).text('').removeClass('selected');
  742. i.active = false;
  743. i.key = 0;
  744. saveSettings();
  745. styleActiveSettings();
  746. e.preventDefault();
  747. });
  748. }
  749.  
  750. // const typing = () => $('input, textarea').is(':focus');
  751. const typing = () => {
  752. const focused = document.querySelector(":focus");
  753. return focused === document.activeElement && ['TEXTAREA', 'INPUT'].includes(focused?.tagName) && (!document.hasFocus || document.hasFocus()) && !!(focused.type || focused.href || ~focused.tabIndex);
  754. };
  755.  
  756. const sleep = ms => new Promise(r => setTimeout(r, ms));
  757. const press = key => ['down', 'up'].forEach(i => unsafeWindow[`onkey${i}`]({ keyCode: key }));
  758.  
  759. let fsWaiting = false;
  760. const fastSplit = async(a) => { // a = true: was fast onesplit; a = false: was fast doublesplit
  761. if(fsWaiting) return;
  762. fsWaiting = true;
  763. setTimeout(() => void(fsWaiting = false), settings.fastsplit_hotkeys[a ? 1 : 4].val + settings.fastsplit_hotkeys[a ? 2 : 5].val);
  764. if([39, 37, 2, 4, 6].includes(currentServerId) || !a || userPreferences.extraOneFastSplitDelay) await sleep(settings.fastsplit_hotkeys[a ? 1 : 4].val);
  765. press(getKey("FreezeSelf"));
  766. await sleep(settings.fastsplit_hotkeys[a ? 2 : 5].val);
  767. press(getKey("FreezeSelf"));
  768. }
  769.  
  770.  
  771.  
  772. let cursorLockActivated = false, holdAutoFeed = false, _onblur;
  773. const waitForBlur = () => {
  774. if(unsafeWindow.onblur != null){
  775. const oldBlur = unsafeWindow.onblur;
  776. unsafeWindow.onblur = function(){
  777. spamRec = spamSpeed = splittingbots = false;
  778. keys = {};
  779. if(!settings.checkboxes[3].active){
  780. $("#linesplit-markers div").hide();
  781. linesplitting = false;
  782. }
  783. return oldBlur.apply(this, arguments);
  784. }
  785. _onblur = unsafeWindow.onblur;
  786. } else {
  787. setTimeout(waitForBlur, 400);
  788. }
  789. };
  790. waitForBlur();
  791.  
  792. // hook for typing in chat w/ cursor lock
  793. const waitForKeyup = () => {
  794. if(unsafeWindow.onkeyup != null){
  795. const _keydown = unsafeWindow.onkeyup;
  796. unsafeWindow.onkeyup = function(){
  797. const keyCode = arguments[0]?.keyCode;
  798. if(arguments[0]?.target?.id === 'chtbox' && arguments[0]?.isTrusted && cursorLockActivated && keyCode === getKey('FixedMouse')) return;
  799. if(arguments[0]?.isTrusted && holdAutoFeed && [getKey('MacroFeed'), getKey('MultiFeed')].includes(keyCode)) return;
  800. return _keydown.apply(this, arguments);
  801. }
  802. } else {
  803. setTimeout(waitForKeyup, 400);
  804. }
  805. };
  806. waitForKeyup();
  807.  
  808. const waitForSetGraphics = () => {
  809. if(unsafeWindow.onkeyup != null){
  810. const _setGraphics = unsafeWindow.setGraphics;
  811. unsafeWindow.setGraphics = (a, b) => userPreferences.preventAutoLowGraphics && b ? $("#curser").hide() : _setGraphics(a, b);
  812. } else {
  813. setTimeout(waitForSetGraphics, 400);
  814. }
  815. };
  816. waitForSetGraphics();
  817.  
  818. const fsfbCustomBlur = function(){
  819. ['MacroSplit', ...(holdAutoFeed ? [] : ['MacroFeed', 'MultiFeed']), ...(cursorLockActivated ? [] : ['FixedMouse'])].forEach(id => unsafeWindow.onkeyup({keyCode: getKey(id)}));
  820. spamRec = spamSpeed = splittingbots = false;
  821. keys = {};
  822. if(!settings.checkboxes[3].active){
  823. $("#linesplit-markers div").hide();
  824. linesplitting = false;
  825. }
  826. }
  827.  
  828. const toggleHoldFeed = active => {
  829. holdAutoFeed = active ?? !holdAutoFeed;
  830. unsafeWindow.onblur = holdAutoFeed || cursorLockActivated ? fsfbCustomBlur : _onblur;
  831. unsafeWindow[`onkey${holdAutoFeed ? 'down' : 'up'}`]({ keyCode: getKey('MacroFeed') });
  832. }
  833.  
  834. const toggleCursorLock = active => {
  835. cursorLockActivated = active ?? !cursorLockActivated;
  836. unsafeWindow.onblur = holdAutoFeed || cursorLockActivated ? fsfbCustomBlur : _onblur;
  837. unsafeWindow[`onkey${cursorLockActivated ? 'down' : 'up'}`]({ keyCode: getKey('FixedMouse') });
  838. }
  839. unsafeWindow.fsfbCursorLock = toggleCursorLock;
  840.  
  841. const toggleSetting = index => {
  842. let el = $('#' + settings.quickSettings[index].set);
  843. if($('#fsfb-settings-main')[0].contains(el[0])) el.prop('checked', !el.prop('checked')).trigger('change');
  844. else el.unbind().click();
  845. }
  846.  
  847. Object.defineProperty(KeyboardEvent.prototype, 'keyCode', {
  848. get: function() {
  849. switch (this.key.toLowerCase()) {
  850. case settings.fastsplit_hotkeys[0]?.keyName.toLowerCase(): return getKey("Split");
  851. case settings.fastsplit_hotkeys[3]?.keyName.toLowerCase(): return getKey("DoubleSplit");
  852. default: return this.which;
  853. }
  854. }
  855. });
  856.  
  857. let slowfeeding = !1, linesplitting = !1, hiddenUI = !1, splittingbots = !1, spamRec = !1, spamSpeed = !1;
  858. const pressed = e => {
  859. const key = e.which ? e.which : e.keyCode;
  860. if(document.activeElement.type === 'textarea') e.stopImmediatePropagation();
  861. if(typing() || keysChanging || e.key == undefined || e.keyCode == undefined) return;
  862. if(key === 27 && quickBuying){ // esc pressed
  863. quickBuying = false;
  864. $('#fsfb-quickbuy').removeClass('activatedInv');
  865. curserMsg('Quick buy deactivated.', 'red');
  866. $('.inventory-box').removeClass('fsfb-shown').find('p').show();
  867. e.preventDefault();
  868. e.stopImmediatePropagation();
  869. }
  870. if(key === settings.hotkeys[0].key){ // 7 feed
  871. let i = 1;
  872. let interval = setInterval(() => {
  873. press(getKey("MacroFeed"));
  874. if(++i > 7) clearInterval(interval);
  875. }, 85);
  876. e.preventDefault();
  877. }
  878. if(key === settings.hotkeys[1].key && !settings.checkboxes[3].active){ // linesplit lock
  879. linesplitting = true;
  880. linesplit();
  881. $("#linesplit-markers div").show();
  882. e.preventDefault();
  883. }
  884. if(key === settings.hotkeys[1].key && settings.checkboxes[3].active){ // linesplit lock
  885. linesplitting = !linesplitting;
  886. if(linesplitting){
  887. $("#linesplit-markers div").show();
  888. linesplit();
  889. } else {
  890. $("#linesplit-markers div").hide();
  891. $('#canvas').trigger($.Event('mousemove', {clientX: mosX, clientY: mosY})); // return mouse to where the cursor is
  892. }
  893. e.preventDefault();
  894. }
  895. if(key === settings.hotkeys[2].key){ // macro split bots
  896. splittingbots = true;
  897. const splittingBots = () => {
  898. if(!splittingbots) return;
  899. press(getKey("SplitBots"));
  900. setTimeout(splittingBots, 50);
  901. }
  902. splittingBots();
  903. e.preventDefault();
  904. }
  905. if(key === settings.hotkeys[3].key){ // hide ui
  906. _replaceCSS('hideUI-css', (hiddenUI = !hiddenUI) ? '.hideUI{ display: none !important; }' : '');
  907. e.preventDefault();
  908. }
  909. if(key === settings.hotkeys[4].key){ // toggle cursor lock
  910. toggleCursorLock();
  911. e.preventDefault();
  912. }
  913. if(key === settings.hotkeys[5].key){ // check profile
  914. const swal = $('.sweet-alert'),
  915. swalText = swal.text();
  916. if((swalText.includes('Level: ') || swalText.includes('The selected player is not logged in or is playing in invisible mode.')) && swalText.includes('Not valid!')) $('button.confirm').click();
  917. if(mouseHoveringChat) $('#chtCanvas')[0].ondblclick({clientX: mosX, clientY: mosY, preventDefault: function(){}});
  918. else {
  919. let contextmenuShown = $('#contextMenu').css('display') == 'block';
  920. let evt = new MouseEvent("contextmenu", {
  921. bubbles: true,
  922. cancelable: true,
  923. view: unsafeWindow,
  924. clientX: mosX,
  925. clientY: mosY
  926. });
  927. document.body.dispatchEvent(evt);
  928. if($('#contextPlayerSkin').css('background-color') == 'rgb(51, 51, 51)') curserMsg('Unable to show profile; no player was clicked on.', 'red');
  929. if(!contextmenuShown) $('#contextMenu').hide();
  930. $('#contextUserProfile').addClass('hover');
  931. $('#contextMenu')[0].onclick({stopPropagation: function(){}});
  932. }
  933. e.preventDefault();
  934. }
  935. if(key === getKey("FixedMouse")){ // real cursor lock key is pressed
  936. cursorLockActivated = false;
  937. unsafeWindow.onblur = _onblur;
  938. }
  939. if(e.keyCode != 0 && e.key.toLowerCase() === settings.fastsplit_hotkeys[0]?.keyName.toLowerCase()){ // fast onesplit
  940. fastSplit(!0);
  941. e.preventDefault();
  942. }
  943. if(e.keyCode != 0 && e.key.toLowerCase() === settings.fastsplit_hotkeys[3]?.keyName.toLowerCase()){ // fast doublesplit
  944. fastSplit(!1);
  945. e.preventDefault();
  946. }
  947. if(key === settings.slowFeed[0].key){ // toggle feed
  948. slowfeeding = !slowfeeding;
  949. const feeding = () => {
  950. if(!slowfeeding) return void(holdAutoFeed && toggleHoldFeed(false));
  951. unsafeWindow?.uisdoa?.foprw?.() || settings.slowFeed[1].val === 0 || press(getKey("MacroFeed"));
  952.  
  953. settings.slowFeed[1].val === 0 ? toggleHoldFeed(slowfeeding) : setTimeout(feeding, settings.slowFeed[1].val);
  954.  
  955. }
  956. feeding();
  957. e.preventDefault();
  958. }
  959. if(key === settings.frozenvirus[0].key){
  960. let fvEl = $('#invFrozenVirus'),
  961. wasActive = fvEl.hasClass('activatedInv');
  962. if(fvEl.css('display') === 'none') return void(curserMsg(`FSFB script can't click on the frozen virus powerup! Frozen viruses are needed before using this hotkey`, 'red'));
  963. curserMsg();
  964.  
  965. wasActive || fvEl[0].onmousedown({ button: 0 });
  966. press(getKey("MacroFeed"));
  967. wasActive || setTimeout(() => fvEl[0].onmousedown({ button: 0 }), settings.frozenvirus[1].val);
  968. }
  969. if(key === settings.quickSettings[0].key){ // quick settings 1
  970. toggleSetting(0);
  971. e.preventDefault();
  972. }
  973. if(key === settings.quickSettings[1].key){ // quick settings 2
  974. toggleSetting(1);
  975. e.preventDefault();
  976. }
  977. if(key === settings.quickSettings[2].key){ // quick settings 3
  978. toggleSetting(2);
  979. e.preventDefault();
  980. }
  981. if(key === settings.quickSettings[3].key){ // quick settings 4
  982. toggleSetting(3);
  983. e.preventDefault();
  984. }
  985. if(key === settings.quickSettings[4].key){ // quick settings 5
  986. toggleSetting(4);
  987. e.preventDefault();
  988. }
  989. if(key === settings.quickSettings[5].key){ // quick settings 6
  990. toggleSetting(5);
  991. e.preventDefault();
  992. }
  993. if(!spamRec && uisdoa && settings.checkboxes[6].active && key === getKey("Recombine")){
  994. spamRec = true;
  995. const spammingRec = () => {
  996. if(!spamRec) return;
  997. // press(getKey("keyRecombine"));
  998. if(unsafeWindow?.uisdoa?.foprc) uisdoa.foprc(1);
  999. setTimeout(spammingRec, 10);
  1000. }
  1001. spammingRec();
  1002. e.preventDefault();
  1003. }
  1004. if(!spamSpeed && uisdoa && settings.checkboxes[6].active && key === getKey("Speed")){
  1005. spamSpeed = true;
  1006. const spammingSpeed = () => {
  1007. if(!spamSpeed) return;
  1008. // press(getKey("keySpeed"));
  1009. if(unsafeWindow?.uisdoa?.foprc) uisdoa.foprc(0);
  1010. setTimeout(spammingSpeed, 10);
  1011. }
  1012. spammingSpeed();
  1013. e.preventDefault();
  1014. }
  1015. }
  1016.  
  1017. const released = key => {
  1018. if(typing() || keysChanging) return;
  1019. if(key == settings.hotkeys[2].key) splittingbots = false; // macro split bots
  1020. if(key == settings.hotkeys[1].key && !settings.checkboxes[3].active){ // linesplit lock
  1021. linesplitting = false;
  1022. $('#canvas').trigger($.Event('mousemove', {clientX: mosX, clientY: mosY})); // return mouse to where the cursor is
  1023. $("#linesplit-markers div").hide();
  1024. }
  1025. if(spamRec && key === getKey("Recombine")) spamRec = false;
  1026. if(spamSpeed && key === getKey("Speed")) spamSpeed = false;
  1027. }
  1028.  
  1029.  
  1030. const slowfeedhotkey = settings.slowFeed[0];
  1031. // add slowfeed HTML
  1032. $('#fsfb-sect-slowfeed').append(`<p class="hotkey-paragraph">Auto-Feed</p>`)
  1033. .append(`<br><p>${slowfeedhotkey.title}</p><div id="${slowfeedhotkey.id}" class="fsfb-hotkey"></div>`)
  1034. .append(`<br><p>${settings.slowFeed[1].title}</p><input id="${settings.slowFeed[1].id}" class="fsfb-hotkey" onkeypress="return onlyNumberKey(event)" maxlength="3"></input>`);
  1035.  
  1036. $('#' + slowfeedhotkey.id).on('contextmenu', e => {
  1037. $('#' + slowfeedhotkey.id).text('').removeClass('selected');
  1038. slowfeedhotkey.active = false;
  1039. slowfeedhotkey.key = 0;
  1040. saveSettings();
  1041. styleActiveSettings();
  1042. e.preventDefault();
  1043. });
  1044.  
  1045. document.getElementById(settings.slowFeed[1].id).addEventListener("keypress", function(e){
  1046. setTimeout(() => { // goes too fast or smth
  1047. settings.slowFeed[1].val = +$('#' + settings.slowFeed[1].id).val();
  1048. saveSettings();
  1049. }, 5);
  1050. });
  1051.  
  1052. // add fastsplit HTML
  1053. $('#fsfb-sect-fastsplit').append(`<p class="hotkey-paragraph">Fast-Split</p>`);
  1054. // for(let i of settings.fastsplit_hotkeys){
  1055. for(let j = 0; j < settings.fastsplit_hotkeys.length; j++){
  1056. let i = settings.fastsplit_hotkeys[j];
  1057. $('#fsfb-sect-fastsplit').append('val' in i ? `<br><p>${i.title}</p><input id="${i.id}" class="fsfb-hotkey" onkeypress="return onlyNumberKey(event)" maxlength="3"></input>` : `<br><p>${i.title}</p><div id="${i.id}" class="fsfb-hotkey"></div>`);
  1058. if('val' in i){
  1059. document.getElementById(i.id).addEventListener("keypress", function(e){
  1060. setTimeout(() => { // goes too fast or smth
  1061. settings.fastsplit_hotkeys[j].val = +$('#' + i.id).val();
  1062. if($('#' + i.id).val() == "") settings.fastsplit_hotkeys[j].val = 0;
  1063. saveSettings();
  1064. }, 5);
  1065. });
  1066. } else {
  1067. $('#' + i.id).on('contextmenu', e => {
  1068. $('#' + i.id).text('').removeClass('selected');
  1069. i.active = false;
  1070. i.key = 0;
  1071. i.keyName = '';
  1072. saveSettings();
  1073. styleActiveSettings();
  1074. e.preventDefault();
  1075. });
  1076. }
  1077. }
  1078. $('#fsfb-sect-frzvrs').append(`<p class="hotkey-paragraph">Frozen-Virus</p>`)
  1079. .append(`<br><p>${settings.frozenvirus[0].title}</p><div id="${settings.frozenvirus[0].id}" class="fsfb-hotkey"></div>`)
  1080. .append(`<br><p>${settings.frozenvirus[1].title}</p><input id="${settings.frozenvirus[1].id}" class="fsfb-hotkey" onkeypress="return onlyNumberKey(event)" maxlength="3"></input>`);
  1081.  
  1082. $('#' + settings.frozenvirus[0].id).on('contextmenu', e => {
  1083. $('#' + settings.frozenvirus[0].id).text('').removeClass('selected');
  1084. settings.frozenvirus[0].active = false;
  1085. settings.frozenvirus[0].key = 0;
  1086. saveSettings();
  1087. styleActiveSettings();
  1088. e.preventDefault();
  1089. });
  1090.  
  1091. document.getElementById(settings.frozenvirus[1].id).addEventListener("keypress", function(e){
  1092. setTimeout(() => { // goes too fast or smth
  1093. settings.frozenvirus[1].val = +$('#' + settings.frozenvirus[1].id).val();
  1094. saveSettings();
  1095. }, 5);
  1096. });
  1097.  
  1098.  
  1099. // add quick settings HTML
  1100. $('#fsfb-sect-quickSettings').append(`<p class="hotkey-paragraph">Quick Settings</p>`);
  1101.  
  1102. for(let i of settings.quickSettings){
  1103. $('#fsfb-sect-quickSettings').append(`<select id="${i.id1}" class="fsfb-quickchange"><option value="cDark">Dark Theme</option><option value="cFancyGrid">Fancy Grid</option><option value="cSectionGrid">Section Grid</option><option value="cGrid">Gridlines</option><option value="cSkins">Skins</option><option value="cWearables">Wearables</option><option value="cNames">Show Names</option><option value="cMinionNames">Minion Names</option><option value="cLargeNames">Large Names</option><option value="cNameOutlines">Name Outline</option><option value="cMass">Show Mass</option><option value="cFood">Show Food</option><option value="cFoodHalf">Half Food</option><option value="cCellAnimations">Cell Anim</option><option value="cSkinAnimations">Skin Anim</option><option value="cMapBorder">Map Border</option><option value="cCustomBack">Custom BG</option><option value="aCustomBack">Sounds</option><option value="cZoom">Infinite Zoom</option><option value="cFixedZoom">Fixed Zoom</option><option value="cSlowMotion">Slow-Motion</option><option value="cMinionUi">Minion Panel</option><option value="cLeaderboard">Leaderboard</option><option value="cChat">Chat</option><option value="cChatSize">Chat Large</option><option value="cMinimap">Minimap</option><option value="cFPS">FPS/Ping</option><option value="cColors">Cell Colors</option><option value="cCellBorders">Cell Borders</option><option value="cCellSpikes">Cell Spikes</option><option value="cClassicViruses">Classic Virus</option><option value="cPolygonShapes">Polygon Cells</option><option value="cLineShapes">Line Cells</option><option value="cBubbleCells">Bubble Cells</option><option value="cVisibilityStatus">Prof Visiblity</option><option value="cAllowPartyInvite">Party Inv</option><option value="cAllowPartyAnimations">Party Anim</option><option value="cIconDRank">Dono Icon</option><option value="cGoldCrownChat">Gold Icon</option><option value="fsfb-revcell">Rev Order</option><option value="fsfb-portalstop">Portals Top</option><option value="fsfb-hideminions">Hide Minions</option><option value="fsfb-nominionskins">No Bot Skins</option><option value="fsfb-hideejected">Hide All Ej.</option><option value="fsfb-hidestaticej">Hide Static Ej.</option><option value="fsfb-nodeathanim">No Death Anim</option><option value="fsfb-hideshouts">Hide Shouts</option><option value="fsfb-tranchat">Chat Trans</option></select><div id="${i.id}" class="fsfb-hotkey"></div>`);
  1104. $('#' + i.id).on('contextmenu', e => {
  1105. $('#' + i.id).text('').removeClass('selected');
  1106. i.active = false;
  1107. i.key = 0;
  1108. saveSettings();
  1109. styleActiveSettings();
  1110. e.preventDefault();
  1111. });
  1112. };
  1113.  
  1114. $('.fsfb-quickchange').on("change", function(){
  1115. settings.quickSettings[+this.id.replace('fsfb-quick-select', '') - 1].set = this.value;
  1116. saveSettings();
  1117. });
  1118.  
  1119. // add UI scaling
  1120. $('#fsfb-sect-uiScale').append(`<p class="hotkey-paragraph">Game Scaling</p>`);
  1121. for(let i of settings.uiScaling){
  1122. $('#fsfb-sect-uiScale').append(`<div class="fsfb-slider"><p>${i.title}</p><input id="${i.id}" class="fsfb-slider" type="range" ${i.id == "fsfb-foodSize" ? `min="0.5" max="1.5" value="1" step="0.1"`: `min="1" max="9" value="5"`}></input></div>`);
  1123. // $('#fsfb-sect-uiScale').append(`<div class="fsfb-slider"><p>${i.title}</p><input id="${i.id}" class="fsfb-slider" type="range" ${i.id == "fsfb-foodSize" ? `min="0.5" max="1.5" value="1" step="0.1"`: `min="1" max="9" value="5"`}></input></div>`);
  1124. $( "#" + i.id).on('input', function() {
  1125. changeSettings(this.id, $(this).val());
  1126. });
  1127. };
  1128.  
  1129.  
  1130. let quickBuying = false;
  1131. ['megaphone_shout', 'minion_nuker', 'freeze_yourself'].forEach(elId => $('#invCloak').after(`<div class="inventory-box" id="fsfb-${elId}" style="display: none;"></div>`));
  1132. $(settings.checkboxes[10] ? '#fsfb-megaphone_shout' : '#inv360Shot').after(`<div class="inventory-box" id="fsfb-quickbuy" style="display: none;"></div>`);
  1133. $('#fsfb-quickbuy').on('click', function(){
  1134. quickBuying = !quickBuying;
  1135. if (quickBuying){
  1136. $(this).addClass('activatedInv');
  1137. curserMsg('Quick buy activated, click the powerup you would like to buy.', 'green');
  1138. $('.inventory-box').addClass('fsfb-shown').find('p').hide();
  1139. $('#invCloak').removeClass('fsfb-shown');
  1140. $('#fsfb-quickbuy').removeClass('fsfb-shown');
  1141. } else {
  1142. $(this).removeClass('activatedInv');
  1143. curserMsg('Quick buy deactivated.', 'red');
  1144. $('.inventory-box').removeClass('fsfb-shown').find('p').show();
  1145. }
  1146. });
  1147.  
  1148.  
  1149.  
  1150. const styleActiveSettings = () => {
  1151.  
  1152. // css selector :has() isn't fully supported at the moment, so gotta use jquery for the time being :(
  1153.  
  1154. // #fsfb-settings-main p:has(+.fsfb-hotkey:not(:empty)) {
  1155. // color:#df901c;
  1156. // }
  1157. // #fsfb-settings-main .fsfb-hotkey:not(:empty) ~ p:has(+input.fsfb-hotkey):not(div.fsfb-hotkey:empty + * + p):not(div.fsfb-hotkey:empty + * + p + * + * + p) {
  1158. // color: #df901c;
  1159. // }
  1160.  
  1161. if(!userPreferences.stylizeActiveSettings) return;
  1162. $('#fsfb-settings-main p:has(+div.fsfb-hotkey), #fsfb-settings-main select:has(+div.fsfb-hotkey)').each(function(){
  1163. $(this)[$(this).is(':has(+.fsfb-hotkey:empty)') ? 'removeClass' : 'addClass']('fsfb-active-setting');
  1164. })
  1165. $('#fsfb-settings-main .fsfb-hotkey~ p:has(+input.fsfb-hotkey)').each(function(){
  1166. $(this)[$(this).is('div.fsfb-hotkey:empty + * + p') || $(this).is('div.fsfb-hotkey:empty + * + p + * + * + p') ? 'removeClass' : 'addClass']('fsfb-active-setting');
  1167. });
  1168. $('input[type="range"].fsfb-slider').each(function(){
  1169. $(this).prev()[this.value == this.getAttribute('value') ? 'removeClass': 'addClass']('fsfb-active-setting');
  1170. })
  1171. }
  1172.  
  1173. // add lag detection settings to things
  1174. let customCells = false;
  1175.  
  1176. $("#fsfb-sect-theme>label>input").addClass('fsfb-ncustom');
  1177. $('#fsfb-powsoverlay, #fsfb-mtchmass').addClass('fsfb-ncustom');
  1178. $('#fsfb-check-Hazard, #fsfb-check-border').removeClass('fsfb-ncustom');
  1179. $("#fsfb-revcell, #fsfb-portalstop, #fsfb-bublecell").removeClass('fsfb-ncustom');
  1180. $('#fsfb-foodSize').prev().addClass('fsfb-lag');
  1181.  
  1182. let antilagCells = false;
  1183.  
  1184. let fillCells = false;
  1185. $('#fsfb-bublecell, #fsfb-showmass, #fsfb-spikedcells, #fsfb-mtchmass, #fsfb-powsoverlay, #fsfb-anticloak, #fsfb-portalmass').addClass('fsfb-lag');
  1186.  
  1187. $('#fsfb-powsoverlay, #fsfb-bublecell, #fsfb-anticloak').addClass('fsfb-nfill');
  1188.  
  1189. let onlyCells = false;
  1190.  
  1191. let strokeCells = false;
  1192. $('#fsfb-bublecell, #fsfb-powsoverlay').addClass('fsfb-nstroke');
  1193.  
  1194. let foodSizeOn = false;
  1195.  
  1196. // let drawCells = false;
  1197. // $('#fsfb-powsoverlay, #fsfb-portalmass').addClass('fsfb-ndraw');
  1198. let customCellsChanged = false;
  1199.  
  1200. let translateChanged = false, pwOverlayChanged = false;
  1201. const changeSettings = (ID, a, e) => { // a = active, e = event (optional)
  1202. for(let i of settings.uiScaling) if(i.id == ID) i.level = a;
  1203. for(let i of settings.checkboxes) if(i.id == ID) i.active = a;
  1204. for(let i of settings.export_import) if(i.id == ID) i.active = a;
  1205. for(let i of settings.theme_boxes) if(i.id == ID) i.active = a;
  1206. for(let i of settings.anti_lag) if(i.id == ID) i.active = a;
  1207. for(let i of settings.chat_translate){
  1208. if(i.id == ID){
  1209. if(ID == "fsfb-tranchat" && (typeof GM_xmlhttpRequest != 'function') && e?.originalEvent?.isTrusted){
  1210. curserMsg('Fsfb script is unable to access the GM_xmlhttpRequest function. Chat translate won\'t work without this. This is often caused by not using the tampermonkey extension (or not the latest version).', 'red', 1e4);
  1211. $('#' + ID).prop('checked', false);
  1212. return;
  1213. }
  1214. translateChanged = true;
  1215. i.active = a;
  1216. }
  1217. }
  1218. for(let i of settings.theme){
  1219. if(i.id == ID) i.active = a;
  1220. if(i.id1 == ID) i.color = a;
  1221. }
  1222.  
  1223. if(ID == "fsfb-hideshouts") $('#megaholder')[`${a ? 'add' : 'remove'}Class`]('hideMegaphone');
  1224. if(ID == "fsfb-qBuy") $('#fsfb-quickbuy')[a ? 'show' : 'hide'](); //.css('display', a ? 'flex' : 'hide');
  1225. if(ID == "fsfb-pwsonerow"){
  1226. $('#fsfb-quickbuy').detach().appendTo(`#inventory${a ? 2 : 1}`);
  1227. [1, 2].forEach((i) => $("#inventory" + i).addClass("fsfb-inventories").css("order", a ? i : -i));
  1228. _replaceCSS("css-invsingleline", a ? `#inventory{ display: flex; position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%); } .fsfb-inventories { position: initial !important; transform: initial !important; }` : '');
  1229. }
  1230.  
  1231. if(ID == "fsfb-bublecell" && $("#cBubbleCells").is(":checked") != a && e?.originalEvent?.isTrusted) $('#cBubbleCells').unbind().click();
  1232. if(ID == "fsfb-showmass" && $("#cMass").is(":checked") != a && e?.originalEvent?.isTrusted) $('#cMass').unbind().click();
  1233.  
  1234. if((ID == "fsfb-myskins" || ID == "fsfb-partyskins") && !$("#cSkins").is(":checked") && a) $('#cSkins').unbind().click();
  1235. if((ID == "fsfb-mynick" || ID == "fsfb-partynicks") && !$("#cNames").is(":checked") && a) $('#cNames').unbind().click();
  1236.  
  1237. if(ID == "fsfb-myskins" && a) $('#fsfb-partyskins').prop('checked', false).trigger('change');
  1238. if(ID == "fsfb-partyskins" && a) $('#fsfb-myskins').prop('checked', false).trigger('change');
  1239.  
  1240. if(ID == "fsfb-mynick" && a) $('#fsfb-partynicks').prop('checked', false).trigger('change');
  1241. if(ID == "fsfb-partynicks" && a) $('#fsfb-mynick').prop('checked', false).trigger('change');
  1242.  
  1243. if(ID == "fsfb-hideejected" && a) $('#fsfb-hidestaticej').prop('checked', false).trigger('change');
  1244. if(ID == "fsfb-hidestaticej" && a) $('#fsfb-hideejected').prop('checked', false).trigger('change');
  1245.  
  1246. if(ID == "fsfb-powsoverlay") svSwitch = true;
  1247.  
  1248. let zoomLvl = "100%";
  1249. let map = {
  1250. 1: 50,
  1251. 2: 70,
  1252. 3: 80,
  1253. 4: 90,
  1254. 5: 100,
  1255. 6: 110,
  1256. 7: 125,
  1257. 8: 150,
  1258. 9: 200
  1259. }
  1260. if(ID == "fsfb-invSize") $('#inventory').css('zoom', (map[+a] ?? 100)+ '%');
  1261. if(ID == "fsfb-statsSize") $('#stats-container').css('zoom', (map[+a] ?? 100) + '%');
  1262.  
  1263.  
  1264. let prevCustomCells = customCells;
  1265.  
  1266. // lag settings
  1267. customCells = $(".fsfb-ncustom").is(":checked") || $('#fsfb-foodSize').val() != '1'; // || userPreferences.whiteBorder4BlackCells;
  1268. fillCells = $('.fsfb-nfill').is(':checked');
  1269. strokeCells = $('.fsfb-nstroke').is(':checked');
  1270. onlyCells = $('#fsfb-myskins, #fsfb-partyskins, #fsfb-mynick, #fsfb-partynicks').is(':checked');
  1271. antilagCells = $("#fsfb-sect-antilag>label>input").not('#fsfb-nodeathanim').is(':checked');
  1272. foodSizeOn = $('#fsfb-foodSize').val() != '1';
  1273.  
  1274. if(prevCustomCells != customCells && e?.originalEvent?.isTrusted) customCellsChanged = true;
  1275. // if(customCellsChanged && e?.originalEvent?.isTrusted) setTimeout(() => (customCellsChanged = false), 25);
  1276. if(customCellsChanged && e?.originalEvent?.isTrusted) setTimeout(() => (customCellsChanged = false), 25);
  1277.  
  1278. styleActiveSettings();
  1279. saveSettings();
  1280. }
  1281.  
  1282. // add event listeners
  1283. let mosX, mosY,
  1284. keys = {},
  1285. keysChanging = false,
  1286. mouseHoveringChat = false;
  1287.  
  1288.  
  1289. $(document).on('click', e => {
  1290. if(e.target.id.includes('fsfb')){
  1291. for(let i = 0; i < settings.hotkeys.length; i++) checkHotkeyClicked(e, settings.hotkeys[i]);
  1292. for(let i = 0; i < settings.fastsplit_hotkeys.length; i++) settings.fastsplit_hotkeys[i].val == null && checkHotkeyClicked(e, settings.fastsplit_hotkeys[i]);
  1293. for(let i = 0; i < settings.quickSettings.length; i++) checkHotkeyClicked(e, settings.quickSettings[i]);
  1294. checkHotkeyClicked(e, settings.slowFeed[0]); // slow feed
  1295. checkHotkeyClicked(e, settings.frozenvirus[0]);
  1296. }
  1297. checkPowerupClicked(e);
  1298. })
  1299. .on('mousemove', e => {
  1300. if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
  1301. mouseHoveringChat = e.target.id == 'chtCanvas';
  1302. ({clientX: mosX, clientY: mosY} = e);
  1303. if (linesplitting) linesplit();
  1304. })
  1305.  
  1306. .on("keydown", e => {
  1307. // if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
  1308. if(keysChanging){
  1309. for(let i = 0; i < settings.hotkeys.length; i++) checkNewHotkey(e, settings.hotkeys[i]);
  1310. for(let i = 0; i < settings.fastsplit_hotkeys.length; i++) checkFsHotkey(e, settings.fastsplit_hotkeys[i]);
  1311. for(let i = 0; i < settings.quickSettings.length; i++) checkNewHotkey(e, settings.quickSettings[i]);
  1312. checkNewHotkey(e, settings.slowFeed[0]); // slow feed
  1313. checkNewHotkey(e, settings.frozenvirus[0]);
  1314. }
  1315. if (!(e.keyCode in keys)){
  1316. keys[e.keyCode] = !0;
  1317. pressed(e);
  1318. }
  1319. keysChanging = false;
  1320. })
  1321. .on("keyup", e => {
  1322. if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
  1323. delete keys[e.keyCode];
  1324. released(e.keyCode);
  1325. if(cursorLockActivated && e.keyCode == getKey("FixedMouse")){
  1326. e.stopImmediatePropagation();
  1327. e.preventDefault();
  1328. }
  1329. });
  1330.  
  1331. let factsArr = typeof facts_list == 'undefined' ? null : facts_list.replace(/^\n|\n$/gm, '').split(/\n/gm);
  1332. const randomFact = index => factsArr && (factsArr[index] ?? factsArr[~~(Math.random() * factsArr.length)]);
  1333.  
  1334.  
  1335. const hashCode = (str, shift) => {
  1336. let hash = 0;
  1337. for (let i = 0; i < str.length; i++) hash = (hash << shift) - hash + str.charCodeAt(i), hash |= 0;
  1338. return str.length == 0 ? 0 : hash;
  1339. };
  1340.  
  1341. const RNG = (min, max) => Math.random() * (max - min) + min;
  1342.  
  1343. // chat cmds
  1344. if(userPreferences.extraChatCommands){
  1345. const $chtbox = $('#chtbox');
  1346. $chtbox.on('keydown', e => {
  1347. if(e.keyCode != 13) return;
  1348.  
  1349. if(userPreferences.messageBanWarning && /a\s*g\s*a\s*r\s*p\s*o\s*w\s*e\s*r/gmi.test($chtbox.val())){
  1350. curserMsg("FSFB WARNING: FSFB script has censored your message as it has detected that your message may've contained a word/phrase that agma.io gives a 24 hour IP ban for. This feature can be disabled in the tampermonkey script settings.", 'red', 1e4);
  1351. $chtbox.val($chtbox.val().replace(/a\s*g\s*a\s*r\s*p\s*o\s*w\s*e\s*r/gmi, match => '*'.repeat(match.length)));
  1352. }
  1353.  
  1354. let newMsg = '', cmdVerif, customCmd = false, firstMatch = $chtbox.val().match(new RegExp(`(?<=((^|(^\/party +)|(^\/pm +\\S+ ))${escapeRegExp(userPreferences.chatPrefix)}))\\S+(\\s+\\S+)*(?=($|\\s+))`, 'gmi'));
  1355. if(firstMatch == null) return;
  1356. let[command, ...params] = firstMatch[0].split(/\s+/g);
  1357. const originalMsg = $chtbox.val().replace(new RegExp(`(?<=((^|(^\/party +)|(^\/pm +\\S+ ))))${escapeRegExp(userPreferences.chatPrefix)}${escapeRegExp(firstMatch[0])}(?=($|\\s+))`, 'gmi'), ''),
  1358. isolatedMsg = firstMatch[0].replace(new RegExp(command + '\\s', 'gi'), '');
  1359. coinsInfo();
  1360. xpInfo();
  1361.  
  1362. /* ~~~ example of external chat cmds (can be used by other scripts) ~~~ */
  1363. /*
  1364. externalChatCmds = [
  1365. {
  1366. commands: ['test1', 'test2', 'test3'],
  1367. callback: (command, isolatedMsg, params) => {
  1368. let newMsg = '';
  1369. return newMsg;
  1370. }
  1371. },
  1372. {
  1373. commands: ['eg1', 'eg2'],
  1374. callback: (cmd, isoMsg, params) => {
  1375. console.log(cmd, isoMsg, params);
  1376. alert('bru')
  1377. return '';
  1378. }
  1379. }
  1380. ]
  1381. */
  1382.  
  1383. let { externalChatCmds } = unsafeWindow;
  1384. if(externalChatCmds?.length){
  1385. for(let i of externalChatCmds){
  1386. if(i.commands.includes(command.toLowerCase())){
  1387. customCmd = true;
  1388. newMsg = i.callback(command.toLowerCase(), isolatedMsg, params);
  1389. }
  1390. }
  1391. }
  1392.  
  1393.  
  1394. if(!customCmd && (cmdVerif ??= true)){
  1395. switch(command.toLowerCase()){
  1396. case 'help':
  1397. $('#fsfb-extra-info')[0].click();
  1398. setTimeout(() => $('.fsfb-modal-body').scrollTop(1850), 200);
  1399. newMsg = '';
  1400. break;
  1401. case 'bots': case 'bot': case 'min': case 'mins': case 'minion': case 'minions': {
  1402. let minInfo = misc_settings.bots?.[currentUser], minsAmt, minsTimeRem;
  1403. $('#infoContent').children().each(function(){
  1404. if($(this).text().includes('Minion Time:')) minsTimeRem = $(this).find('span').text();
  1405. if($(this).text().includes('Minions:')) minsAmt = $(this).find('span').text();
  1406. });
  1407. if((minsChatAmt[currentUser] != null && minsChatAmt[currentUser]?.amt && !minsChatAmt[currentUser]?.started) && (minInfo == null || (minInfo && Date.now() > minInfo.currMs + minInfo.rem))){ /* || (minInfo && Date.now() > minInfo.currMs + minInfo.rem) */
  1408. newMsg = `Minion Pack Unstarted: ${minsChatAmt[currentUser].amt}`;
  1409. } else if(minsChatAmt[currentUser] != null && minsChatAmt[currentUser]?.started && minsChatAmt[currentUser]?.amt && minInfo == null ){
  1410. newMsg = `Minion Pack Activated: ${minsChatAmt[currentUser].amt}`;
  1411. } else if(!(minsAmt == '0' && minsTimeRem == '00:00:00') && minsChatAmt[currentUser] != null && minsChatAmt[currentUser].amt && minInfo != null && (minInfo.active || minsChatAmt[currentUser].started) && minInfo.rem - (Date.now() - minInfo.currMs) > 0){
  1412. newMsg = `Minion Pack Activated: ${minsChatAmt[currentUser].amt}, with ${msToTime(minInfo.rem - (Date.now() - minInfo.currMs))} remaining`;
  1413. } else if(!(minsAmt == '0' && minsTimeRem == '00:00:00') && minsAmt != null && minsTimeRem != null){
  1414. let timeArr = minsTimeRem.split(':'), msBotsTime = (3.6e6 * +timeArr[0]) + (6e4 * +timeArr[1]) + (1e3 * +timeArr[2]);
  1415. newMsg = `Minion Pack Activated: ${minsAmt}, with ${msToTime(msBotsTime)} remaining`;
  1416. } else {
  1417. newMsg = `Minion Pack Activated: none`;
  1418. }
  1419. break;
  1420. }
  1421. case 'pws': case 'pw': case 'powers': case 'power': case 'inv': case 'inventory': {
  1422. curserMsg(`It's recommended to use ${userPreferences.chatPrefix + command}1, ${userPreferences.chatPrefix + command}2, and ${userPreferences.chatPrefix + command}3 instead, so your chat messages aren't cut-off by the chat maxlength`, 'red', 8e3);
  1423. updatePwCount();
  1424. newMsg = 'Inv: ';
  1425. for (let i = 0; i < 6; i++) {
  1426. newMsg = 'Inv: ';
  1427. for (let pw in pws) {
  1428. const check = index => i < index ? ' ' : '';
  1429. if (pws[pw] != '') newMsg += `${pws[pw]}${check(1)}${(i > 2 ? pw.slice(0, 6 - i) : pw)},${check(2)}`;
  1430. }
  1431. newMsg = newMsg.replace(/,[^,]*$/g, '');
  1432. if (newMsg.length <= 100) i = 6;
  1433. }
  1434. if (newMsg == 'Inv: ') newMsg = 'Inv: no powers';
  1435. break;
  1436. }
  1437. case 'totalpws': case 'totalpw': case 'totalpowers': case 'totalpower': case 'total': case 'totpw':
  1438. newMsg = `Total Powerups: ${$('.inventory-box>p').toArray().map(x => +x.innerText).reduce((a, b) => a + b).toLocaleString('en-US')}`
  1439. break;
  1440. case 'xplevel': case 'levelxp': case 'lvlxp': case 'xpcompleted': case 'xp':
  1441. newMsg = `XP Completed: ${currentPercent ? String(Math.round(currentPercent * currentLevel * 1e3, 1)).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "/" + (currentLevel * 1e3).toLocaleString('en-US') : "0/0"}`
  1442. break;
  1443. case 'level': case 'levels': case 'lvls': case 'lvl': case 'lvlcompleted': case 'lvlscompleted':
  1444. newMsg = `Level ${currentLevel}, with ${round(currentPercent * 100, 3)}% completed`
  1445. break;
  1446. case 'coin': case 'coins':
  1447. newMsg = `Coins: ${String(currentCoins).replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`
  1448. break;
  1449. case 'hours': case 'hrs': case 'hour': case 'hr': case 'timeplayed':
  1450. newMsg = (isLogged() ? $('.timePlayed>span').text().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : 'Time Played: 0, not logged in');
  1451. break;
  1452. case 'rank': case 'lvlrank':
  1453. newMsg = `My Rank: ${isLogged() ? $('.ranking.text-left>span').text().match(/(?<=^Your rank: )\d+$/gm)?.[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",") : '∞, not logged in'}`;
  1454. break;
  1455. case 'ping': case 'delay': case 'ms':
  1456. newMsg = `My Ping: ${$('#ping').text().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`
  1457. break;
  1458. case 'fps': case 'frames':
  1459. newMsg = `My FPS: ${$('#fps').text().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`
  1460. break;
  1461. case 'topmass': case 'highscore': case 'highmass': case 'highestmass':
  1462. newMsg = `My Top Mass: ${$('#topMass').text()}`
  1463. break;
  1464. case 'cells': case 'cellcount': case 'cell':
  1465. newMsg = `My Cell Count: ${$('#cellsAmount').text()}`
  1466. break;
  1467. case 'pw1': case 'pws1': case 'power1': case 'powers1': case 'inv1': case 'inventory1':
  1468. newMsg = getPowerMessage(1);
  1469. break;
  1470. case 'pw2': case 'pws2': case 'power2': case 'powers2': case 'inv2': case 'inventory2':
  1471. newMsg = getPowerMessage(2);
  1472. break;
  1473. case 'pw3': case 'pws3': case 'power3': case 'powers3': case 'inv3': case 'inventory3':
  1474. newMsg = getPowerMessage(3);
  1475. break;
  1476. case 'friends': case 'friend': case 'friendsonline': case 'friendonline':
  1477. newMsg = `Friends Online: ${$('#friendsLoggedInAmt').text() + $('#friendsTotalAmt').text()}`;
  1478. break;
  1479. case 'request': case 'requests': case 'friendrequest': case 'friendrequests': case 'friendreq': case 'req':
  1480. newMsg = `Friend Requests: ${$('#friendsRequestsAmt').text() === '' ? 'none' : $('#friendsRequestsAmt').text()}`;
  1481. break;
  1482. case 'gold': case 'goldmem': case 'goldmember': case 'golddays': case 'gm': {
  1483. const obj = accGoldMem[currentUser];
  1484. if(obj?.has == null){
  1485. newMsg = `Days of Goldmember Remaining: none`;
  1486. break;
  1487. }
  1488. newMsg = `Days Of Goldmember Remaining: ${obj.has || obj.days != null ? (obj.days + (obj.days == 1 ? ' Day' : ' Days')) : 'none'}`;
  1489. break;
  1490. }
  1491. case 'alive': case 'alivetime': case 'timealive':
  1492. newMsg = `Time Alive: ${playerIsAlive() ? msToTime(Date.now() - timeAlive) : 'not alive'}`;
  1493. break;
  1494. case 'mass': case 'currentmass':
  1495. newMsg = `Current Mass: ${playerIsAlive() ? currentMass.toLocaleString('en-US') : 'none, not alive'}`;
  1496. break;
  1497. case 'user': case 'username': case 'usr': case 'account': case 'acc':
  1498. newMsg = `My Username: ${currentUser == "Please Login First" ? "logged out" : currentUser}`
  1499. break;
  1500. case 'custom': case 'customs': case 'customskins': case 'totalcustoms':
  1501. if($('#publicSkinsPage').children().length == 0){
  1502. curserMsg(`Please load your custom skins before using this command`, 'red', 6e3);
  1503. newMsg = '';
  1504. break;
  1505. }
  1506. newMsg = `My Total Custom Skins: ${$('[id^="skinCustomImg"').length}, worth ${($('[id^="skinCustomImg"').length * 1e6).toLocaleString('en-US')}`
  1507. break;
  1508. case 'skins': case 'boughtskins': case 'ownedskins': case 'totalskins': {
  1509. if($('#skinsBuy [id^="skinContainer"]').length == 0){
  1510. curserMsg(`Please load your owned skins from the agma.io skin store`, 'red', 6e3);
  1511. newMsg = '';
  1512. break;
  1513. }
  1514. let ownedSkinsArr = [], edSkins = 0, totalValue = 0;
  1515. $('#skinsBuy [id^="skinUseBtn"]').each(function(){
  1516. ownedSkinsArr.push(+$(this)[0].id.match(/\d+$/gm)[0]);
  1517. })
  1518. for(let i of skinsArr){
  1519. if(ownedSkinsArr.includes(i.id)){
  1520. i.price > 2e6 ? ++edSkins : totalValue += i.price;
  1521. }
  1522. }
  1523. newMsg = `My Total Bought Skins: ${$('#skinsBuy [id^="skinUseBtn"').length}, worth ${totalValue.toLocaleString('en-US')} coins (${edSkins} lim. ed.)`;
  1524. break;
  1525. }
  1526. case 'ownedwearables': case 'wearables': case 'wears': case 'totalwearables': case 'totalwears': {
  1527. if(!$('#fsfb-wearsloaded').length){
  1528. curserMsg(`Please load your wearables before using this command`, 'red', 6e3);
  1529. newMsg = ''; // aft
  1530. break;
  1531. }
  1532. let wearsPrice = [0], edSkins = 0;
  1533. $('[id^="wearableUseBtn"]').each(function(){
  1534. if ($(this).parents().eq(0).siblings('p').text() == '(Limited Edition)') edSkins++;
  1535. wearsPrice.push(+$(this).parents().eq(0).siblings('span.win-price').text().replace(/,/g, ''));
  1536. });
  1537. newMsg = `My Total Wearables: ${$('[id^="wearableUseBtn"]').length}, worth ${sigma(wearsPrice).toLocaleString('en-US')} coins (${edSkins} lim. ed.)`;
  1538. break;
  1539. }
  1540. case 'cloak': case 'invis': case 'invisibility': {
  1541. let status, time;
  1542. $('#infoContent>p').each(function(){
  1543. if($(this).text().includes('Cloaked: ')) status = $(this).text().match(/(?<=Cloaked: ).+/gm);
  1544. if($(this).text().includes('Cloak Time: ')) time = $(this).text().match(/(?<=Cloak Time: ).+/gm);
  1545. })
  1546. newMsg = status == null || time == null ? `Cloak: inactive` : `Cloak ${status}, with ${time} remaining`;
  1547. break;
  1548. }
  1549. case 'addfriend': case 'add':
  1550. if(params?.length == 0){
  1551. curserMsg(`Please add a username after the command, such as: ${userPreferences.chatPrefix}${command} Fishyyy`, 'red', 6e3);
  1552. newMsg = '';
  1553. break;
  1554. }
  1555. unsafeWindow.friendAdd(params[0]);
  1556. break;
  1557. case 'partymembers': case 'partymember': case 'party':
  1558. newMsg = $('#partyContent').children().length - 1 > 0 ? `In a party, with ${$('#partyContent').children().length - 1} members` : `Party members: not in a party`;
  1559. break;
  1560. case 'players': case 'ply': case 'plyrs': case 'serverplayers': case 'totalplayers':{
  1561. let plyr_max = [0, 0]; // players[0] | maxplayers[1]
  1562. $('[id^="serverPlayers"]').each(function(){
  1563. plyr_max = plyr_max.map((n, i) => n + +this.innerText.split('/')[i]);
  1564. });
  1565. let serverBots = getServerValue('bots') ?? 0, totalbots = 0;
  1566. for(let server in svInfo){
  1567. totalbots += (svInfo[server].bots ?? 0);
  1568. }
  1569. newMsg = `Server Players: ${$('[id^="serverRow"].active>[id^="serverPlayers"]').text()}${serverBots ? ` (${serverBots} bots)` : ''}, Total Players: ${plyr_max[0].toLocaleString('en-US') + '/' + plyr_max[1].toLocaleString('en-US')} ${totalbots ? `(${totalbots} bots)` : ''}`;
  1570. break;
  1571. }
  1572. case 'server':
  1573. newMsg = `Currently playing in ${$('[id^="serverRow"].active>*').first().text()}`;
  1574. break;
  1575. case 'abil': case 'abils': case 'ability': case 'abilities': { // not finished
  1576. let arr = [], newStr = '';
  1577. $('.checkmark').each(function(){
  1578. if($(this).css('display') == 'block') arr.push($(this).siblings().eq(1).attr('item'));
  1579. })
  1580. if(misc_settings.abil?.[currentUser] != null || arr.length){
  1581. const abil = misc_settings.abil?.[currentUser],
  1582. map = {
  1583. 18: 'Freeze',
  1584. 22: 'Invis Cloak',
  1585. 20: '2x Spawn',
  1586. 23: '2x Exp'
  1587. }
  1588. if(currentUser == "Please Login First"){
  1589. newMsg = `Active Abilities: none`;
  1590. break;
  1591. }
  1592. for(let i of arr) newStr += map[i] + (abil?.[i] != null && 8.64e7 - (Date.now() - abil[i]) > 0 ? (' with ' + msToTime(8.64e7 - (Date.now() - abil[i])) + ' left, ') : ', ');
  1593. newMsg = `Active Abilities: ${newStr == '' ? 'none' : newStr}`;
  1594. newMsg = newMsg.replace(/,[^,]*$/g, '');
  1595. } else {
  1596. newMsg = `Active Abilities: none`;
  1597. }
  1598. break;
  1599. }
  1600. case 'xpproj': case 'xpprojected': case 'projectedxp': case 'projxp': case 'levelprojected': case 'lvlproj': case 'lvlprojected': {
  1601. const projectedHr = lastHrXP.length > 0 ? sigma(getProperty(lastHrXP.slice(-5), "gained")) * 12 : 0;
  1602. newMsg = `Next Hour Projected XP: ${xpStatsInPercentages ? round(convertToPerc(projectedHr, currentLevel) * 100, 2) + '%' : round(projectedHr).toLocaleString('en-US')}`;
  1603. break;
  1604. }
  1605. case 'projcoins': case 'projcoin': case 'projectedcoin': case 'projectedcoins': case 'coinsproj': case 'coinproj': case 'coinprojected': case 'coinsprojected': {
  1606. const projectedHr = lastHrCoins.length > 5 ? Math.round(sigma(getProperty(lastHrCoins.slice(-5), "gained")) * 12) : 0;
  1607. newMsg = `Next Hour Projected Coins: ${projectedHr.toLocaleString('en-US')}`;
  1608. break;
  1609. }
  1610. case 'xpremaining': case 'xprem': case 'remainingxp': case 'remxp': {
  1611. const xpRemaining = currentLevel ? currentLevel * 1e3 - currentPercent * currentLevel * 1e3 : 0,
  1612. newMsg = `XP Remanining: ${xpStatsInPercentages ? round(convertToPerc(xpRemaining, currentLevel) * 100, 2) + '%' : round(xpRemaining).toLocaleString('en-US')}`;
  1613. break;
  1614. }
  1615. case 'coinsremaining': case 'coinsrem': case 'remainingcoins': case 'remcoins': {
  1616. const coinsRemaining = currentCoins ? 25e4 - currentCoins % 25e4 : 0;
  1617. newMsg = `Coins Remanining: ${coinsRemaining.toLocaleString('en-US')}`;
  1618. break;
  1619. }
  1620. case 'xplasthour': case 'lasthourxp': case 'hrxp': case 'xphr': case 'hourxp': case 'xphour': {
  1621. const lastHr = lastHrXP.length > 0 ? sigma(getProperty(lastHrXP, "gained")) : 0;
  1622. newMsg = `XP Last Hour: ${xpStatsInPercentages ? round(convertToPerc(lastHr, currentLevel) * 100, 2) + '%' : round(lastHr).toLocaleString('en-US')}`;
  1623. break;
  1624. }
  1625. case 'coinslasthour': case 'lasthourcoins': case 'hrcoins': case 'coinshr': case 'hourcoins': case 'coinshour': {
  1626. const lastHr = lastHrCoins.length > 0 ? Math.round(sigma(getProperty(lastHrCoins, "gained"))) : 0
  1627. newMsg = `Coins Last Hour: ${lastHr.toLocaleString('en-US')}`;
  1628. break;
  1629. }
  1630. case 'xplastmin': case 'xplastminute': case 'lastminxp': case 'lastminutexp': case 'minxp': case 'minutexp': case 'xpmin': {
  1631. const lastMin = lastMinXP.length > 0 ? sigma(getProperty(lastMinXP, "gained")) : 0;
  1632. newMsg = `XP Last Minute: ${xpStatsInPercentages ? round(convertToPerc(lastMin, currentLevel) * 100, 2) + '%' : round(lastMin).toLocaleString('en-US')}`;
  1633. break;
  1634. }
  1635. case 'coinslastmin': case 'coinslastminute': case 'lastmincoins': case 'lastminutecoins': case 'coinsxp': case 'minutecoins': case 'coinsmin': {
  1636. const lastMin = lastMinCoins.length > 0 ? Math.round(sigma(getProperty(lastMinCoins, "gained"))) : 0;
  1637. newMsg = `Coins Last Minute: ${lastMin.toLocaleString('en-US')}`;
  1638. break;
  1639. }
  1640. case 'xplast12s': case 'xplast12sec': case 'xplast12seconds': case 'last12secondsxp': case 'lastminutexp': case 'xp12s': case '12sxp': case 'xp12sec': {
  1641. const last12sec = lastMinXP.length > 0 ? lastMinXP[lastMinXP.length - 1].gained : 0;
  1642. newMsg = `XP Last 12 Seconds: ${xpStatsInPercentages ? round(convertToPerc(last12sec, currentLevel) * 100, 2) + '%' : round(last12sec).toLocaleString('en-US')}`;
  1643. break;
  1644. }
  1645. case 'xpsession': case 'sessionxp': case 'xpsesh': case 'sesh': case 'seshxp': case 'online': {
  1646. const sessionXP = currentXP && accounts[currentUser] ? currentXP - accounts[currentUser].xp : 0;
  1647. newMsg = `Session XP: ${xpStatsInPercentages ? round(currentLevel && accounts[currentUser] ? ((round(currentPercent, 3) + currentLevel) - accounts[currentUser].lvl) * 100 : 0, 2) + '%' : round(sessionXP).toLocaleString('en-US')}, Session Length: ${msToTime(Date.now() - scriptStartXP)}`;
  1648. break;
  1649. }
  1650. case 'lifetimexp': case 'xplifetime':
  1651. newMsg = `Lifetime XP: ${round(currentXP ?? 0).toLocaleString('en-US')}`;
  1652. break;
  1653. case 'coinssession': case 'sessioncoins': case 'coinssesh': case 'seshcoins': {
  1654. const sessionCoins = currentCoins && accounts[currentUser] ? Math.round(currentCoins - accounts[currentUser].coins): 0;
  1655. newMsg = `Session Coins: ${sessionCoins.toLocaleString('en-US')}, Session Length: ${msToTime(Date.now() - scriptStartCoins)}`;
  1656. break;
  1657. }
  1658. case 'ratewaifu': case 'waifu': case 'waifurating': case 'waifurate': case 'howwaifu':
  1659. cmdVerif = false;
  1660. if(params?.length == 0){
  1661. if(currentUser == "Please Login First"){
  1662. newMsg = `My Waifu Rating: 0 / 10`;
  1663. break;
  1664. }
  1665. newMsg = `My Waifu Rating: ${Math.round(+String(Math.round(hashCode(currentUser, 5) * 102.2)).slice(-3, -1) / 10)} / 10`;
  1666. break;
  1667. }
  1668. newMsg = `${params[0]}'s Waifu Rating: ${Math.round(+String(Math.round(hashCode(params[0], 5) * 102.2)).slice(-3, -1) / 10)} / 10`;
  1669. break;
  1670. case 'ratepro': case 'pro': case 'prorating': case 'prorate': case 'howpro':
  1671. cmdVerif = false;
  1672. if(params?.length == 0){
  1673. if(currentUser == "Please Login First"){
  1674. newMsg = `I am 0% pro`;
  1675. break;
  1676. }
  1677. newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 5) * 194.3)).slice(-4, -1) / 10)}% pro`;
  1678. break;
  1679. }
  1680. newMsg = `${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 5) * 194.3)).slice(-4, -1) / 10)}% pro`;
  1681. break;
  1682. case 'ratedog': case 'dog': case 'dograting': case 'dograte': case 'howdog':
  1683. cmdVerif = false;
  1684. if(params?.length == 0){
  1685. if(currentUser == "Please Login First"){
  1686. newMsg = `I am 0% dog`;
  1687. break;
  1688. }
  1689. newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 6) * 189.3)).slice(-4, -1) / 10)}% dog`;
  1690. break;
  1691. }
  1692. newMsg = `${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 6) * 189.3)).slice(-4, -1) / 10)}% dog`;
  1693. break;
  1694. case 'rateking': case 'king': case 'kingrating': case 'kingrate': case 'howking':
  1695. cmdVerif = false;
  1696. if(params?.length == 0){
  1697. if(currentUser == "Please Login First"){
  1698. newMsg = `I am 0% king`;
  1699. break;
  1700. }
  1701. newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 5) * 389.3)).slice(-4, -1) / 10)}% king`;
  1702. break;
  1703. }
  1704. newMsg = `${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 5) * 389.3)).slice(-4, -1) / 10)}% king`;
  1705. break;
  1706. case 'dice': case 'die': case 'roll': case 'rolldice': {
  1707. const sides = params?.length == 0 || isNaN(+params[0]) ? 6 : +params[0];
  1708. newMsg = `Rolled a dice with ${sides.toLocaleString('en-US')} sides, landed on ${Math.round(RNG(1, sides)).toLocaleString('en-US')}`;
  1709. break;
  1710. }
  1711. case 'rng': case 'random': case 'randomnumber': case 'number': case 'num': {
  1712. const min = params?.length < 1 || isNaN(+params[0]) ? 0 : +params[0];
  1713. const max = params?.length < 2 || isNaN(+params[1]) ? min + 10 : +params[1];
  1714. if(min > max){
  1715. curserMsg(`Please make sure your minimum (${min}) is less than your maximum (${max})`, 'red', 5e3);
  1716. newMsg = '';
  1717. break;
  1718. }
  1719. newMsg = `Generated a random number between ${min.toLocaleString('en-US')} and ${max.toLocaleString('en-US')}, chose: ${Math.round(RNG(min, max)).toLocaleString('en-US')}`;
  1720. break;
  1721. }
  1722. case 'ratefriends': case 'friendsrating': case 'friendsrate': case 'howfriends':
  1723. cmdVerif = false;
  1724. if(params?.length == 0){
  1725. if(currentUser == "Please Login First"){
  1726. newMsg = `I am 0% friends with a frog`;
  1727. break;
  1728. }
  1729. newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 4) * 283.7)).slice(-4, -1) / 10)}% friends with a frog`;
  1730. break;
  1731. }
  1732. if(params?.length == 1){
  1733. if(currentUser == "Please Login First"){
  1734. newMsg = `I am 0% friends with a ${params[0]}`;
  1735. break;
  1736. }
  1737. newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 4) * 283.7) + Math.round((hashCode(params[0], 5) * 405.2))).slice(-4, -1) / 10)}% friends with a ${params[0]}`;
  1738. break;
  1739. } else {
  1740. newMsg = `${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 4) * 283.7) + Math.round((hashCode(params[0], 5) * 405.2))).slice(-4, -1) / 10)}% friends with ${params[1]}`;
  1741. break;
  1742. }
  1743. case 'rateenemies': case 'enemiesrating': case 'enemiesrate': case 'howenemies': case 'enemies':
  1744. cmdVerif = false;
  1745. if(params?.length == 0){
  1746. if(currentUser == "Please Login First"){
  1747. newMsg = `I am 0% enemies with a frog`;
  1748. break;
  1749. }
  1750. newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 4) * 164.45)).slice(-5, -2) / 10)}% enemies with a frog`;
  1751. break;
  1752. }
  1753. if(params?.length == 1){
  1754. if(currentUser == "Please Login First"){
  1755. newMsg = `I am 0% enemies with a ${params[0]}`;
  1756. break;
  1757. }
  1758. newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 4) * 164.45) + Math.round((hashCode(params[0], 5) * 405.2))).slice(-5, -2) / 10)}% enemies with a ${params[0]}`;
  1759. break;
  1760. } else {
  1761. newMsg = `${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 4) * 164.45) + Math.round((hashCode(params[0], 5) * 405.2))).slice(-5, -2) / 10)}% enemies with ${params[1]}`;
  1762. break;
  1763. }
  1764. case 'flip': case 'coinflip': case 'heads': case 'tails': case 'flipcoin': {
  1765. newMsg = `Coin flipped! Landed on ${Math.round(RNG(0, 1)) ? 'heads' : 'tails'}`;
  1766. break;
  1767. }
  1768. case 'script': case 'version': case 'fsfb':
  1769. newMsg = `Using fsfb script! Version ${version}`
  1770. break;
  1771. case 'time': case 'localtime': case 'localetime': case 'date':
  1772. newMsg = `My Time: ${new Date().toLocaleString()}`
  1773. break;
  1774. case 'leaderboard': case 'leader': case 'lb':
  1775. newMsg = `My leaderboard position: ${!playerIsAlive() || leaderboardPos == null ? 'none, not alive' : leaderboardPos}`;
  1776. break;
  1777. case 'altcaps': case 'altcap': case 'altscaps': case 'altscap':
  1778. newMsg = isolatedMsg.toLowerCase().split('').map((v, i) => i % 2 == 0 ? v : v.toUpperCase()).join('');
  1779. cmdVerif = false;
  1780. break;
  1781. case 'sparkles': case 'sprk':
  1782. newMsg = '✨' + isolatedMsg + '✨';
  1783. cmdVerif = false;
  1784. break;
  1785. case 'cfix': case 'canvasfix': {
  1786. document.querySelectorAll('canvas').forEach(canvas => ({ innerWidth: canvas.width, innerHeight: canvas.height } = unsafeWindow));
  1787. // const canvas = document.querySelector('#canvas');
  1788. // ({ innerWidth: canvas.width, innerHeight: canvas.height } = unsafeWindow);
  1789. curserMsg('Canvas was reset.', 'green', 5e3);
  1790. newMsg = '';
  1791. break;
  1792. }
  1793. case 'solo': case 'soloserver': // message from Miracle Scripts (another great agma script) - https://greasyfork.org/scripts/391142
  1794. newMsg = ':warning: SOLO SERVER :warning: No teaming!! No hay equipo!! Pas d\'équipe!! Kein Teaming!! لا فريق';
  1795. cmdVerif = false;
  1796. break;
  1797. case 'facts': case 'fact': case 'funfact': {
  1798. const fact = randomFact();
  1799. newMsg = fact ? randomFact() : '';
  1800. fact ?? curserMsg(`Fsfb is unable to access the list of facts. Possible cause: not using tampermonkey or not using the latest version of tampermonkey.`, 'red', 6e3);
  1801. break;
  1802. }
  1803. case 'fastsplit': case 'fs': case 'fastsplits': case 'fastsplitdelay': case 'delay': {
  1804. const fs = settings.fastsplit_hotkeys;
  1805. newMsg = `My fast onesplit delays: ${fs[1].val}, ${fs[2].val}; Fast double delays: ${fs[4].val}, ${fs[5].val}`;
  1806. break;
  1807. }
  1808. case "dailyprogress": case "dailychallenge": case "progress": case "challengeprogress": case "challenge": case "challenges": {
  1809. let progress = +$("#challengeProgress").text();
  1810. let goal = +$("#challengeGoal").text();
  1811. let percent = ((progress / goal) * 100).toFixed(1);
  1812. newMsg = `Daily Challenge Progress: ${progress.toLocaleString('en-US')} / ${goal.toLocaleString('en-US')} (${percent}% completed)`;
  1813. break;
  1814. }
  1815. case "sp": case "sorapoint": case "sorapoints": case "sps": {
  1816. let soraPoints = +$('.sora-points-inv.chall-mythic').text();
  1817. newMsg = `Sora Points: ${soraPoints.toLocaleString('en-US')}`;
  1818. break;
  1819. }
  1820. case "keys": case "key": case "challengekeys": case "challengekey": {
  1821. let challengeKeys = +$('.mystery-keys.chall-mythic').text();
  1822. newMsg = `Challenge Keys: ${challengeKeys.toLocaleString('en-US')}`;
  1823. break;
  1824. }
  1825. case "stars": case "challengestars": case "challengestar": case "star": {
  1826. let challengeStars = +$('.challenge-stars.chall-mythic').text();
  1827. newMsg = `Challenge Stars: ${challengeStars.toLocaleString('en-US')}`;
  1828. break;
  1829. }
  1830. default:
  1831. newMsg = $chtbox.val();
  1832. return;
  1833. }
  1834. }
  1835. // (unsafeWindow.kfjsdafl != null) ? $chtbox.val(newMsg) : $chtbox.val(''), console.log(newMsg);;
  1836. $chtbox.val(newMsg == '' ? '' : originalMsg + (newMsg?.[cmdVerif ? 'replace' : '']?.(/[youaie](?!.: )/gmi, m => cmap[m]) || newMsg));
  1837. })
  1838. }
  1839.  
  1840. // check if player is alive
  1841. let playerAlive = false, timeAlive;
  1842. const playerIsAlive = () => playerAlive && $('#advert').css('display') != 'none' ? false : playerAlive;
  1843. unsafeWindow.playerIsAlive = playerIsAlive;
  1844.  
  1845. const _setNick = unsafeWindow.setNick
  1846. unsafeWindow.setNick = function(){
  1847. if(userPreferences.messageBanWarning && /a\s*g\s*a\s*r\s*p\s*o\s*w\s*e\s*r/gmi.test(arguments[0])){
  1848. curserMsg("FSFB WARNING: FSFB script has censored your message as it has detected that your message may've contained a word/phrase that agma.io gives a 24 hour IP ban for. This feature can be disabled in the tampermonkey script settings.", 'red', 1e4);
  1849. const newNick = (arguments[0].replace(/a\s*g\s*a\s*r\s*p\s*o\s*w\s*e\s*r/gmi, match => '*'.repeat(match.length)));
  1850. arguments[0] = newNick;
  1851. document.getElementById('nick').value = newNick;
  1852. }
  1853. if(arguments[1] || !playerAlive) timeAlive = Date.now();// respawned
  1854. playerAlive = true;
  1855. setTimeout(() => { svSwitch = true }, 1000);
  1856. return _setNick.apply(this, arguments);
  1857. }
  1858. const _closeAdvert = unsafeWindow.closeAdvert
  1859. unsafeWindow.closeAdvert = function(){
  1860. playerAlive = false;
  1861. return _closeAdvert.apply(this, arguments);
  1862. }
  1863.  
  1864. const getPowerMessage = line => {
  1865. let obj = {};
  1866. [1, 2, 3].forEach(n => (obj[`string${n}`] = `Inv (${n}/?): `));
  1867. updatePwCount();
  1868. const newPws = {
  1869. Recombine: pws.rec,
  1870. Speed: pws.spd,
  1871. Growth: pws.grw,
  1872. Virus: pws.vrs,
  1873. Mothercell: pws.mtcl,
  1874. Portal: pws.prtl,
  1875. 'Gold Block': pws.gblk,
  1876. Freeze: pws.fz,
  1877. Push: pws.psh,
  1878. Wall: pws.wall,
  1879. 'Anti-Freeze': pws.afz,
  1880. 'Anti-Recombine': pws.arc,
  1881. Shield: pws.shld,
  1882. 'Frozen Virus': pws.fvs
  1883. }
  1884. for(let i in newPws){
  1885. let add = newPws[i] == '' ? '' : `${newPws[i]} ${i}, `;
  1886. if(obj.string1.length + add.length <= 100) obj.string1 += add;
  1887. else if(obj.string2.length + add.length <= 100) obj.string2 += add;
  1888. else obj.string3 += add;
  1889. }
  1890. if(obj.string1 == `Inv (1/?): ` && line == 1) return 'Inv (1/1): no powers';
  1891. if(obj['string' + line] == `Inv (${line}/?): `){
  1892. curserMsg(`This inventory line doesn't exist! Try a smaller number`, 'red', 6e3);
  1893. return '';
  1894. }
  1895. let totalLines = 1;
  1896. if(obj.string3 == 'Inv (3/?): ' && obj.string2 != 'Inv (2/?): ') totalLines = 2;
  1897. else if(obj.string3 != 'Inv (3/?): ' && obj.string2 != 'Inv (2/?): ') totalLines = 3;
  1898. return obj['string' + line].replace(/,[^,]*$/g, '').replace(/\?/g, totalLines);
  1899. }
  1900.  
  1901. let pws = {rec: '', spd: '', grw: '', vrs: '', mtcl: '', prtl: '', gblk: '', fz: '', psh: '', wall: '', afz: '', arc: '', shld: '', fvs: ''};
  1902. const updatePwCount = () => {
  1903. $('.inventory-box').each(function(){
  1904. const map = {
  1905. Wall : 'wall',
  1906. AntiFreeze: 'afz',
  1907. AntiRecombine: 'arc',
  1908. Shield: 'shld',
  1909. FrozenVirus: 'fvs',
  1910. Recombine: 'rec',
  1911. Speed: 'spd',
  1912. Growth: 'grw',
  1913. SpawnVirus: 'vrs',
  1914. SpawnMothercell: 'mtcl',
  1915. SpawnPortal: 'prtl',
  1916. SpawnGoldOre: 'gblk',
  1917. Freeze: 'fz',
  1918. '360Shot': 'psh'
  1919. }
  1920. if(map[$(this)[0]?.id.slice(3)] != null) pws[map[$(this)[0].id.slice(3)]] = $(this).css('display') != 'none' ? $(this).children().eq(0).text() || '1' : '';
  1921. })
  1922. }
  1923.  
  1924. $('#fsfb-export-btn').on('click', () => {
  1925. if(!$('#fsfb-sect-imexport>label>input').is(':checked')) return void(curserMsg('You need to select at least one setting to export!', 'red'));
  1926. if($('#fsfb-custom-bg').is(':checked')) curserMsg('Exporting settings with a custom background is likely to increase the file size', 'red', 8e3);
  1927. let script_settings = {},
  1928. script_themes = {};
  1929. for (let i in settings) { // put the settings & themes into different objects
  1930. if (i == "theme" || i == "theme_boxes") script_themes[i] = settings[i];
  1931. else if (i != "export_import") script_settings[i] = settings[i];
  1932. }
  1933. let bgImg = null;
  1934. const downloadFile = () => {
  1935. let obj = {
  1936. selected: {
  1937. game_settings: $('#fsfb-game-settings').is(':checked'),
  1938. game_controls: $('#fsfb-game-controls').is(':checked'),
  1939. game_background: $('#fsfb-custom-bg').is(':checked'),
  1940. script_settings: $('#fsfb-script-settings').is(':checked'),
  1941. script_theme: $('#fsfb-theme-settings').is(':checked')
  1942. },
  1943. game_settings: localStorage.settings,
  1944. game_controls: localStorage.hotkeys,
  1945. game_background: bgImg,
  1946. script_settings: script_settings,
  1947. script_theme: script_themes
  1948. }
  1949. // if the setting is turned off, then dont need to export it - set it to null
  1950. for(let i in obj){
  1951. if(i != 'selected' && !obj.selected[i]) obj[i] = null;
  1952. }
  1953. const a = document.createElement('a'),
  1954. file = new Blob([JSON.stringify(obj)], {type: "text/plain"});
  1955. a.href = URL.createObjectURL(file);
  1956. a.download = "fsfb script settings";
  1957. a.click();
  1958. URL.revokeObjectURL(a.href); // download a txt file of exported settings
  1959. }
  1960. if($('#fsfb-custom-bg').is(':checked')){
  1961. let db, req = unsafeWindow.indexedDB.open("AgmaDB", 1);
  1962. req.onsuccess = function (x) {
  1963. db = req.result;
  1964. db.onclose = (c) => { db = null; }
  1965. db.onversionchange = (c) => { db.close(), db = null; }
  1966. let _ = db.transaction("general", "readonly").objectStore("general").get("cbgDataURL");
  1967. _.onsuccess = () => {
  1968. bgImg = _.result;
  1969. downloadFile();
  1970. }
  1971. }, req.onupgradeneeded = function (c) {
  1972. let req = c.target.result;
  1973. if (!req.objectStoreNames.contains("general")) {
  1974. req.createObjectStore("general");
  1975. downloadFile();
  1976. } else downloadFile();
  1977. };
  1978. } else {
  1979. downloadFile();
  1980. }
  1981. });
  1982.  
  1983. $('#fsfb-import-btn').on('click', () => {
  1984. if(!$('#fsfb-sect-imexport>label>input').is(':checked')) return void(curserMsg('You need to select at least one setting to import!', 'red'));
  1985. if($('#fsfb-custom-bg').is(':checked')) curserMsg('Pasting in settings that include a background image is likely to cause initial lag', 'red', 0);
  1986. swal({
  1987. title: "Import Settings",
  1988. text: "Add your exported settings below and press OK to continue.",
  1989. type: "input",
  1990. showCancelButton: true,
  1991. closeOnConfirm: false,
  1992. animation: "slide-from-top",
  1993. inputPlaceholder: "Paste exported settings here"
  1994. },
  1995. function(inputVal){
  1996. if (inputVal == null) return false;
  1997. if (inputVal == "") {
  1998. swal.showInputError("Please don't leave the input empty!");
  1999. return false
  2000. }
  2001. try {
  2002. let val = JSON.parse(inputVal);
  2003. if(val.selected.script_settings && $('#fsfb-script-settings').is(':checked')){
  2004. for (let i in val.script_settings) {
  2005. for (let j in val.script_settings[i]) {
  2006. for(let x in settings){
  2007. for(let y in settings[x]){
  2008. if(val.script_settings[i][j].id == settings[x][y].id) settings[x][y] = val.script_settings[i][j];
  2009. }
  2010. }
  2011. }
  2012. }
  2013. }
  2014. if(val.selected.script_theme && $('#fsfb-theme-settings').is(':checked')){
  2015. for (let i in val.script_theme) {
  2016. for (let j in val.script_theme[i]) {
  2017. for(let x in settings){
  2018. for(let y in settings[x]){
  2019. if(val.script_theme[i][j].id == settings[x][y].id) settings[x][y] = val.script_theme[i][j];
  2020. }
  2021. }
  2022. }
  2023. }
  2024. }
  2025. if(val.selected.game_settings && $('#fsfb-game-settings').is(':checked')) localStorage.setItem('settings', val.game_settings);
  2026. if(val.selected.game_controls && $('#fsfb-game-controls').is(':checked')) localStorage.setItem('hotkeys', val.game_controls);
  2027. if(val.selected.game_background && $('#fsfb-custom-bg').is(':checked')){
  2028. let db, req = unsafeWindow.indexedDB.open("AgmaDB", 1);
  2029. req.onsuccess = function (x) {
  2030. db = req.result;
  2031. db.onclose = (c) => { db = null; }
  2032. db.onversionchange = (c) => { db.close(), db = null; }
  2033. let _ = db.transaction("general", "readwrite").objectStore("general").put(val.game_background, "cbgDataURL");
  2034. }, req.onupgradeneeded = function (c) {
  2035. let req = c.target.result;
  2036. if (!req.objectStoreNames.contains("general")) {
  2037. req.createObjectStore("general");
  2038. }
  2039. };
  2040. }
  2041. // if any agma controls were imported, need to refresh bc it's set using localstorage
  2042. if((val.selected.game_settings && $('#fsfb-game-settings').is(':checked')) || (val.selected.game_controls && $('#fsfb-game-controls').is(':checked')) || (val.selected.game_background && $('#fsfb-custom-bg').is(':checked'))){
  2043. swal({
  2044. title: "Please Refresh!",
  2045. text: "Refreshing the page is required for your imported agma settings to take effect",
  2046. type: "warning",
  2047. });
  2048. } else swal({ title: "Settings Successfully Imported!", type: "success" });
  2049. saveSettings();
  2050. styleActiveSettings();
  2051. updateScriptSettingsUI();
  2052. } catch (error){
  2053. swal({
  2054. title: "Something went wrong!",
  2055. text: "Please make sure you've entered in valid settings",
  2056. type: "error"
  2057. });
  2058. }
  2059. });
  2060.  
  2061. });
  2062.  
  2063. $('#fsfb-settings-right').append(`<div class="fa fa-2x fa-info-circle" id="fsfb-extra-info" data-toggle="modal" data-target=".fsfb-bug-modal"></div>`)
  2064.  
  2065. if(userPreferences.hideAds){
  2066. localStorage.ad_l_time = "9e99"; // smth like time since last ad
  2067. $('[id^="agma-io_"], [id^="adWrapper"], #preroll').addClass("fsfb-removeAds"); // move ads way off the screen
  2068. } else if(localStorage.ad_l_time == "9e99")localStorage.ad_l_time = Date.now();
  2069.  
  2070. let {innerWidth: width, innerHeight: height} = unsafeWindow;
  2071. $(unsafeWindow).on('resize', () => ({innerWidth: width, innerHeight: height} = unsafeWindow));
  2072.  
  2073. let pointMove;
  2074. const linesplit = () => {
  2075. if(!linesplitting) return;
  2076. let closest, points = [{n: "top", x: width / 2, y: 0, nx: width / 2, ny: -10e6}, {n: "bottom", x: width / 2, y: height, nx: width / 2, ny: 10e6}, {n: "left", x: 0, y: height / 2, nx: -10e6, ny: height / 2}, {n: "right", x: width, y: height / 2, nx: 10e6, ny: height / 2}];
  2077. if(userPreferences.linesplitClosestSide){
  2078. let closestSide = [mosY / height, (height - mosY) / height, mosX / width, (width - mosX) / width]; // top, bottom, left, right
  2079. closest = points[closestSide.indexOf(Math.min(...closestSide))];
  2080. } else {
  2081. let distance = p => Math.sqrt(Math.pow(mosX - p.x, 2) + Math.pow(mosY - p.y, 2)),
  2082. closestPoint = points.reduce((a, b) => distance(a) < distance(b) ? a : b);
  2083. for (let i = 0; i < points.length; i++) {
  2084. if (closestPoint.x == points[i].x && closestPoint.y == points[i].y) closest = points[i]
  2085. }
  2086. }
  2087. pointMove = {x: closest.nx, y: closest.ny};
  2088. $('#canvas').trigger($.Event('mousemove', { clientX: closest.nx, clientY: closest.ny }));
  2089. $("#linesplit-markers div").css('background-color', 'transparent');
  2090. $('#linesplit-' + closest.n).css('background-color', '#e25615');
  2091. }
  2092.  
  2093. let confBtns = document.getElementsByClassName('purchase-btn confirmation');
  2094. const btnsArr = Array.from(document.getElementsByClassName('purchase-btn confirmation'));
  2095.  
  2096. const changeHTML = (index, price, id, name) => {
  2097. setTimeout(() => {
  2098. const amtDropdown = document.getElementById('shopAmountDropdown')
  2099. document.getElementsByClassName('sweet-alert showSweetAlert')[0].childNodes[7].firstChild.textContent = 'If you click "Buy", you will purchase this item. It will cost ' + price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' in total.';
  2100. const dropdownChange = () => {
  2101. if (amtDropdown.value == "custom") return buyCstmAmt(price, id, name);
  2102. let priceSum = (amtDropdown.value * price).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  2103. document.getElementsByClassName('sweet-alert showSweetAlert')[0].childNodes[7].firstChild.textContent = 'If you click "Buy", you will purchase this item. It will cost ' + priceSum + ' in total.';
  2104. };
  2105. amtDropdown.addEventListener('change', dropdownChange);
  2106. }, 50);
  2107. };
  2108. const buyCstmAmt = (price, id, name) => {
  2109. swal({
  2110. title: "Enter Purchase Amount",
  2111. text: "<span>How many <b>" + name + "</b> would you like to buy?</span>",
  2112. html: true,
  2113. type: "input",
  2114. showCancelButton: true,
  2115. closeOnConfirm: false,
  2116. inputPlaceholder: "Input amount here"
  2117. },
  2118. function(input){
  2119. let pwAmt = +input,
  2120. priceTotal = pwAmt * price;
  2121. if (input == null) return false;
  2122. if (input == "") {
  2123. swal.showInputError("Please don't leave the input empty!");
  2124. return false
  2125. }
  2126. if(!/^[0-9]+$/.test(input) || !(+input >>> 0 === parseFloat(+input))){
  2127. swal.showInputError("Please only enter positive integers!");
  2128. return false
  2129. }
  2130. swal({
  2131. title: ' Confirm ',
  2132. text: '<p>If you click "Buy", you will purchase this item. It will cost ' + priceTotal.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' in total.<br><small>You chose to purchase ' + pwAmt.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' <b>' + name + '</b></small></p>',
  2133. html: true,
  2134. type: 'warning',
  2135. showCancelButton: true,
  2136. closeOnConfirm: true,
  2137. confirmButtonColor: '#4caf51',
  2138. confirmButtonText: 'Yes, confirm purchase',
  2139. cancelButtonText: 'No, cancel purchase'
  2140. }, function(confirmed){
  2141. if(confirmed && $('.confirm').is(':visible')) buyPw(id, pwAmt);
  2142. })
  2143.  
  2144. });
  2145. };
  2146. // buy a certain amount of powers including > 255
  2147. const buyPw = (shopID, pwAmt) => {
  2148. if (pwAmt < 1) return;
  2149. if (pwAmt > 255) {
  2150. for (let i = 0; i < ~~(pwAmt / 255); i++) setTimeout(() => purchaseItem(shopID, 255), 500 * i + 250);
  2151. purchaseItem(shopID, pwAmt % 255);
  2152. } else purchaseItem(shopID, pwAmt);
  2153. };
  2154.  
  2155.  
  2156. if(userPreferences.improvedShop){
  2157. // add event listeners to dropdown buttons
  2158. for (let i = 0; i < confBtns.length; i++) {
  2159. confBtns[i].onclick = function () {
  2160. setTimeout(() => {
  2161. if (document.getElementById('shopAmountDropdown') != null) document.getElementById('shopAmountDropdown').innerHTML += '<option value="20">20</option> <option value="50">50</option> <option value="100">100</option> <option value="250">250</option> <option value="custom">Pick</option>';
  2162. }, 5);
  2163. }
  2164. }
  2165. // add event listeners to shop buttons
  2166. for (let i = 0; i < 16; i++) {
  2167. if (i != 10 && i != 11){
  2168. btnsArr[i].addEventListener('click', function(e){
  2169. if(!e.isTrusted && !quickBuying) return;
  2170. let index = i;
  2171. changeHTML(index, btnsArr[index].getAttribute('price'), btnsArr[index].getAttribute('item'), $('.purchase-btn.confirmation[item="' + btnsArr[index].getAttribute('item') + '"]').prev().find('h3').eq(0).text());
  2172. });
  2173. }
  2174. }
  2175. }
  2176.  
  2177.  
  2178. // listen for when a min pack is bought
  2179. let minBoughtAmt = {};
  2180. if(userPreferences.extraChatCommands){
  2181. let lastClickedPrice, currentPrice, lastID;
  2182. const confirmClicked = () => {
  2183. setTimeout(() => {
  2184. if($('.sweet-alert h2').text() != "Success!" || lastClickedPrice != currentPrice) return;
  2185. const map = {
  2186. 1: '10 Bots 1 Hour',
  2187. 2: '40 Bots 1 Hour',
  2188. 3: '50 Bots 2 Hours',
  2189. 4: '80 Bots 1 Hour',
  2190. 5: '100 Bots 4 Hours',
  2191. 6: '125 Bots 8 Hours',
  2192. 7: '300 Bots 24 Hours',
  2193. 8: '100 MASS Bots 1 Hour',
  2194. 9: '100 Bots 24 Hours',
  2195. 10: '125 Bots 48 Hours',
  2196. 11: '300 Bots 72 Hours',
  2197. 12: '100 MASS Bots 24 Hours'
  2198. }
  2199. minBoughtAmt[currentUser] = {...minsChatAmt[currentUser], ... {chatAmt: map[lastID], started: false}};
  2200. }, 900);
  2201. };
  2202. $('.purchase-btn2.confirm-minion[item]').on('click', function () {
  2203. lastClickedPrice = this.getAttribute('price').replace(/,/g, '');
  2204. lastID = this.getAttribute('item');
  2205. $('.confirm').attr('disabled', 'true'); // disable so user doesn't buy early - swal is slow to load text
  2206. setTimeout(() => {
  2207. $('.confirm').removeAttr('disabled');
  2208. currentPrice = $('.sweet-alert.showSweetAlert.visible p').eq(0).text().replace(/\D+/g, '');
  2209. }, 750);
  2210. setTimeout(() => $('.confirm')[0].addEventListener('click', confirmClicked), 500);
  2211. })
  2212. }
  2213.  
  2214. // context menu: click on skin -> skin ID to clipboard, click on name -> name to clipboard
  2215. $('#contextPlayer').on('click', e => {
  2216. if(!userPreferences.rightClickCopyInfo) return;
  2217. if($('#contextPlayerSkin').width() + $('#contextPlayerSkin').offset().left + 5 > e.pageX){ // bcs #contextMenu event :dog:
  2218. // cell was clicked
  2219. if($('#contextPlayerSkin').css('background-image') != "none"){
  2220. let skinID = $('#contextPlayerSkin').css('background-image').match(/(?<=\/skins\/)[0-9]+/gm)[0]; // get the skin image, then match only the skin's ID
  2221. navigator.clipboard.writeText(skinID).then(function() {
  2222. curserMsg('Skin ID of ' + skinID + ' was copied to your clipboard.', 'green');
  2223. }, function() {
  2224. curserMsg('Something went wrong. Nothing was added to your clipboard.', 'red');
  2225. });
  2226. } else if($('#contextPlayerSkin').css('background-color') == 'rgb(51, 51, 51)') curserMsg('No player selected. Nothing was added to your clipboard.', 'red');
  2227. else curserMsg('No skin equipped. Nothing was added to your clipboard.', 'red');
  2228. } else { // name was clicked
  2229. if($('#contextPlayerSkin').css('background-color') == 'rgb(51, 51, 51)') curserMsg('No player selected. Nothing was added to your clipboard.', 'red');
  2230. else {
  2231. navigator.clipboard.writeText($('#contextPlayerName').text()).then(function() {
  2232. curserMsg('Nickname: "' + $('#contextPlayerName').text() + '" was copied to your clipboard', 'green');
  2233. }, function() {
  2234. curserMsg("Something went wrong. Nothing was added to your clipboard.", "red");
  2235. });
  2236. }
  2237. }
  2238. });
  2239.  
  2240.  
  2241. // little bar at the top of the screen that tells u stuff
  2242. let curserTimeout;
  2243. function curserMsg(msg, color, time){
  2244. if(color == "green") color = "rgb(0, 192, 0)";
  2245. if(color == "red") color = "rgb(255, 0, 0)";
  2246. if(color == "gray") color = "rgb(153, 153, 153)";
  2247. clearTimeout(curserTimeout);
  2248. $('#curser').text(msg).show().css('color', color)
  2249. if(time != 0) curserTimeout = setTimeout(() => $('#curser').fadeOut(400), time ?? 4e3);
  2250. }
  2251. unsafeWindow.curserMsg = curserMsg;
  2252.  
  2253. let minTimeRemaining = 0;
  2254. const startMin = unsafeWindow.strMin;
  2255. unsafeWindow.strMin = function(){
  2256. minsStart();
  2257. return startMin.apply(this, arguments);
  2258. }
  2259. let minsChatAmt = {},
  2260. accGoldMem = {};
  2261. const minsStart = async() => {
  2262. let minAmt, minsStarted = false;
  2263. const waitUntil1 = (condition) => new Promise(resolve => {
  2264. let interval = setInterval(() => {
  2265. $('#infoContent').children().each(function(){
  2266. if($(this).text().includes('Minion Time:')) minsStarted = true;
  2267. })
  2268. condition() && (clearInterval(interval), resolve());
  2269. }, 100);
  2270. setTimeout(() => { (clearInterval(interval), resolve()); }, 1e4);
  2271. });
  2272. await waitUntil1(() => minsStarted == true);
  2273. if(!minsStarted) return;
  2274. $('#infoContent').children().each(function(){
  2275. if($(this).text().includes('Minion Time:')) minTimeRemaining = $(this).find('span').text();
  2276. if($(this).text().includes('Minions:')) minAmt = $(this).find('span').text();
  2277. })
  2278. let timeArr = minTimeRemaining.split(':'), msBotsTime = (3.6e6 * +timeArr[0]) + (6e4 * +timeArr[1]) + (1e3 * +timeArr[2]);
  2279. misc_settings.bots[currentUser] = {...misc_settings.bots[currentUser], ...{active: true, amt: minAmt, chatAmt: null, rem: msBotsTime, currMs: Date.now()}};
  2280. setStorage("fsfb-misc", misc_settings);
  2281. }
  2282.  
  2283. // ability time remaining
  2284. if(userPreferences.showRemainingAbilityTime){
  2285. let lastClickedPrice, currentPrice, lastID;
  2286. const confirmClicked = () => {
  2287. setTimeout(() => {
  2288. if($('.sweet-alert h2').text() != "Success!" || lastClickedPrice != currentPrice) return;
  2289. misc_settings.abil[currentUser] = {...misc_settings.abil[currentUser], ...{[lastID] : Date.now()}};
  2290. setStorage("fsfb-misc", misc_settings);
  2291. }, 900);
  2292. };
  2293. [18, 20, 22, 23].forEach(el => {
  2294. let h5 = $(`.purchase-btn.confirmation[item="${el}"]`).parents().eq(0).find('div h5');
  2295. h5.clone().insertAfter(h5).addClass('fsfb-fake').hide();
  2296. $(`.purchase-btn[item="${el}"]`).on('click', function () {
  2297. lastClickedPrice = this.getAttribute('price');
  2298. lastID = this.getAttribute('item');
  2299. $('.confirm').attr('disabled', 'true'); // disable so user doesn't buy early - swal is slow to load text
  2300. setTimeout(() => {
  2301. $('.confirm').removeAttr('disabled');
  2302. currentPrice = $('.sweet-alert.showSweetAlert.visible p').eq(0).text().replace(/\D+/g, '');
  2303. }, 750);
  2304. setTimeout(() => $('.confirm')[0].addEventListener('click', confirmClicked), 500);
  2305. })
  2306. });
  2307. }
  2308.  
  2309. // sort wearables by owned
  2310. const waitUntil = condition => new Promise(resolve => {
  2311. let interval = setInterval(() => {
  2312. condition() && (clearInterval(interval), resolve());
  2313. }, 100);
  2314. setTimeout(() => { (clearInterval(interval), resolve()) }, 15e3);
  2315. });
  2316. if(userPreferences.sortWearablesByOwned){
  2317. $('#wearablesTab').on('click', async() => {
  2318. await waitUntil(() => $('#phpWearables li').length > 55);
  2319. if($('#phpWearables li').length <= 55) return;
  2320. $($('[id^="wearableUseBtn"]').get().reverse()).each(function(){
  2321. $($(this).parents().get(2)).insertBefore($("#phpWearables li:eq(0)"));
  2322. })
  2323. $('#phpWearables').append('<div id="fsfb-wearsloaded"></div>');
  2324. });
  2325. }
  2326. // https://stackoverflow.com/questions/2424191/how-do-i-make-an-element-draggable-in-jquery
  2327. $.fn.draggable = function(){
  2328. var $this = this,
  2329. ns = 'draggable_'+(Math.random()+'').replace('.',''),
  2330. mm = 'mousemove.'+ns,
  2331. mu = 'mouseup.'+ns,
  2332. $w = $(unsafeWindow),
  2333. isFixed = ($this.css('position') === 'fixed'),
  2334. adjX = 0, adjY = 0;
  2335.  
  2336. $this.mousedown(function(ev){
  2337. var pos = $this.offset();
  2338. if (isFixed) {
  2339. adjX = $w.scrollLeft(); adjY = $w.scrollTop();
  2340. }
  2341. var ox = (ev.pageX - pos.left), oy = (ev.pageY - pos.top);
  2342. $this.data(ns,{ x : ox, y: oy });
  2343. $w.on(mm, function(ev){
  2344. ev.preventDefault();
  2345. ev.stopPropagation();
  2346. if (isFixed) {
  2347. adjX = $w.scrollLeft(); adjY = $w.scrollTop();
  2348. }
  2349. var offset = $this.data(ns);
  2350. $this.css({left: Math.round(ev.pageX - adjX - offset.x), top: Math.round(ev.pageY - adjY - offset.y)});
  2351. });
  2352. $w.on(mu, function(){
  2353. $w.off(mm + ' ' + mu).removeData(ns);
  2354. });
  2355. });
  2356. return this;
  2357. };
  2358.  
  2359.  
  2360. /* xp/coins statistics */
  2361.  
  2362. const updateTimeXP = 12e3; // xp bar updates every 12 seconds, don't change
  2363. let lastMinXP = [];
  2364. let lastHrXP = [];
  2365. let currentPercent, currentLevel, currentXP, currentCoins;
  2366.  
  2367. unsafeWindow.logStatsScriptXP = !1;
  2368. unsafeWindow.logStatsScriptCoins = !1;
  2369. let scriptStartCoins = Date.now();
  2370. let scriptStartXP = Date.now();
  2371. let accounts = {};
  2372. const guiDisplay = "none";
  2373. let coinsHTMLactive = false;
  2374.  
  2375. const updateTimeCoins = 6e3;
  2376. let lastMinCoins = [];
  2377. let lastHrCoins = [];
  2378.  
  2379. // add stats box html
  2380. const statsBody = document.createElement('div');
  2381. statsBody.setAttribute('id', 'stats-container');
  2382. statsBody.style.display = guiDisplay;
  2383. statsBody.innerHTML = `
  2384. <div id="stats-main">
  2385. <div id="stats-title">
  2386. <title id="stats-extra-info">XP Stats - Updating Every 12s</title>
  2387. <div>
  2388. <div title="Toggle Percentages Shown" id="stats-perc-btn" class="fa fa-percent"></div>
  2389. <div title="Toggle Stats Shown" id="stats-change-shown" class="fa fa-eye-slash"></div>
  2390. <div title="Reset Stats" class="fa fa-refresh" id="stats-reset-btn"></div>
  2391. </div>
  2392. </div>
  2393. <div>
  2394. <table id="stats-table">
  2395. <tbody>
  2396. <tr>
  2397. <td><label><input type="checkbox">Lvl Completed:</label></td>
  2398. <td>0/0</td>
  2399. </tr>
  2400. <tr>
  2401. <td><label><input type="checkbox">Remaining:</label></td>
  2402. <td>100,000</td>
  2403. </tr>
  2404. <tr>
  2405. <td><label><input type="checkbox">Projected (hr):</label></td>
  2406. <td>0</td>
  2407. </tr>
  2408. <tr>
  2409. <td><label><input type="checkbox">Last Hour:</label></td>
  2410. <td>100,000</td>
  2411. </tr>
  2412. <tr>
  2413. <td><label><input type="checkbox">Last Minute:</label></td>
  2414. <td>100,000</td>
  2415. </tr>
  2416. <tr>
  2417. <td><label><input type="checkbox">Last 12s:</label></td>
  2418. <td>100,000</td>
  2419. </tr>
  2420. <tr>
  2421. <td><label><input type="checkbox">Minute Mean:</label></td>
  2422. <td>100,000</td>
  2423. </tr>
  2424. <tr>
  2425. <td><label><input type="checkbox">Minute Median:</label></td>
  2426. <td>100,000</td>
  2427. </tr>
  2428. <tr>
  2429. <td><label><input type="checkbox">Minute Sd:</label></td>
  2430. <td>100,000</td>
  2431. </tr>
  2432. <tr>
  2433. <td><label><input type="checkbox">Latest Outliers:</label></td>
  2434. <td>100,000</td>
  2435. </tr>
  2436. <tr>
  2437. <td><label><input type="checkbox">Session XP:</label></td>
  2438. <td>100,000</td>
  2439. </tr>
  2440. <tr>
  2441. <td><label><input type="checkbox">Session Length:</label></td>
  2442. <td>100,000</td>
  2443. </tr>
  2444. <tr>
  2445. <td><label><input type="checkbox">Lifetime XP:</label></td>
  2446. <td>100,000</td>
  2447. </tr>
  2448. </tbody>
  2449. </table>
  2450. </div>
  2451. </div>`;
  2452. document.querySelector('body').append(statsBody);
  2453.  
  2454. $("#stats-container").draggable();
  2455. let statsboxPos = misc_settings?.statsPos ?? {top: $("#stats-container")[0].style.top, left: $("#stats-container")[0].style.left};
  2456. if(userPreferences.saveStatsBoxPosition && statsboxPos) $("#stats-container").css({'top' : statsboxPos.top, 'left' : statsboxPos.left});
  2457.  
  2458. const convertToPerc = (xpAmt, lvl = 0) => isNaN(xpAmt / (lvl * 1000)) ? 0 : xpAmt / (lvl * 1000);
  2459.  
  2460. $('#stats-reset-btn').on('click', () => {
  2461. if(coinsHTMLactive){
  2462. lastMinCoins = [];
  2463. lastHrCoins = [];
  2464. for(let i in accounts) accounts[i].coins = 0;
  2465. if(accounts[currentUser] !== null && currentUser !== 'Please Login First') accounts[currentUser].coins = currentCoins;
  2466. scriptStartCoins = Date.now();
  2467. } else {
  2468. lastMinXP = [];
  2469. lastHrXP = [];
  2470. for(let i in accounts){
  2471. accounts[i].xp = 0;
  2472. accounts[i].lvl = 0;
  2473. }
  2474. if(accounts[currentUser] !== null && currentUser !== 'Please Login First'){
  2475. accounts[currentUser].xp = currentXP;
  2476. accounts[currentUser].lvl = round(currentPercent, 3) + currentLevel;
  2477. }
  2478. scriptStartXP = Date.now();
  2479. }
  2480. updateUI();
  2481. });
  2482. let xpStatsInPercentages = false;
  2483. $('#stats-perc-btn').on('click', () => {
  2484. xpStatsInPercentages = !xpStatsInPercentages;
  2485. updateUI();
  2486. });
  2487.  
  2488. let changingShownStats = false;
  2489. $('#stats-change-shown').on('click', () => {
  2490. changingShownStats = !changingShownStats;
  2491. if(changingShownStats){
  2492. $('#stats-table tr').show();
  2493. _replaceCSS('stats-input-css', '#stats-table input{ display: unset; }');
  2494. } else {
  2495. for(let i in misc_settings.statsSettings[coinsHTMLactive ? 'coins' : 'xp']){
  2496. misc_settings.statsSettings[coinsHTMLactive ? 'coins' : 'xp'][i] = $(`#stats-${i} input[type="checkbox"]`).is(':checked');
  2497. $('#stats-' + i)[$(`#stats-${i} input[type="checkbox"]`).is(':checked') ? 'show' : 'hide']();
  2498. }
  2499. setStorage("fsfb-misc", misc_settings);
  2500. _replaceCSS('stats-input-css', '#stats-table input{ display: none; }');
  2501. }
  2502. });
  2503.  
  2504. class StatLog {
  2505. constructor(val, lvl, user, arr) {
  2506. this.type = this.findWhich(arr); // mostly for debugging
  2507. this.id = this.getID(arr);
  2508. this.user = user;
  2509. this.amount = val;
  2510. this.gained = this.calcGain(val, arr, this.id);
  2511. this.lvl = lvl;
  2512. // this.timestamp = Date.now(); // mostly for debugging
  2513. }
  2514. findWhich(arr) {
  2515. return ((arr == lastMinXP || arr == lastHrXP ? "xp " : "coins " ) + (arr == lastHrXP || arr == lastHrCoins ? "hour" : "minute"));
  2516. }
  2517. calcGain(val, arr, id){
  2518. const prevObj = arr[arr.length - 1];
  2519. if(prevObj && arr == lastHrXP && id == 1) return round(sigma(getProperty(lastMinXP, "gained")), 3);
  2520. if(prevObj && arr == lastHrCoins && id == 1) return round(sigma(getProperty(lastMinCoins, "gained")), 3);
  2521. return prevObj && val - prevObj.amount >= 0 && prevObj.user == this.user && prevObj.amount != 0 ? round(val - prevObj.amount, 3) : 0;
  2522. }
  2523. getID(arr) {
  2524. return arr[arr.length - 1] ? arr[arr.length - 1].id + 1 : 1;
  2525. }
  2526.  
  2527. }
  2528.  
  2529. const xpInfo = () => {
  2530. currentPercent = $('.progress-bar[role=progressbar]')[0].style.width.slice(0, -1) / 100;
  2531. currentLevel = +$('#level.user-level')[0].textContent;
  2532. currentXP = (levelSum(currentLevel) + currentLevel * currentPercent) * 1e3;
  2533. }
  2534. const coinsInfo = () => {
  2535. currentCoins = +($('#coinsDash')[0].textContent.replace(/ /g, ''));
  2536. }
  2537.  
  2538. const updateUI = () => {
  2539. if(changingShownStats) return;
  2540. if(!coinsHTMLactive){
  2541. const xpHr = lastHrXP.length,
  2542. xpMin = lastMinXP.length,
  2543. lvlCompleted = currentPercent ? String(Math.round(currentPercent * currentLevel * 1e3, 1)).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "/" + (currentLevel * 1e3).toLocaleString('en-US') : "0/0",
  2544. xpRemaining = currentLevel ? currentLevel * 1e3 - currentPercent * currentLevel * 1e3 : 0,
  2545. projectedHr = xpHr > 0 ? sigma(getProperty(lastHrXP.slice(-5), "gained")) * 12 : 0,
  2546. lastHr = xpHr > 0 ? sigma(getProperty(lastHrXP, "gained")) : 0,
  2547. lastHrCompleted = xpHr > 0 ? xpHr : 0,
  2548. lastMin = xpMin > 0 ? sigma(getProperty(lastMinXP, "gained")) : 0,
  2549. lastMinCompleted = xpMin > 0 ? xpMin : 0,
  2550. lastMinTotal = 6e4 / updateTimeXP,
  2551. last12sec = xpMin > 0 ? lastMinXP[lastMinXP.length - 1].gained : 0,
  2552. xBar = xpHr > 0 ? mean(getProperty(lastHrXP, "gained")) : 0,
  2553. xTilde = xpHr > 0 ? median(getProperty(lastHrXP, "gained")) : 0,
  2554. standardDev = xpHr > 0 ? standardDeviation(getProperty(lastHrXP, "gained")) : 0,
  2555. outliers = xpHr > 0 ? checkOutliers(getProperty(lastHrXP, "gained")) : 0,
  2556. sessionXP = currentXP && accounts[currentUser] ? currentXP - accounts[currentUser].xp : 0,
  2557. sessionLength = msToTime(Date.now() - scriptStartXP),
  2558. lifetimeXP = currentXP ? Math.round(currentXP) : 0,
  2559. updateTime = updateTimeXP;
  2560. document.getElementById('stats-table').innerHTML = `
  2561. <tbody>
  2562. <tr id="stats-lvlcomp">
  2563. <td><label><input type="checkbox">Lvl Completed:</label></td>
  2564. <td>${lvlCompleted.toLocaleString('en-US')}</td>
  2565. </tr>
  2566. <tr id="stats-rem">
  2567. <td><label><input type="checkbox">Remaining:</label></td>
  2568. <td>${xpStatsInPercentages ? round(convertToPerc(xpRemaining, currentLevel) * 100, 2) + '%' : round(xpRemaining).toLocaleString('en-US')}</td>
  2569. </tr>
  2570. <tr id="stats-projhr">
  2571. <td><label><input type="checkbox">Projected (hr):</label></td>
  2572. <td>${xpStatsInPercentages ? round(convertToPerc(projectedHr, currentLevel) * 100, 2) + '%' : round(projectedHr).toLocaleString('en-US')}</td>
  2573. </tr>
  2574. <tr id="stats-lasthr">
  2575. <td><label><input type="checkbox">Last Hour:</label></td>
  2576. <td>${xpStatsInPercentages ? round(convertToPerc(lastHr, currentLevel) * 100, 2) + '%' : round(lastHr).toLocaleString('en-US')}<span class="stats-completed">(${lastHrCompleted}/60)</span></td>
  2577. </tr>
  2578. <tr id="stats-lastmin">
  2579. <td><label><input type="checkbox">Last Minute:</label></td>
  2580. <td>${xpStatsInPercentages ? round(convertToPerc(lastMin, currentLevel) * 100, 2) + '%' : round(lastMin).toLocaleString('en-US')}<span class="stats-completed">(${lastMinCompleted + "/" + lastMinTotal})</span></td>
  2581. </tr>
  2582. <tr id="stats-lastsec">
  2583. <td><label><input type="checkbox">Last 12s:</label></td>
  2584. <td>${xpStatsInPercentages ? round(convertToPerc(last12sec, currentLevel) * 100, 2) + '%' : round(last12sec).toLocaleString('en-US')}</td>
  2585. </tr>
  2586. <tr id="stats-mean">
  2587. <td><label><input type="checkbox">Minute Mean:</label></td>
  2588. <td>${xpStatsInPercentages ? round(convertToPerc(xBar, currentLevel) * 100, 2) + '%' : round(xBar).toLocaleString('en-US')}</td>
  2589. </tr>
  2590. <tr id="stats-median">
  2591. <td><label><input type="checkbox">Minute Median:</label></td>
  2592. <td>${xpStatsInPercentages ? round(convertToPerc(xTilde, currentLevel) * 100, 2) + '%' : round(xTilde).toLocaleString('en-US')}</td>
  2593. </tr>
  2594. <tr id="stats-sd">
  2595. <td><label><input type="checkbox">Minute Sd:</label></td>
  2596. <td>${xpStatsInPercentages ? round(convertToPerc(standardDev, currentLevel) * 100, 2) + '%' : round(standardDev).toLocaleString('en-US')}</td>
  2597. </tr>
  2598. <tr id="stats-sesh">
  2599. <td><label><input type="checkbox">Session XP:</label></td>
  2600. <td>${xpStatsInPercentages ? round(currentLevel && accounts[currentUser] ? ((round(currentPercent, 3) + currentLevel) - accounts[currentUser].lvl) * 100 : 0, 2) + '%' : round(sessionXP).toLocaleString('en-US')}</td>
  2601. </tr>
  2602. <tr id="stats-seshlength">
  2603. <td><label><input type="checkbox">Session Length:</label></td>
  2604. <td id="stats-sesh-length">${sessionLength}</td>
  2605. </tr>
  2606. <tr id="stats-lifetime">
  2607. <td><label><input type="checkbox">Lifetime XP:</label></td>
  2608. <td>${lifetimeXP.toLocaleString('en-US')}</td>
  2609. </tr>
  2610. </tbody>`;
  2611. for(let i in misc_settings.statsSettings.xp){
  2612. $('#stats-' + i)[misc_settings.statsSettings.xp[i] ? 'show' : 'hide']();
  2613. $('#stats-' + i + ' input[type="checkbox"').prop("checked", misc_settings.statsSettings.xp[i]);
  2614. }
  2615. $('#stats-extra-info').text(`XP Stats - Updating Every ${updateTime / 1e3}s`).css('color', '#00bbff');
  2616. $('#stats-sesh-length').text(msToTime(Date.now() - scriptStartXP));
  2617. $('#stats-perc-btn').show();
  2618. } else {
  2619. const coinsHr = lastHrCoins.length,
  2620. coinsMin = lastMinCoins.length,
  2621. coinGoalCompleted = currentCoins ? currentCoins.toLocaleString('en-US') + "/" + Math.ceil(currentCoins / 25e4).toLocaleString('en-US') * 25e4 : 0,
  2622. coinsRemaining = currentCoins ? 25e4 - currentCoins % 25e4 : 0,
  2623. projectedHr = coinsHr > 5 ? Math.round(sigma(getProperty(lastHrCoins.slice(-5), "gained")) * 12) : 0,
  2624. lastHr = coinsHr > 0 ? Math.round(sigma(getProperty(lastHrCoins, "gained"))) : 0,
  2625. lastHrCompleted = coinsHr > 0 ? coinsHr : 0,
  2626. lastMin = coinsMin > 0 ? Math.round(sigma(getProperty(lastMinCoins, "gained"))) : 0,
  2627. lastMinCompleted = coinsMin > 0 ? coinsMin : 0,
  2628. lastMinTotal = 6e4 / updateTimeCoins,
  2629. last12sec = coinsMin > 0 ? getProperty(lastHrCoins.slice(-5), "amount")[0] : 0,
  2630. xBar = coinsHr > 0 ? Math.round(mean(getProperty(lastHrCoins, "gained"))) : 0,
  2631. xTilde = coinsHr > 0 ? Math.round(median(getProperty(lastHrCoins, "gained"))) : 0,
  2632. standardDev = coinsHr > 0 ? Math.round(standardDeviation(getProperty(lastHrCoins, "gained"))) : 0,
  2633. outliers = coinsHr > 0 ? checkOutliers(getProperty(lastHrCoins, "gained")) : 0,
  2634. sessionXP = currentCoins && accounts[currentUser] ? Math.round(currentCoins - accounts[currentUser].coins): 0,
  2635. sessionLength = msToTime(Date.now() - scriptStartCoins),
  2636. updateTime = updateTimeCoins;
  2637. document.getElementById('stats-table').innerHTML = `
  2638. <tbody>
  2639. <tr id="stats-rem">
  2640. <td><label><input type="checkbox">Remaining:</label></td>
  2641. <td>${coinsRemaining.toLocaleString('en-US')}</td>
  2642. </tr>
  2643. <tr id="stats-projhr">
  2644. <td><label><input type="checkbox">Projected (hr):</label></td>
  2645. <td>${projectedHr.toLocaleString('en-US')}</td>
  2646. </tr>
  2647. <tr id="stats-lasthr">
  2648. <td><label><input type="checkbox">Last Hour:</label></td>
  2649. <td>${lastHr.toLocaleString('en-US')}<span class="stats-completed">(${lastHrCompleted}/60)</span></td>
  2650. </tr>
  2651. <tr id="stats-lastmin">
  2652. <td><label><input type="checkbox">Last Minute:</label></td>
  2653. <td>${lastMin.toLocaleString('en-US')}<span class="stats-completed">(${lastMinCompleted + "/" + lastMinTotal})</span></td>
  2654. </tr>
  2655. <tr id="stats-mean">
  2656. <td><label><input type="checkbox">Minute Mean:</label></td>
  2657. <td>${xBar.toLocaleString('en-US')}</td>
  2658. </tr>
  2659. <tr id="stats-median">
  2660. <td><label><input type="checkbox">Minute Median:</label></td>
  2661. <td>${xTilde.toLocaleString('en-US')}</td>
  2662. </tr>
  2663. <tr id="stats-sd">
  2664. <td><label><input type="checkbox">Minute Sd:</label></td>
  2665. <td>${standardDev.toLocaleString('en-US')}</td>
  2666. </tr>
  2667. <tr id="stats-sesh">
  2668. <td><label><input type="checkbox">Session Coins:</label></td>
  2669. <td>${sessionXP.toLocaleString('en-US')}</td>
  2670. </tr>
  2671. <tr id="stats-seshlength">
  2672. <td><label><input type="checkbox">Session Length:</label></td>
  2673. <td id="stats-sesh-length">${sessionLength}</td>
  2674. </tr>
  2675. </tbody>`;
  2676. for(let i in misc_settings.statsSettings.coins){
  2677. $('#stats-' + i)[misc_settings.statsSettings.coins[i] ? 'show' : 'hide']();
  2678. $('#stats-' + i + ' input[type="checkbox"').prop("checked", misc_settings.statsSettings.coins[i]);
  2679. }
  2680. $('#stats-extra-info').text(`Coin Stats - Updating Every ${updateTime / 1e3}s`).css('color', '#ffc800');
  2681. $('#stats-sesh-length').text(msToTime(Date.now() - scriptStartCoins));
  2682. $('#stats-perc-btn').hide();
  2683. }
  2684. }
  2685.  
  2686. $('.progress-bar').eq(1).parent()[0].style.cursor = "pointer";
  2687.  
  2688. if(userPreferences.coinXPstats){
  2689. $('.progress-bar').eq(1).parent().on("click", () => {
  2690. const statsCont = $('#stats-container');
  2691. if(statsCont[0].style.display == "none") statsCont.fadeIn(400);
  2692. else if(!coinsHTMLactive) statsCont.fadeOut(400);
  2693. coinsHTMLactive = false;
  2694. updateUI();
  2695. }), [$(".dash-coin.dcTopBar").eq(0), $("#coinsTopLeft"), $(".progress-bar-coins").eq(1)].forEach(el => {
  2696. el.on('click', (e) => {
  2697. const statsCont = $('#stats-container');
  2698. if(statsCont[0].style.display == "none") statsCont.fadeIn(400);
  2699. else if(coinsHTMLactive) statsCont.fadeOut(400);
  2700. coinsHTMLactive = true;
  2701. updateUI();
  2702. e.stopImmediatePropagation();
  2703. });
  2704. });
  2705. }
  2706.  
  2707. // copy chat msgs
  2708. if(userPreferences.rightClickCopyChat){
  2709. $('#contextSpectate').after(`<li id="contextCopyChat" class="contextmenu-item enabled"><div class="fa fa-clipboard fa-2x context-icon"></div><p>Copy Chat Messages</p></li>`);
  2710. $('#contextCopyChat').on('click', () => {
  2711. let arr = chatmsgs, str = "";
  2712. if(arr != null){
  2713. for(let i of arr.reverse()) str += `${new Date(i.time).toLocaleTimeString()} ${i.name}: ${i.message}\n`;
  2714. navigator.clipboard.writeText(str).then(function() {
  2715. curserMsg('Chat messages were successfully added to clipboard.', 'green');
  2716. }, function() {
  2717. curserMsg('Something went wrong. Nothing was added to your clipboard.', 'red');
  2718. });
  2719. }
  2720. $('#contextMenu').hide();
  2721. });
  2722. }
  2723.  
  2724.  
  2725.  
  2726. // add linesplit bubbles
  2727. $('body').append(`<div id="linesplit-markers"><div id="linesplit-top"></div><div id="linesplit-right"></div><div id="linesplit-bottom"></div><div id="linesplit-left"></div></div>`); // linesplit html
  2728. // add class to all elements that need to be hidden
  2729. setTimeout(() => $('#stats-container, #inventory, #chat, #minionUi, #infection_remain_zombie, #party, #challengeInfoBox, #gamemodeBox, #infoBox, #brGameContainer, #infGameContainer, #curser, #leaderboard, #minimap, #btnFriends, .innerBoxDashboard2, #fpsBox, #settingsBtn, #megaholder, #keyboard-layout, div[style^="position: fixed; right: 20px; bottom: 230px; z-index: 998;"], #linesplit_overlay, #fushykng, #art-panel').addClass("hideUI"), 4e3);
  2730.  
  2731. const addFriendDecline = () => {
  2732. if(!userPreferences.friendDeclineAll || currentUser == 'Please Login First') return;
  2733. $('#friendAcceptAll').text('Reject All').addClass('fsfb-temp').clone().insertAfter($('#friendAcceptAll')).attr('style', 'right: 93px;').text('Accept All').removeClass('fsfb-temp');
  2734. $('.fsfb-temp').attr('id', 'friendRejectAll').removeAttr('onclick').removeClass('fsfb-temp');
  2735. $('#friendRejectAll').on('click', () => {
  2736. $('#requestList>.friend>.btn-friends.remove').each(function(){ $(this)[0].click() })
  2737. });
  2738. }
  2739.  
  2740. if(userPreferences.friendDeclineAll){ // $('#friendAcceptAll').length
  2741. $('#btnFriends').on('click', async() => {
  2742. await waitUntil(() => $('#friendAcceptAll').length > 0);
  2743. if($('#friendAcceptAll').length == 0) return;
  2744. await sleep(50);
  2745. addFriendDecline();
  2746. })
  2747. }
  2748.  
  2749. let pushFn = Array.prototype.push,
  2750. spliceFn = Array.prototype.splice,
  2751. prop = null,
  2752. specialCells = !0,
  2753. customDc = false,
  2754. cellProto,
  2755. avgFps = 0,
  2756. fpsArr = [],
  2757. svSwitch = false,
  2758. entArr = null,
  2759. chatmsgs,
  2760. hideMinionMaxSize = 80;
  2761.  
  2762. let r1Portal = {
  2763. portal: null,
  2764. lastMass: 0,
  2765. lastMassChange: 0,
  2766. lastValue: 0,
  2767. room: 1
  2768. }, r2Portal = {
  2769. portal: null,
  2770. lastMass: 0,
  2771. lastMassChange: 0,
  2772. lastValue: 0,
  2773. room: 2
  2774. };
  2775.  
  2776. let svInfo = {
  2777. "default": {
  2778. ejPortalMass: 12,
  2779. r1Id: 1,
  2780. r2Id: 7,
  2781. r3Id: null,
  2782. r1StartMass: 500,
  2783. r2StartMass: 500,
  2784. entities: [],
  2785. serverType: "normal"
  2786. },
  2787. 1: { // POPSPLIT
  2788. entities: [
  2789. // A1
  2790. {type: 1, x: 2500, y: 2500, size: 45},
  2791. // r1
  2792. {type: 0, x: 3400, y: 15300, size: 195},
  2793. {type: 1, x: 2500, y: 15000, size: 45},
  2794. {type: 2, x: 3000, y: 14500, size: 35},
  2795. {type: 3, x: 2700, y: 14600, size: 29},
  2796. // r2
  2797. {type: 0, x: 12000, y: 15500, size: 195},
  2798. {type: 1, x: 11000, y: 15000, size: 29},
  2799. {type: 2, x: 12600, y: 14900, size: 35},
  2800. {type: 2, x: 12000, y: 15200, size: 35},
  2801. {type: 2, x: 11300, y: 14900, size: 35},
  2802. {type: 3, x: 12800, y: 14500, size: 29}
  2803. ],
  2804. bots: 4
  2805. },
  2806. 2: { // SLOWSPLIT
  2807. ejPortalMass: 20,
  2808. r1Id: 1,
  2809. r2Id: 14, // rightmost portal
  2810. r3Id: 6,
  2811. r1StartMass: 500,
  2812. r2StartMass: 500,
  2813. entities: [
  2814. // A1
  2815. {type: 1, x: 2500, y: 2500, size: 45},
  2816. // r1
  2817. {type: 0, x: 7400, y: 21300, size: 195},
  2818. {type: 1, x: 3500, y: 21000, size: 45},
  2819. {type: 2, x: 5000, y: 20500, size: 35},
  2820. {type: 3, x: 3700, y: 20600, size: 29},
  2821. // r2
  2822. {type: 0, x: 14000, y: 22000, size: 195},
  2823. {type: 1, x: 11000, y: 21000, size: 29},
  2824. {type: 2, x: 11300, y: 20900, size: 35},
  2825. {type: 2, x: 12000, y: 21200, size: 35},
  2826. {type: 2, x: 12600, y: 20900, size: 35},
  2827. {type: 3, x: 12800, y: 20500, size: 29},
  2828. // r2 (2nd?)
  2829. {type: 0, x: 22000, y: 21500, size: 195},
  2830. {type: 2, x: 21300, y: 21000, size: 45}
  2831. ]
  2832. },
  2833. 3: { // Dodgeball NA
  2834. entities: [],
  2835. bots: 7
  2836. },
  2837. 4: { // FASTSPLIT
  2838. ejPortalMass: 20,
  2839. r1Id: 1,
  2840. r2Id: 14, // rightmost portal
  2841. r3Id: 6,
  2842. r1StartMass: 500,
  2843. r2StartMass: 500,
  2844. entities: [
  2845. // A1
  2846. {type: 1, x: 2500, y: 2500, size: 45},
  2847. // r1
  2848. {type: 0, x: 7400, y: 21300, size: 195},
  2849. {type: 1, x: 3500, y: 21000, size: 45},
  2850. {type: 2, x: 5000, y: 20500, size: 35},
  2851. {type: 3, x: 3700, y: 20600, size: 29},
  2852. // r2
  2853. {type: 0, x: 14000, y: 22000, size: 195},
  2854. {type: 1, x: 11000, y: 21000, size: 29},
  2855. {type: 2, x: 11300, y: 20900, size: 35},
  2856. {type: 2, x: 12000, y: 21200, size: 35},
  2857. {type: 2, x: 12600, y: 20900, size: 35},
  2858. {type: 3, x: 12800, y: 20500, size: 29},
  2859. // r2 (2nd?)
  2860. {type: 0, x: 22000, y: 21500, size: 195},
  2861. {type: 2, x: 21300, y: 21000, size: 35}
  2862. ],
  2863. bots: 1
  2864. },
  2865. 5: { // SPLITRUN
  2866. entities: [
  2867. // A1
  2868. {type: 1, x: 2500, y: 2500, size: 45},
  2869. // r1
  2870. {type: 0, x: 3400, y: 15300, size: 195},
  2871. {type: 1, x: 2500, y: 15000, size: 45},
  2872. {type: 2, x: 3000, y: 14500, size: 35},
  2873. {type: 3, x: 2700, y: 14600, size: 29},
  2874. // r2
  2875. {type: 0, x: 12000, y: 15500, size: 195},
  2876. {type: 1, x: 11000, y: 15000, size: 29},
  2877. {type: 2, x: 12600, y: 14900, size: 35},
  2878. {type: 2, x: 11300, y: 14900, size: 35},
  2879. {type: 2, x: 12000, y: 15200, size: 35},
  2880. {type: 3, x: 12800, y: 14500, size: 29}
  2881. ]
  2882. },
  2883. 6: { // XINSTA
  2884. ejPortalMass: 12,
  2885. r1Id: 1,
  2886. r2Id: 6,
  2887. r1StartMass: 500,
  2888. r2StartMass: 500,
  2889. entities: [
  2890. // A1
  2891. {type: 1, x: 2500, y: 2500, size: 45},
  2892. // r1
  2893. {type: 0, x: 3400, y: 26300, size: 195},
  2894. {type: 1, x: 2500, y: 26000, size: 45},
  2895. {type: 2, x: 3000, y: 25500, size: 45},
  2896. {type: 3, x: 2700, y: 25600, size: 29},
  2897. // r2
  2898. {type: 0, x: 15000, y: 26500, size: 195},
  2899. {type: 1, x: 14000, y: 26000, size: 29},
  2900. {type: 2, x: 14300, y: 25900, size: 45},
  2901. {type: 2, x: 15000, y: 26200, size: 45},
  2902. {type: 2, x: 15600, y: 25900, size: 45},
  2903. {type: 3, x: 15800, y: 25500, size: 29}
  2904. ], bots: 6
  2905. },
  2906. 7: { // XY
  2907. entities: [
  2908. // A1
  2909. {type: 1, x: 2500, y: 2500, size: 45},
  2910. // r1
  2911. {type: 0, x: 3400, y: 15300, size: 195},
  2912. {type: 1, x: 2500, y: 15000, size: 45},
  2913. {type: 2, x: 3000, y: 14500, size: 35},
  2914. {type: 3, x: 2700, y: 14600, size: 29},
  2915. // r2
  2916. {type: 0, x: 12000, y: 15500, size: 195},
  2917. {type: 1, x: 11000, y: 15000, size: 29},
  2918. {type: 2, x: 11300, y: 14900, size: 35},
  2919. {type: 2, x: 12000, y: 15200, size: 35},
  2920. {type: 2, x: 12600, y: 14900, size: 35},
  2921. {type: 3, x: 12800, y: 14500, size: 29}
  2922. ], bots: 5
  2923. },
  2924. 8: { // INSTANT EU
  2925. entities: [
  2926. // A1
  2927. {type: 1, x: 2500, y: 2500, size: 45},
  2928. // r1
  2929. {type: 0, x: 3400, y: 15300, size: 195},
  2930. {type: 1, x: 2500, y: 15000, size: 45},
  2931. {type: 2, x: 3000, y: 14500, size: 35},
  2932. {type: 3, x: 2700, y: 14600, size: 29},
  2933. // r2
  2934. {type: 0, x: 12000, y: 15500, size: 195},
  2935. {type: 1, x: 11000, y: 15000, size: 29},
  2936. {type: 2, x: 11300, y: 14900, size: 35},
  2937. {type: 2, x: 12000, y: 15200, size: 35},
  2938. {type: 2, x: 12600, y: 14900, size: 35},
  2939. {type: 3, x: 12800, y: 14500, size: 29}
  2940. ]
  2941. },
  2942. 9: { // CR EU
  2943. entities: [
  2944. // A1
  2945. {type: 1, x: 2500, y: 2500, size: 45},
  2946. // r1
  2947. {type: 0, x: 3400, y: 27300, size: 195},
  2948. {type: 1, x: 2500, y: 27000, size: 45},
  2949. {type: 2, x: 3000, y: 26500, size: 35},
  2950. {type: 3, x: 2700, y: 26600, size: 29},
  2951. // r2
  2952. {type: 0, x: 12000, y: 27500, size: 195},
  2953. {type: 1, x: 11000, y: 27000, size: 29},
  2954. {type: 2, x: 11300, y: 26900, size: 35},
  2955. {type: 2, x: 12000, y: 27200, size: 35},
  2956. {type: 2, x: 12600, y: 26900, size: 35},
  2957. {type: 3, x: 12800, y: 26500, size: 29}
  2958. ], bots: 8
  2959. },
  2960. 11: { // GIGANTIC 1
  2961. entities: [
  2962. {type: 1, x: 2500, y: 2500, size: 45},
  2963. {type: 3, x: 5000, y: 33000, size: 45},
  2964. {type: 1, x: 18000, y: 18000, size: 45},
  2965. {type: 3, x: 24000, y: 12000, size: 45}
  2966. ],
  2967. serverType: "gigantic"
  2968. },
  2969. 12: { // GIANT NA
  2970. entities: [
  2971. {type: 1, x: 2500, y: 2500, size: 45},
  2972. {type: 3, x: 5000, y: 33000, size: 45},
  2973. {type: 1, x: 18000, y: 18000, size: 45},
  2974. {type: 3, x: 24000, y: 12000, size: 45}
  2975. ],
  2976. serverType: "gigantic"
  2977. },
  2978. 13: { // SS EU
  2979. ejPortalMass: 13.5,
  2980. r1Id: 12, //Lower room
  2981. r2Id: 11,
  2982. r1StartMass: 500,
  2983. r2StartMass: 500,
  2984. entities: [
  2985. // r1
  2986. {type: 0, x: 1500, y: 27500, size: 195},
  2987. {type: 1, x: 14000, y: 32000, size: 45},
  2988. {type: 3, x: 13200, y: 33500, size: 45},
  2989. {type: 4, x: 12500, y: 32200, size: 142},
  2990. {type: 4, x: 14500, y: 32200, size: 142},
  2991. // r2
  2992. {type: 0, x: 16000, y: 33500, size: 195},
  2993. {type: 1, x: 500, y: 24000, size: 45},
  2994. {type: 3, x: 2500, y: 24000, size: 45},
  2995. {type: 4, x: 900, y: 22000, size: 142},
  2996. {type: 4, x: 500, y: 15000, size: 224},
  2997. // r3 (?)
  2998. {type: 4, x: 1200, y: 5500, size: 224},
  2999. {type: 4, x: 2000, y: 3000, size: 224},
  3000. {type: 4, x: 4000, y: 6000, size: 224},
  3001. {type: 4, x: 4500, y: 3000, size: 224},
  3002. // on map
  3003. {type: 4, x: 8000, y: 3500, size: 224},
  3004. {type: 4, x: 10000, y: 4500, size: 224},
  3005. {type: 4, x: 16000, y: 3000, size: 224},
  3006. {type: 4, x: 15400, y: 2400, size: 224},
  3007. {type: 4, x: 7500, y: 19000, size: 224},
  3008. {type: 4, x: 19200, y: 14000, size: 224},
  3009. {type: 4, x: 24400, y: 24000, size: 224},
  3010. {type: 4, x: 17500, y: 14000, size: 142},
  3011. {type: 4, x: 30000, y: 16000, size: 142}
  3012. ],
  3013. serverType: "supersonic"
  3014. },
  3015. 14: { // SS NA
  3016. ejPortalMass: 13.5,
  3017. r1Id: 12, //Lower room
  3018. r2Id: 11,
  3019. r1StartMass: 500,
  3020. r2StartMass: 500,
  3021. entities: [
  3022. // r1
  3023. {type: 0, x: 1500, y: 27500, size: 195},
  3024. {type: 1, x: 14000, y: 32000, size: 45},
  3025. {type: 3, x: 13200, y: 33500, size: 45},
  3026. {type: 4, x: 12500, y: 32200, size: 142},
  3027. {type: 4, x: 14500, y: 32200, size: 142},
  3028. // r2
  3029. {type: 0, x: 16000, y: 33500, size: 195},
  3030. {type: 1, x: 500, y: 24000, size: 45},
  3031. {type: 3, x: 2500, y: 24000, size: 45},
  3032. {type: 4, x: 900, y: 22000, size: 142},
  3033. {type: 4, x: 500, y: 15000, size: 224},
  3034. // r3 (?)
  3035. {type: 4, x: 1200, y: 5500, size: 224},
  3036. {type: 4, x: 2000, y: 3000, size: 224},
  3037. {type: 4, x: 4000, y: 6000, size: 224},
  3038. {type: 4, x: 4500, y: 3000, size: 224},
  3039. // on map
  3040. {type: 4, x: 8000, y: 3500, size: 224},
  3041. {type: 4, x: 10000, y: 4500, size: 224},
  3042. {type: 4, x: 16000, y: 3000, size: 224},
  3043. {type: 4, x: 15400, y: 2400, size: 224},
  3044. {type: 4, x: 7500, y: 19000, size: 224},
  3045. {type: 4, x: 19200, y: 14000, size: 224},
  3046. {type: 4, x: 24400, y: 24000, size: 224},
  3047. {type: 4, x: 17500, y: 14000, size: 142},
  3048. {type: 4, x: 30000, y: 16000, size: 142}
  3049. ],
  3050. serverType: "supersonic"
  3051. },
  3052. 16: { // BR NA
  3053. entities: [],
  3054. bots: 7
  3055. },
  3056. 17: { // CR AS
  3057. entities: [
  3058. // A1
  3059. {type: 1, x: 2500, y: 2500, size: 45},
  3060. // r1
  3061. {type: 0, x: 3400, y: 27300, size: 195},
  3062. {type: 1, x: 2500, y: 27000, size: 45},
  3063. {type: 2, x: 3000, y: 26500, size: 35},
  3064. {type: 3, x: 2700, y: 26600, size: 29},
  3065. // r2
  3066. {type: 0, x: 12000, y: 27500, size: 195},
  3067. {type: 1, x: 11000, y: 27000, size: 29},
  3068. {type: 2, x: 11300, y: 26900, size: 35},
  3069. {type: 2, x: 12000, y: 27200, size: 35},
  3070. {type: 2, x: 12600, y: 26900, size: 35},
  3071. {type: 3, x: 12800, y: 26500, size: 29}
  3072. ]
  3073. },
  3074. 18: { // GIGA 1
  3075. entities: [
  3076. {type: 1, x: 2500, y: 2500, size: 45},
  3077. {type: 3, x: 5000, y: 33000, size: 45},
  3078. {type: 1, x: 18000, y: 18000, size: 45},
  3079. {type: 3, x: 24000, y: 12000, size: 45}
  3080. ],
  3081. serverType: "gigantic"
  3082. },
  3083. 19: { // GIGANTIC 2
  3084. entities: [
  3085. {type: 1, x: 2500, y: 2500, size: 45},
  3086. {type: 3, x: 5000, y: 33000, size: 45},
  3087. {type: 1, x: 18000, y: 18000, size: 45},
  3088. {type: 3, x: 24000, y: 12000, size: 45}
  3089. ],
  3090. serverType: "gigantic"
  3091. },
  3092. 20: { // CR NA
  3093. entities: [
  3094. // A1
  3095. {type: 1, x: 2500, y: 2500, size: 45},
  3096. // r1
  3097. {type: 0, x: 3400, y: 27300, size: 195},
  3098. {type: 1, x: 2500, y: 27000, size: 45},
  3099. {type: 2, x: 3000, y: 26500, size: 35},
  3100. {type: 3, x: 2700, y: 26600, size: 29},
  3101. // r2
  3102. {type: 0, x: 12000, y: 27500, size: 195},
  3103. {type: 1, x: 11000, y: 27000, size: 29},
  3104. {type: 2, x: 11300, y: 26900, size: 35},
  3105. {type: 2, x: 12000, y: 27200, size: 35},
  3106. {type: 2, x: 12600, y: 26900, size: 35},
  3107. {type: 3, x: 12800, y: 26500, size: 29}
  3108. ]
  3109. },
  3110. 21: { // EU SF BR
  3111. entities: [],
  3112. bots: 7
  3113. },
  3114. 23: { // GIGANTIC 3
  3115. entities: [
  3116. {type: 1, x: 2500, y: 2500, size: 45},
  3117. {type: 3, x: 5000, y: 33000, size: 45},
  3118. {type: 1, x: 18000, y: 18000, size: 45},
  3119. {type: 3, x: 24000, y: 12000, size: 45}
  3120. ],
  3121. serverType: "gigantic"
  3122. },
  3123. 24: { // GIGANTIC 4
  3124. entities: [
  3125. {type: 1, x: 2500, y: 2500, size: 45},
  3126. {type: 3, x: 5000, y: 33000, size: 45},
  3127. {type: 1, x: 18000, y: 18000, size: 45},
  3128. {type: 3, x: 24000, y: 12000, size: 45}
  3129. ],
  3130. serverType: "gigantic"
  3131. },
  3132. 25: { // GIANT 2 NA
  3133. entities: [
  3134. {type: 1, x: 2500, y: 2500, size: 45},
  3135. {type: 3, x: 5000, y: 33000, size: 45},
  3136. {type: 1, x: 18000, y: 18000, size: 45},
  3137. {type: 3, x: 24000, y: 12000, size: 45}
  3138. ],
  3139. serverType: "gigantic"
  3140. },
  3141. 26: { // GIGA 2
  3142. entities: [
  3143. {type: 1, x: 2500, y: 2500, size: 45},
  3144. {type: 3, x: 5000, y: 33000, size: 45},
  3145. {type: 1, x: 18000, y: 18000, size: 45},
  3146. {type: 3, x: 24000, y: 12000, size: 45}
  3147. ],
  3148. serverType: "gigantic"
  3149. },
  3150. 37: { // MEGASPLIT EU
  3151. entities: [],
  3152. bots: 25
  3153. },
  3154. 38: { // Solo Agf
  3155. ejPortalMass: 12,
  3156. r1Id: 1,
  3157. r2Id: 6,
  3158. r1StartMass: 500,
  3159. r2StartMass: 400,
  3160. entities: [
  3161. // A1
  3162. {type: 1, x: 2500, y: 2500, size: 45},
  3163. // room 1
  3164. {type: 0, x: 3500, y: 11500, size: 195},
  3165. {type: 1, x: 2500, y: 11500, size: 45},
  3166. {type: 2, x: 3000, y: 11000, size: 35},
  3167. {type: 3, x: 2700, y: 11100, size: 29},
  3168. // room 2
  3169. {type: 0, x: 9000, y: 11800, size: 180},
  3170. {type: 1, x: 8200, y: 10500, size: 29},
  3171. {type: 2, x: 8300, y: 10900, size: 35},
  3172. {type: 2, x: 9000, y: 11200, size: 35},
  3173. {type: 2, x: 9600, y: 10900, size: 35},
  3174. {type: 3, x: 9800, y: 10500, size: 29}
  3175. ], bots: 9
  3176. },
  3177. 39: { // MEGASPLIT AS
  3178. entities: [
  3179. ],
  3180. bots: 12
  3181. },
  3182. 40: { // INFECTION EU
  3183. entities: [],
  3184. bots: 15
  3185. },
  3186. 41: { // INFECTION NA
  3187. entities: [],
  3188. bots: 20
  3189. },
  3190. 42: { // GIANT 3 NA
  3191. entities: [
  3192. {type: 1, x: 2500, y: 2500, size: 45},
  3193. {type: 3, x: 5000, y: 33000, size: 45},
  3194. {type: 1, x: 18000, y: 18000, size: 45},
  3195. {type: 3, x: 24000, y: 12000, size: 45}
  3196. ],
  3197. serverType: "gigantic"
  3198. },
  3199. 43: { // Instant AS
  3200. entities: [
  3201. // A1
  3202. {type: 1, x: 2500, y: 2500, size: 45},
  3203. // r1
  3204. {type: 0, x: 3400, y: 15300, size: 195},
  3205. {type: 1, x: 2500, y: 15000, size: 45},
  3206. {type: 2, x: 3000, y: 14500, size: 35},
  3207. {type: 3, x: 2700, y: 14600, size: 29},
  3208. // r2
  3209. {type: 0, x: 12000, y: 15500, size: 195},
  3210. {type: 1, x: 11000, y: 15000, size: 29},
  3211. {type: 2, x: 11300, y: 14900, size: 35},
  3212. {type: 2, x: 12000, y: 15200, size: 35},
  3213. {type: 2, x: 12600, y: 14900, size: 35},
  3214. {type: 3, x: 12800, y: 14500, size: 29}
  3215. ], bots: 3
  3216. }
  3217. };
  3218.  
  3219. let noPortalSvIdList = [11, 19, 23, 24, 37, 36, 31, 29, 40, 41, 16, 15, 21, 35, 12, 25, 42, 28, 32, 22, 18, 26, 30, 39];
  3220. let currentServerId = 0;
  3221.  
  3222. function getServerValue(value) {
  3223. return (svInfo[currentServerId] && svInfo[currentServerId][value]) ? svInfo[currentServerId][value] : svInfo.default[value]
  3224. }
  3225.  
  3226. try{
  3227. for(let { isCurrent, id } of JSON.parse(localStorage.gameservers)){
  3228. if(isCurrent) currentServerId = id;
  3229. }
  3230. } catch {console.warn("FSFB: Failed to grab current server id.")};
  3231.  
  3232. // setTimeout(() => {
  3233. // svSwitch = true;
  3234. // }, 250);
  3235.  
  3236. function setHideMinionSize() {
  3237. switch(getServerValue("serverType")){
  3238. case "gigantic":
  3239. hideMinionMaxSize = 120;
  3240. break;
  3241. case "supersonic":
  3242. hideMinionMaxSize = 150;
  3243. break;
  3244. case "normal":
  3245. hideMinionMaxSize = 80;
  3246. break;
  3247. default:
  3248. hideMinionMaxSize = 80;
  3249. break;
  3250. }
  3251. }
  3252. setHideMinionSize();
  3253.  
  3254. let ss = unsafeWindow.setserver;
  3255. unsafeWindow.setserver = (sv, sn) => {
  3256. playerAlive = false;
  3257. setHideMinionSize();
  3258. r1Portal.portal = null;
  3259. r2Portal.portal = null;
  3260. svSwitch = true;
  3261. ss(sv, sn);
  3262. if(sn) currentServerId = sn.includes('AS | Instant') ? 43 : +sv.match(/(?<=s)\d+(?=\.agma\.io)/gm)?.[0];
  3263. }
  3264.  
  3265. function createCell(posX, posY, type, nSize){
  3266. if(!cellProto) return null;
  3267.  
  3268. let color,
  3269. colorDimmed = "#FFFFFF",
  3270. size = 0,
  3271. imageId = 0,
  3272. spikes = null;
  3273.  
  3274. switch(type){
  3275. case 0:
  3276. color = "#622373";
  3277. colorDimmed = "#4e1c5c";
  3278. size = nSize ? nSize : 200;
  3279. imageId = 1;
  3280. break;
  3281. case 1:
  3282. color = "#ff0000";
  3283. colorDimmed = "#cc0001";
  3284. size = nSize ? nSize : 32;
  3285. spikes = {x: posX, y: posY, s: size, p: size};
  3286. imageId = 2;
  3287. break;
  3288. case 2:
  3289. color = "#76ff54";
  3290. colorDimmed = "#66b319";
  3291. size = nSize ? nSize : 35;
  3292. imageId = 3;
  3293. break;
  3294. case 3:
  3295. color = "#ffd000";
  3296. colorDimmed = "#ccb300";
  3297. size = nSize ? nSize : 32;
  3298. spikes = {x: posX, y: posY, s: size, p: size};
  3299. imageId = 4;
  3300. break;
  3301. case 4:
  3302. color = "#00a2e8";
  3303. colorDimmed = "#0081b9";
  3304. size = nSize ? nSize : 150;
  3305. imageId = 5;
  3306. break;
  3307. default:
  3308. color = "#FFFFFF";
  3309. size = 500;
  3310. };
  3311.  
  3312. let cell = new cellProto.constructor();
  3313. cell[prop[41]] = imageId;
  3314. cell[prop[19]] = null;
  3315. cell[prop[50]] = 0;
  3316. cell[prop[40]] = spikes ? 1 : 0;
  3317. cell[prop[26]] = null;
  3318. cell[prop[52]] = false;
  3319. cell[prop[53]] = false;
  3320. cell[prop[39]] = [];
  3321. cell[prop[57]] = 0;
  3322. cell[prop[45]] = false;
  3323. cell[prop[37]] = true;
  3324. cell[prop[24]] = null;
  3325. cell[prop[44]] = false;
  3326. cell[prop[56]] = false;
  3327. cell[prop[47]] = false;
  3328. cell[prop[25]] = null;
  3329. cell[prop[35]] = Date.now();
  3330. cell[prop[51]] = 0;
  3331. cell[prop[42]] = null;
  3332. cell.clanCache = null;
  3333. cell.clanPart = null;
  3334. cell.color = color;
  3335. cell[prop[46]] = 69;
  3336. cell[prop[38]] = spikes;
  3337. cell[prop[33]] = 1;
  3338. cell[prop[36]] = 0;
  3339. cell[prop[31]] = posX;
  3340. cell[prop[32]] = posY;
  3341. cell[prop[3]] = 0;
  3342. cell.id = 1e9;
  3343. cell[prop[21]] = null;
  3344. cell[prop[20]] = null;
  3345. cell.massCache = null;
  3346. cell[prop[11]] = size;
  3347. cell.nSize = size;
  3348. cell.name = null;
  3349. cell.namePart = null;
  3350. cell.nameSize = 0;
  3351. cell.oid = 0;
  3352. cell.ox = posX;
  3353. cell.oy = posY;
  3354. cell[prop[58]] = 0;
  3355. cell.rotation = 0;
  3356. cell.shape = 0;
  3357. cell.size = size;
  3358. cell.skinId = 0;
  3359. cell.strokeSize = size + 4;
  3360. cell.textDrawn = null;
  3361. cell.transform = null;
  3362. cell[prop[18]] = true;
  3363. cell[prop[2]] = colorDimmed ? colorDimmed : dimmColor(color);
  3364. cell[prop[17]] = null;
  3365. cell.x = posX;
  3366. cell[prop[23]] = null;
  3367. cell.y = posY;
  3368. cell[prop[22]] = null;
  3369. cell[prop[54]] = false;
  3370. cell[prop[34]] = Date.now();
  3371. cell[prop[55]] = true;
  3372. return cell;
  3373. }
  3374.  
  3375.  
  3376. function customize(c){
  3377. cellProto = Object.getPrototypeOf(c);
  3378. uisdoa && (unsafeWindow.__cellProto = cellProto);
  3379. // this should stay independent unless sora changes the structure of cell class
  3380. const methods = [];
  3381. let a = c;
  3382. while (a = Reflect.getPrototypeOf(a)) {
  3383. const keys = Reflect.ownKeys(a);
  3384. keys.forEach((k) => methods.push(k));
  3385. }
  3386. let dc_fn_name = methods[7];
  3387. let dc = cellProto[dc_fn_name];
  3388. Object.defineProperty(cellProto, '_drCl', { value: dc });
  3389. Object.defineProperty(cellProto, dc_fn_name, { value: function(){
  3390. if(!customCells && !customCellsChanged && !antilagCells) return dc.apply(this, arguments);
  3391.  
  3392. let cell = this,
  3393. cellType = cell.c$,
  3394. cellColor = cell.color;
  3395.  
  3396. if (antilagCells && (cellType === 0 || cellType === 3) &&
  3397. (settings.anti_lag[0].active && cell[prop[52]] && cell.nSize <= hideMinionMaxSize) ||
  3398. (cellType === 3 && (settings.anti_lag[2].active || (settings.anti_lag[3].active && !cell[prop[37]])) && cell.color !== "#000101")){
  3399. return (cell.hidden = true);
  3400. } else if(cell.hidden){
  3401. cell.hidden = false;
  3402. }
  3403.  
  3404. if(cellType === 0 && !('oType' in cell)){ // second makes sure that the type wasnt changed to playerCell (mothercell showmass)
  3405. if(cell[prop[52]]){
  3406. if(settings.anti_lag[1].active){
  3407. cell[prop[18]] = false;
  3408. }
  3409. }
  3410.  
  3411. if(settings.theme_boxes[1].active){
  3412. if(!('oOwnCell' in cell)) cell.oOwnCell = cell[prop[45]];
  3413. cell[prop[45]] = true;
  3414. }
  3415.  
  3416. if(settings.theme_boxes[6].active){
  3417. if(!('oSpiked' in cell)) cell.oSpiked = cell[prop[56]];
  3418. cell[prop[56]] = settings.theme_boxes[6].active;
  3419. } else if('oSpiked' in cell){
  3420. cell[prop[56]] = cell.oSpiked;
  3421. }
  3422.  
  3423. if(onlyCells || customCellsChanged){
  3424. if(settings.theme_boxes[2].active){
  3425. if(!('oHasImage' in cell)) cell.oHasImage = cell[prop[18]];
  3426. cell[prop[18]] = (cell.oOwnCell === undefined ? cell[prop[45]] : cell.oOwnCell) ? cell[prop[18]] : false;
  3427.  
  3428. } else if(settings.theme_boxes[3].active){
  3429. if(!('oHasImage' in cell)) cell.oHasImage = cell[prop[18]];
  3430. cell[prop[18]] = cell[prop[53]] ? cell[prop[18]] : false;
  3431.  
  3432. } else if('oHasImage' in cell){
  3433. cell[prop[18]] = cell.oHasImage;
  3434. }
  3435.  
  3436. if(settings.theme_boxes[4].active){
  3437. if(!('oName' in cell)) cell.oName = cell.name;
  3438. cell.name = (cell.oOwnCell === undefined ? cell[prop[45]] : cell.oOwnCell) ? cell.name : "";
  3439.  
  3440. } else if(settings.theme_boxes[5].active){
  3441. if(!('oName' in cell)) cell.oName = cell.name;
  3442. cell.name = cell[prop[53]] ? cell.name : "";
  3443.  
  3444. } else if('oName' in cell){
  3445. cell.name = cell.oName;
  3446. }
  3447. }
  3448. } else if(cellType === 1){
  3449. if(!('oSize' in cell)) cell.oSize = cell.nSize; // food normally doesnt have an oSize; ensures that mothercell food is correct
  3450. if(foodSizeOn){
  3451. cell.nSize = cell.oSize * settings.uiScaling[1].level;
  3452. cell.size = cell.oSize * settings.uiScaling[1].level;
  3453. } else if(customCellsChanged) {
  3454. cell.size = cell.oSize;
  3455. cell.nSize = cell.oSize;
  3456. }
  3457. if(settings.theme[0].active){
  3458. if(!('oColor' in cell)) cell.oColor = cell.color;
  3459. cell.color = settings.theme[0].color;
  3460. } else if('oColor' in cell){
  3461. cell.color = cell.oColor;
  3462. }
  3463. } else if(cellType === 2){
  3464. if(settings.theme[1].active){
  3465. if(!('oColor' in cell)) cell.oColor = cell.color;
  3466. cell.color = settings.theme[1].color;
  3467. } else if('oColor' in cell){
  3468. cell.color = cell.oColor;
  3469. }
  3470. if(settings.theme[1].active || settings.theme[2].active){ // dimmed color / stroke
  3471. if(!('oColorDimmed' in cell)) cell.oColorDimmed = cell[prop[2]];
  3472. cell[prop[2]] = settings.theme[2].active ? settings.theme[2].color : settings.theme_boxes[0].active && agmaSettings.sBubbleCells ? settings.theme[1].color : dimmColor(settings.theme[1].color); // if no stroke color is set, it will just be a darker version of the virusColor
  3473. } else if('oColorDimmed' in cell){
  3474. cell[prop[2]] = cell.oColorDimmed;
  3475. }
  3476. } else if(cellType === 9){
  3477. if(settings.theme[3].active){
  3478. if(!('oColor' in cell)) cell.oColor = cell.color;
  3479. cell.color = settings.theme[3].color;
  3480. } else if('oColor' in cell){
  3481. cell.color = cell.oColor;
  3482. }
  3483. if(settings.theme[3].active || settings.theme[4].active){ // dimmed color / stroke
  3484. if(!('oColorDimmed' in cell)) cell.oColorDimmed = cell[prop[2]];
  3485. cell[prop[2]] = settings.theme[4].active ? settings.theme[4].color : settings.theme_boxes[0].active && agmaSettings.sBubbleCells ? settings.theme[3].color : dimmColor(settings.theme[3].color);
  3486. } else if('oColorDimmed' in cell){
  3487. cell[prop[2]] = cell.oColorDimmed;
  3488. }
  3489.  
  3490. if(settings.checkboxes[9].active){
  3491. cell[prop[46]] = 0;
  3492. cell.oType = 9;
  3493. cell[prop[45]] = true;
  3494. cell.oOwnCell = false;
  3495. dc.apply(this, arguments);
  3496. cell[prop[46]] = 9;
  3497. return;
  3498. }
  3499. }
  3500. return dc.apply(this, arguments);
  3501. }});
  3502.  
  3503.  
  3504. Object.defineProperty(cellProto, prop[46], {
  3505. get: function() {
  3506. return this.c$;
  3507. },
  3508. set: function(val) {
  3509. if((val === 0 || val === 3) && this.color === "#000000"){
  3510. this.color = "#000101";
  3511. } else if(val === 0 && this.color === "#622373"){
  3512. this.color = "#622374"; // give purple cells a slightly diff color to make them detectable in .stroke fn
  3513. }
  3514. this.c$ = val;
  3515. }
  3516. });
  3517.  
  3518. customDc = true;
  3519. }
  3520.  
  3521. function dimmColor(color) {
  3522. var r = (Math.floor(parseInt(color.substr(1, 2), 16) * 0.5)).toString(16),
  3523. g = (Math.floor(parseInt(color.substr(3, 2), 16) * 0.5)).toString(16),
  3524. b = (Math.floor(parseInt(color.substr(5, 2), 16) * 0.5)).toString(16);
  3525. if (r.length === 1) r = "0" + r;
  3526. if (g.length === 1) g = "0" + g;
  3527. if (b.length === 1) b = "0" + b;
  3528. return "#" + r + g + b
  3529. }
  3530.  
  3531. const newSortFunc = function(oldFunc){
  3532. if(settings.theme_boxes[8].active){
  3533. return function(a, b){
  3534. const oldFuncApplied = oldFunc.apply(this, arguments),
  3535. aVal = a.c$,
  3536. bVal = b.c$;
  3537. return ((settings.theme_boxes[7].active ? -oldFuncApplied : oldFuncApplied) > 0 || aVal === 4) && bVal !== 4 ? 1 : aVal === 4 && bVal === 4 ? 0 : -1
  3538. }
  3539. } else if(settings.theme_boxes[7].active){
  3540. return function(a, b){
  3541. return -oldFunc.apply(this, arguments);
  3542. }
  3543. } else {
  3544. return oldFunc;
  3545. }
  3546. }
  3547.  
  3548. const _sort = Array.prototype.sort;
  3549. Array.prototype.sort = function(oldFunc) {
  3550. return oldFunc == null || typeof this[0]?.color !== "string" ? _sort.apply(this, arguments) : _sort.call(this, newSortFunc(oldFunc));
  3551. }
  3552.  
  3553.  
  3554. var UA = navigator.userAgent;
  3555. var googleDomain = "translate.google.com";
  3556. var dictURL = "https://" + googleDomain + "/translate_a/single?client=t";
  3557.  
  3558. function init_google_value_tk() {
  3559. var url = "https://" + googleDomain + "/translate_a/element.js";
  3560. GM_xmlhttpRequest({
  3561. method: "GET",
  3562. url: url,
  3563. onreadystatechange: function(resp) {
  3564. if (resp.readyState == 4) {
  3565. clearTimeout(setTimeout(function() {
  3566. this.abort();
  3567. }, 2000));
  3568. if (resp.status == 200) {
  3569. init_google_value_tk_parse(resp.responseText);
  3570. }
  3571. }
  3572. }
  3573. });
  3574. }
  3575.  
  3576. function init_google_value_tk_parse(responseText) {
  3577. var res = /c\._ctkk='(.+?)'/i.exec(responseText);
  3578. if (res != null) {
  3579. setStorage('google_value_tk', res[1]);
  3580. };
  3581. }
  3582.  
  3583. const Request = async(txt, sl = 'auto', tl = 'auto') => {
  3584. return new Promise((resolve, reject) => {
  3585. function parse(gTradStringArray) {
  3586. var arr = JSON.parse(gTradStringArray);
  3587. var translation = '';
  3588. for (let i = 0; i < arr[0].length; i++) {
  3589. if (typeof arr[0][i][0] != 'undefined' && arr[0][i][0] != null) translation += arr[0][i][0];
  3590. }
  3591. resolve(translation);
  3592. }
  3593. var tk = googleTK(txt);
  3594. var Url = dictURL +
  3595. "&hl=auto" +
  3596. "&sl=" + sl + "&tl=" + tl +
  3597. "&dt=bd&dt=ex&dt=ld&dt=md&dt=qca&dt=rw&dt=rm&dt=ss&dt=t&dt=at&ie=UTF-8&oe=UTF-8&otf=2&trs=1&inputm=1&ssel=0&tsel=0&source=btn&kc=3" +
  3598. "&tk=" + tk +
  3599. "&q=" + encodeURI(txt);
  3600. var method = 'POST';
  3601. var Data = '';
  3602. var Hdr = {
  3603. "User-Agent": UA,
  3604. "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
  3605. "Accept-Encoding": "gzip, deflate"
  3606. }
  3607. var Q = Url.split('&q=');
  3608. Url = Q[0];
  3609. Data = '&q=' + Q[1];
  3610. Hdr["Content-Length"] = Data.length + '';
  3611. Hdr["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8";
  3612. GM_xmlhttpRequest({
  3613. method: method,
  3614. url: Url,
  3615. data: Data,
  3616. headers: Hdr,
  3617. onload: function(resp) {
  3618. try {
  3619. parse(resp.responseText)
  3620. } catch (e) {
  3621. unsafeWindow.fsfbLogTranslateErrors && console.error("FSFB:", e);
  3622. resolve(null);
  3623. }
  3624. }
  3625. });
  3626. });
  3627. }
  3628.  
  3629. let dURIC = unsafeWindow.decodeURIComponent,
  3630. opt = [0x92933AFC, 0x75408D32];
  3631. unsafeWindow.decodeURIComponent = function(x){
  3632. if(x === "") x = opt[Math.round(Math.random())].toString();
  3633. return dURIC(x);
  3634. }
  3635.  
  3636. // return token for the new API
  3637. function googleTK(text) {
  3638. // view-source:https://translate.google.com/translate/releases/twsfe_w_20160620_RC00/r/js/desktop_module_main.js && TKK from HTML
  3639. var uM = getStorage('google_value_tk');
  3640. if (uM == 'undefined' || uM == null) {
  3641. init_google_value_tk();
  3642. uM = "427110.1469889687";
  3643. } else if (Number(uM.split('.')[0]) !== Math.floor(Date.now() / 3600000)) {
  3644. init_google_value_tk();
  3645. };
  3646. var cb = "&";
  3647. var k = "";
  3648. var Gf = "=";
  3649. var Vb = "+-a^+6";
  3650. var t = "a";
  3651. var Yb = "+";
  3652. var Zb = "+-3^+b+-f";
  3653. var jd = ".";
  3654. var sM = function(a) {
  3655. return function() {
  3656. return a
  3657. }
  3658. }
  3659. var tM = function(a, b) {
  3660. for (var c = 0; c < b.length - 2; c += 3) {
  3661. let d = b.charAt(c + 2);
  3662. d = d >= t ? d.charCodeAt(0) - 87 : Number(d);
  3663. d = b.charAt(c + 1) == Yb ? a >>> d : a << d;
  3664. a = b.charAt(c) == Yb ? a + d & 4294967295 : a ^ d
  3665. }
  3666. return a
  3667. };
  3668. var vM = function(a) {
  3669. var b;
  3670. if (null !== uM) {
  3671. b = uM;
  3672. } else {
  3673. b = sM(String.fromCharCode(84));
  3674. var c = sM(String.fromCharCode(75));
  3675. b = [b(), b()];
  3676. b[1] = c();
  3677. b = (uM = unsafeWindow[b.join(c())] || k) || k
  3678. }
  3679. let d = sM(String.fromCharCode(116));
  3680. c = sM(String.fromCharCode(107));
  3681. d = [d(), d()];
  3682. d[1] = c();
  3683. c = cb + d.join(k) + Gf;
  3684. d = b.split(jd);
  3685. b = Number(d[0]) || 0;
  3686.  
  3687. for (var e = [], f = 0, g = 0; g < a.length; g++) {
  3688. var m = a.charCodeAt(g);
  3689. 128 > m ? e[f++] = m : (2048 > m ? e[f++] = m >> 6 | 192 : (55296 == (m & 64512) && g + 1 < a.length && 56320 == (a.charCodeAt(g + 1) & 64512) ? (m = 65536 + ((m & 1023) << 10) + (a.charCodeAt(++g) & 1023), e[f++] = m >> 18 | 240, e[f++] = m >> 12 & 63 | 128) : e[f++] = m >> 12 | 224, e[f++] = m >> 6 & 63 | 128), e[f++] = m & 63 | 128)
  3690. }
  3691. a = b || 0;
  3692. for (f = 0; f < e.length; f++) {
  3693. a += e[f];
  3694. a = tM(a, Vb);
  3695. };
  3696. a = tM(a, Zb);
  3697. a ^= Number(d[1]) || 0;
  3698. 0 > a && (a = (a & 2147483647) + 2147483648);
  3699. a %= 1E6;
  3700. return a.toString() + jd + (a ^ b);
  3701. };
  3702. return vM(text);
  3703. }
  3704.  
  3705. let skinsArr, translatedCache = {}, entsAdded = false;
  3706.  
  3707. Array.prototype.push = function(){
  3708. if(this?.length && typeof this?.[0]?.approved === 'boolean' && typeof this?.[0]?.type === 'number' && typeof this?.[0]?.zIndex === 'undefined') skinsArr = this;
  3709. if(this?.length && typeof this?.[0]?.id === "number" && typeof this?.[0]?.color === "string"){
  3710. // if(customCells){
  3711. let cell = this[this.length - 1],
  3712. [pushedCell] = arguments;
  3713. if(!prop){
  3714. prop = Object.keys(cell);
  3715. uisdoa && (unsafeWindow.__prop = Object.keys(cell));
  3716. if(prop.length != 59 || prop[28] != "massCache"){
  3717. console.error("FSFB Scripts error, contact authors: ", prop.length, prop[28]);
  3718. }
  3719. }
  3720. if((pushedCell[prop[36]] && settings.anti_lag[4].active) || (settings.anti_lag[0].active && pushedCell.hidden)) return;
  3721.  
  3722. if(pushedCell.id === getServerValue("r1Id")){
  3723. r1Portal.portal = pushedCell;
  3724. } else if(pushedCell.id === getServerValue("r2Id")){
  3725. r2Portal.portal = pushedCell;
  3726. }
  3727. !customDc && customize(cell);
  3728.  
  3729. if(entsAdded && !settings.checkboxes[8].active){
  3730. for(let i = this.length - 1; i >= 0; --i) {
  3731. if(this[i].id == 1e9){
  3732. this.splice(i, 1);
  3733. entsAdded = false;
  3734. }
  3735. }
  3736. }
  3737. if((svSwitch || !entsAdded) && settings.checkboxes[8].active && this[0][prop[47]] && this.length > 1){
  3738. for(let i = 0; i < getServerValue("entities").length; i++){
  3739. let ent = getServerValue("entities")[i];
  3740. pushFn.apply(this, [createCell(ent.x, ent.y, ent.type, ent.size)]);
  3741. }
  3742. svSwitch = false;
  3743. entsAdded = true;
  3744. }
  3745. }
  3746.  
  3747. let applied = pushFn.apply(this, arguments);
  3748. if(this?.length && typeof this?.[0]?.message === 'string' && typeof this?.[0]?.name === 'string'){
  3749. chatmsgs = this;
  3750. if(arguments[0]?.message && !/[youaie](?!.: )/gmi.test(arguments[0].message)) Object.entries(cmap).forEach(([key, val]) => (arguments[0].message = arguments[0].message.replace(new RegExp(val, 'g'), key)));
  3751. (async() => {
  3752. if(settings.chat_translate[0].active && (settings.chat_translate[1].active || arguments[0][Object.keys(arguments[0])[0]])){
  3753. let originalMsg = arguments[0]?.untranslated ?? arguments[0].message,
  3754. translatedMsg = translatedCache[settings.chat_translate[3].set + settings.chat_translate[4].set]?.[originalMsg] ?? await Request(originalMsg, settings.chat_translate[3].set, settings.chat_translate[4].set);
  3755. if(translatedMsg != null) translatedCache[settings.chat_translate[3].set + settings.chat_translate[4].set] = {...translatedCache[settings.chat_translate[3].set + settings.chat_translate[4].set], ...{[originalMsg] : translatedMsg}};
  3756. if((!('untranslated' in arguments[0]) || arguments[0].translatedLang != settings.chat_translate[3].set + settings.chat_translate[4].set || arguments[0].showingOrig != settings.chat_translate[2].active) && arguments[0]?.message && translatedMsg != null){
  3757. setTimeout(() => {
  3758. arguments[0].untranslated = originalMsg;
  3759. arguments[0].translatedLang = settings.chat_translate[3].set + settings.chat_translate[4].set;
  3760. arguments[0].showingOrig = settings.chat_translate[2].active;
  3761. arguments[0].message = settings.chat_translate[2].active ? originalMsg + ' [ ' + translatedMsg + ' ]' : translatedMsg;
  3762. arguments[0].filter = false;
  3763. arguments[0].cache = null;
  3764. }, 0);
  3765. }
  3766. } else { // change translate off
  3767. if('untranslated' in arguments[0] && arguments[0]?.untranslated != arguments[0].message){
  3768. setTimeout(() => {
  3769. arguments[0].message = arguments[0]?.untranslated;
  3770. arguments[0].translatedLang = 'none';
  3771. arguments[0].filter = false;
  3772. arguments[0].cache = null;
  3773. }, 0);
  3774. }
  3775. }
  3776. })();
  3777. }
  3778. return applied;
  3779. }
  3780. Array.prototype.splice = function(){
  3781. if(customCells && this.length && typeof this[0].id == "number" && typeof this[0].color == "string"){
  3782. let cell = this[arguments[0]];
  3783. if(cell == r1Portal.portal){
  3784. r1Portal.portal = null;
  3785. } else if(cell == r2Portal.portal){
  3786. r2Portal.portal = null;
  3787. }
  3788. }
  3789. return spliceFn.apply(this, arguments);
  3790. }
  3791.  
  3792.  
  3793. const brSetting = settings.theme[6];
  3794. const _fillStyleSetter = Object.getOwnPropertyDescriptor(CanvasRenderingContext2D.prototype, 'fillStyle').set;
  3795.  
  3796. Object.defineProperty(CanvasRenderingContext2D.prototype, 'fillStyle', {
  3797. set: function(newFillStyle) {
  3798. if(brSetting.active
  3799. && newFillStyle === '#CC3030'){
  3800. newFillStyle = userPreferences.rainbowBrHazard ? hslToHex(Math.floor(Date.now() / userPreferences.rainbowBrHazardSpeed) % 360, 100, 50) : brSetting.color;
  3801. } else if(newFillStyle === "#000101" && userPreferences.whiteBorder4BlackCells){
  3802. this.strokeStyle = "#FFFFFF";
  3803. this.lineWidth = 12;
  3804. this.stroke();
  3805. }
  3806. _fillStyleSetter.call(this, newFillStyle);
  3807. }
  3808. });
  3809.  
  3810.  
  3811. const fillFn = CanvasRenderingContext2D.prototype.fill;
  3812. CanvasRenderingContext2D.prototype.fill = function() {
  3813. if(!fillCells) return fillFn.apply(this, arguments);
  3814. const { globalAlpha } = this;
  3815. let doStroke = true;
  3816. if(globalAlpha === .04){
  3817. switch(this.fillStyle){
  3818. case "#ff0000": // rec
  3819. this.strokeStyle = "#cc0001";
  3820. break;
  3821. case "#76ff54": // grw
  3822. this.strokeStyle = "#66b319";
  3823. break;
  3824. case "#ffd000": // spd
  3825. this.strokeStyle = "#ccb300";
  3826. break;
  3827. case "#00a2e8": // min pack
  3828. this.strokeStyle = "#0081b9";
  3829. break;
  3830. case "#622373": // portal
  3831. this.strokeStyle = "#4e1c5c";
  3832. break;
  3833. default:
  3834. doStroke = false;
  3835. };
  3836. if(doStroke){
  3837. this.globalAlpha = uisdoa?.o ?? .2;
  3838. this.lineWidth = 8;
  3839. this.stroke();
  3840. this.globalAlpha = uisdoa?.oo ?? .1;
  3841. this.shadowOffsetY = 1; // for f#&%ing curser lock
  3842. }
  3843. };
  3844. if(!doStroke && globalAlpha === 0.04 && settings.checkboxes[2].active){
  3845. this.strokeStyle = agmaSettings.sDark ? "#FFFFFF" : "#000000";
  3846. this.globalAlpha = 1;
  3847. this.lineWidth = 30;
  3848. this.stroke();
  3849. this.globalAlpha = 0.04;
  3850. } else if (globalAlpha === .4 && settings.theme_boxes[0].active) {
  3851. this.globalAlpha = 0.15
  3852. }
  3853. return fillFn.apply(this, arguments)
  3854. }
  3855. const strokeFn = CanvasRenderingContext2D.prototype.stroke;
  3856. CanvasRenderingContext2D.prototype.stroke = function() {
  3857. if (strokeCells && this.canvas.id === "canvas") {
  3858. const { strokeStyle } = this;
  3859. if (strokeStyle === "#dddddd" || strokeStyle === "#333333" || strokeStyle === "#4e1c5b" /* adjusted portal cell stroke color*/) {
  3860. if(settings.checkboxes[2].active) this.strokeStyle = agmaSettings.sDark ? "#FFFFFF" : "#000000";
  3861. if(this.shadowOffsetY == 1){
  3862. this.shadowOffsetY = 0;
  3863. return;
  3864. };
  3865. };
  3866. if (agmaSettings.sBubbleCells && settings.theme_boxes[0].active && this.lineWidth != 4) {
  3867. this.lineWidth = 15 + Math.min(Math.max(avgFps - 25, 0), 10);
  3868. }
  3869. }
  3870. return strokeFn.apply(this, arguments)
  3871. }
  3872.  
  3873. let drawImgFn = CanvasRenderingContext2D.prototype.drawImage;
  3874. CanvasRenderingContext2D.prototype.drawImage = function () {
  3875. if(this.globalAlpha === 0.01 && /ects\/[1-5](?:_lo)?\.p/.test(arguments[0]?.src)){
  3876. this.globalAlpha = .35;
  3877. }
  3878. drawImgFn.apply(this, arguments);
  3879. if(settings.checkboxes[7].active && this.canvas.id === "canvas"){
  3880. if((r1Portal.portal || r2Portal.portal) && !noPortalSvIdList.includes(currentServerId) && /ects\/1(?:_lo)?\./.test(arguments[0].src)){ // using destroyed doesnt work hence splice
  3881. let c = p => {
  3882. if((p.portal.nSize * p.portal.nSize / 100) != p.lastMass){
  3883. p.lastMassChange = Date.now();
  3884. }
  3885. p.lastMass = (p.portal.nSize * p.portal.nSize / 100);
  3886.  
  3887. let value = Date.now() - p.lastMassChange > 200 ? ~~(((p.portal.nSize * p.portal.nSize / 100) - getServerValue("r" + p.room + "StartMass")) / getServerValue("ejPortalMass")).toString() : p.lastValue;
  3888. p.lastValue = value;
  3889. if(value > 9 || value < 0){
  3890. value = "?";
  3891. }
  3892. this.fillStyle = value == "7" ? "#FFCC12" : "#FFFFFF";
  3893. this.globalAlpha = 1;
  3894. this.font = "72px Ubuntu, serif";
  3895. this.fillText(value, p.portal.x - this.measureText(value).width / 2, p.portal.y + 20);
  3896. }
  3897. r1Portal.portal && c(r1Portal);
  3898. r2Portal.portal && c(r2Portal);
  3899. // }
  3900. }
  3901. }
  3902. }
  3903.  
  3904. function hslToHex(h, s, l) {
  3905. h /= 360;
  3906. s /= 100;
  3907. l /= 100;
  3908. let r, g, b;
  3909. if (s === 0) {
  3910. r = g = b = l;
  3911. } else {
  3912. const hue2rgb = (p, q, t) => {
  3913. if (t < 0) t += 1;
  3914. if (t > 1) t -= 1;
  3915. if (t < 1 / 6) return p + (q - p) * 6 * t;
  3916. if (t < 1 / 2) return q;
  3917. if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
  3918. return p;
  3919. };
  3920. const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
  3921. const p = 2 * l - q;
  3922. r = hue2rgb(p, q, h + 1 / 3);
  3923. g = hue2rgb(p, q, h);
  3924. b = hue2rgb(p, q, h - 1 / 3);
  3925. }
  3926. const toHex = x => {
  3927. const hex = Math.round(x * 255).toString(16);
  3928. return hex.length === 1 ? '0' + hex : hex;
  3929. };
  3930. return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
  3931. }
  3932.  
  3933.  
  3934.  
  3935. const _lineTo = CanvasRenderingContext2D.prototype.lineTo;
  3936. CanvasRenderingContext2D.prototype.lineTo = function () {
  3937.  
  3938. if(settings.theme[5].active){
  3939. if(!agmaSettings.sGrid){ // use to avoid changing color of background "grid lines" settings
  3940. this.strokeStyle = userPreferences.rainbowMapBorder ? hslToHex(Math.floor(Date.now() / userPreferences.rainbowBorderSpeed) % 360, 100, 50) : settings.theme[5].color;
  3941. } else {
  3942. if(agmaSettings.sDark){
  3943. if(!/#(..)\1{2}/gm.test(this.strokeStyle)) this.strokeStyle = userPreferences.rainbowMapBorder ? hslToHex(Math.floor(Date.now() / userPreferences.rainbowBorderSpeed) % 360, 100, 50) : settings.theme[5].color;
  3944. } else {
  3945. let [, r, g, b] = this.strokeStyle.match(/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i).map(hex => parseInt(hex, 16));
  3946. if(r > g && r > b){
  3947. this.strokeStyle = userPreferences.rainbowMapBorder ? hslToHex(Math.floor(Date.now() / userPreferences.rainbowBorderSpeed) % 360, 100, 50) : settings.theme[5].color;
  3948. }
  3949. }
  3950. }
  3951. }
  3952. return _lineTo.apply(this, arguments);
  3953. }
  3954.  
  3955.  
  3956. let currentMass = 0, leaderboardPos;
  3957. const _fillText = CanvasRenderingContext2D.prototype.fillText;
  3958. CanvasRenderingContext2D.prototype.fillText = function() {
  3959. if ((this.fillStyle == "#ffffff" || this.fillStyle == "#626262") && isNaN(arguments?.[0]) && /^Mass: \d+$/gm.test(arguments[0])){
  3960. currentMass = +arguments[0].match(/(?<=^Mass: )\d+$/gm)[0];
  3961. if(hiddenUI) arguments[0] = " ";
  3962. } else if(this.canvas.id == "leaderboard" && this.fillStyle == "#ffaaaa" && /^\d+(?=\.\s)/gm.test(arguments[0])) [leaderboardPos] = arguments[0].match(/^\d+(?=\.\s)/gm);
  3963. _fillText.apply(this, arguments);
  3964. }
  3965.  
  3966. const debounce = (func, timeout = 300) => {
  3967. let timer;
  3968. return (...args) => {
  3969. clearTimeout(timer);
  3970. timer = setTimeout(() => { func.apply(this, args); }, timeout);
  3971. };
  3972. }
  3973.  
  3974. const isLogged = () => currentUser != 'Please Login First';
  3975. let intervalCount = 0, currentUser, lastLoggedOut = Date.now();
  3976.  
  3977. // const mainInterval = setInterval(() => {
  3978. const mainInterval = () => {
  3979. intervalCount++;
  3980. if(userPreferences.hoverShowSkinID && $('#publicSkinsPage').children().length > 0 && $('#publicSkinsPage').find('[id^="skinContainer"]>img')[0]?.title == ''){ // check if skins have loaded
  3981. $('[id^="skinContainer"]').each(function(){
  3982. $(this).find('img').attr('title', $(this).attr('id').replace('skinContainer', '')); // make hover show skin ID
  3983. })
  3984. $('.publicskins-nav-btn').on('click', () => {
  3985. $('[id^="skinContainer"]').each(function(){
  3986. $(this).find('img').attr('title', $(this).attr('id').replace('skinContainer', ''));
  3987. })
  3988. })
  3989. }
  3990. if(userPreferences.sortWearablesByOwned && $('#phpWearables>li').length && !$('#fsfb-wearsloaded').length){
  3991. $($('[id^="wearableUseBtn"]').get().reverse()).each(function(){
  3992. $($(this).parents().get(2)).insertBefore($("#phpWearables li:eq(0)"));
  3993. })
  3994. $('#phpWearables').append('<div id="fsfb-wearsloaded"></div>')
  3995.  
  3996. }
  3997. if(userPreferences.publicSkinSearch && $('#publicSkinsPage').children().length > 0 && !document.getElementById("fsfb-skinsearch")){
  3998. $('.publicskins-nav-bar').eq(0).after(`<input id="fsfb-skinsearch" placeholder="Enter skin name/id here" type="search">`);
  3999. // const handlePress = debounce(() => {
  4000. const handlePress = () => {
  4001. if(!$('#fsfb-skinsearch').val()) return void($('.btn.publicskins-nav-btn.btn-default').not('.btn-primary')[0].click());
  4002. const searchQuery = $('#fsfb-skinsearch').val();
  4003. const skinsSearchedArr = skinsArr.filter(skin => skin.type == 4 && (skin.name.toLowerCase().includes(searchQuery.toLowerCase()) || skin.id == +searchQuery));
  4004. let totalRows = Math.ceil(skinsSearchedArr.length / 4);
  4005. if(totalRows == 0){
  4006. $('#publicSkinsPage tbody').html('').append('<h1>No Skins Found</h1>');
  4007. } else {
  4008. $('#publicSkinsPage tbody').html('');
  4009. for(let i = 0; i < totalRows; i++) $('#publicSkinsPage tbody').append('<tr></tr>');
  4010. let currRow = 0, currColumn = 0;
  4011. for(let i of skinsSearchedArr){
  4012. if(++currColumn > 4) currRow++, currColumn = 1;
  4013. $('#publicSkinsPage tr').eq(currRow).append(`<td id="skinContainer${i.id}" class="skin-container"><img src="skins/${i.id}_lo.png" alt="" ${userPreferences.hoverShowSkinID ? 'title="' + i.id + '"': ''}><h4>${softSanitize(i.name)}</h4><button id="skinUseBtn${i.id}" class="btn btn-primary skinuse-btn" onclick="toggleSkin(${i.id});">Use</button></td>`)
  4014. }
  4015. }
  4016. }
  4017. // }, 300);
  4018. let pressTimer;
  4019. $('#fsfb-skinsearch').on('input', () => {
  4020. clearTimeout(pressTimer);
  4021. pressTimer = setTimeout(() => handlePress(), Math.round(300 / ($('#fsfb-skinsearch').val().length || 300)));
  4022. });
  4023. }
  4024. currentUser = $('#userCoins2')[0].innerText;
  4025. let user_abil = currentUser == 'Please Login First' ? null : misc_settings.abil?.[currentUser];
  4026. if(user_abil != undefined && userPreferences.showRemainingAbilityTime){
  4027. for(let i in user_abil){
  4028. let text = $(`.purchase-btn.confirmation[item="${i}"]`).parents().eq(0).find('div h5'),
  4029. active = $('#' + $(`.purchase-btn.confirmation[item="${i}"]`).parents().eq(0)[0].id + ' img').eq(1).css('display') != "none";
  4030. // has been 24h+ and the player hasn't logged out since it's expired
  4031. if(Date.now() - user_abil[i] > 8.64e7 && active){
  4032. text.eq(1).text('EXPIRED IF UNLOG');
  4033. text.eq(0).find('div h5').hide();
  4034. }
  4035. // has been >24h
  4036. else if(Date.now() - user_abil[i] < 8.64e7 && active){
  4037. text.eq(0).hide();
  4038. text.eq(1).text(msToTime(8.64e7 - (Date.now() - user_abil[i]))).show();
  4039. }
  4040. else { // has been 24h+ & player has logged out
  4041. text.eq(0).find('div h5').show();
  4042. text.eq(1).find('div h5').hide();
  4043. }
  4044. }
  4045. } else {
  4046. $('.white_shopdesc').show();
  4047. $('.white_shopdesc.fsfb-fake').hide();
  4048. }
  4049. if(accounts[currentUser] == null && currentUser !== 'Please Login First'){
  4050. xpInfo();
  4051. coinsInfo();
  4052. accounts = {...accounts, ...{[currentUser] : {coins: currentCoins, xp: currentXP, lvl: round(currentPercent, 3) + currentLevel}}};
  4053. }
  4054. if(accounts[currentUser] != null && accounts[currentUser].coins == 0) accounts[currentUser].coins = currentCoins;
  4055. if(accounts[currentUser] != null && accounts[currentUser].xp == 0) accounts[currentUser].xp = currentXP;
  4056. if(accounts[currentUser] != null && accounts[currentUser].lvl == 0) accounts[currentUser].lvl = round(currentPercent, 3) + currentLevel;
  4057.  
  4058. if(userPreferences.coinXPstats && intervalCount % 12 == 0){
  4059. xpInfo();
  4060. lastMinXP.push(new StatLog(round(currentXP, 3), round(currentPercent, 3) + currentLevel, currentUser, lastMinXP));
  4061. const prevObjXP = lastMinXP[lastMinXP.length - 1];
  4062. if(prevObjXP && prevObjXP.id % (6e4 / updateTimeXP) == 0) lastHrXP.push(new StatLog(round(currentXP, 3), round(currentPercent, 3) + currentLevel, currentUser, lastHrXP));
  4063. if(lastMinXP.length > 6e4 / updateTimeXP) lastMinXP.shift();
  4064. if(lastHrXP.length > 60) lastHrXP.shift();
  4065. if(!coinsHTMLactive && $('#stats-container').css('display') == 'block') updateUI();
  4066. // unsafeWindow.logStatsScriptXP && console.log(lastMinXP, lastHrXP);
  4067. }
  4068. if(userPreferences.coinXPstats && intervalCount % 6 == 0){
  4069. coinsInfo();
  4070. lastMinCoins.push(new StatLog(currentCoins, 0, currentUser, lastMinCoins));
  4071. const lastObjCoins = lastMinCoins[lastMinCoins.length - 1];
  4072. if(lastObjCoins && lastObjCoins.id % (6e4 / updateTimeCoins) == 0) lastHrCoins.push(new StatLog(currentCoins, 0, currentUser, lastHrCoins));
  4073. if(lastMinCoins.length > 6e4 / updateTimeCoins) lastMinCoins.shift();
  4074. if(lastHrCoins.length > 60) lastHrCoins.shift();
  4075. if(coinsHTMLactive && $('#stats-container').css('display') == 'block') updateUI();
  4076. // unsafeWindow.logStatsScriptCoins && console.log(lastMinCoins, lastHrCoins);
  4077. }
  4078. if(userPreferences.coinXPstats && $('#stats-container').css('display') == 'block')$('#stats-sesh-length').text(msToTime(Date.now() - (coinsHTMLactive ? scriptStartCoins : scriptStartXP)))
  4079. if(intervalCount % 3 == 0 && misc_settings?.statsPos != null){
  4080. statsboxPos = {top: $("#stats-container")[0].style.top, left: $("#stats-container")[0].style.left};
  4081. misc_settings.statsPos = statsboxPos;
  4082. setStorage("fsfb-misc", misc_settings);
  4083. }
  4084. if(intervalCount % 2 == 0){ // "You have an activated bot pack available: 100 XXL Bots 1 Hours! Restart your bots before they expire!"
  4085. if(chatmsgs != null && chatmsgs?.length > 2){ // "You have an activated bot pack available: 100 Bots 24 Hours! Restart your bots before they expire!"
  4086. for(let i of chatmsgs){
  4087. if(i.name == '' && i.cache != null && i.cache.color2 == '#ff8100'){
  4088. if(i.message.match(/(?<=(Welcome back to Agma, )).+/g)?.[0] == currentUser){
  4089. const msgBots = $('.memberType').text() == 'GOLD MEMBER' ? chatmsgs?.[chatmsgs.indexOf(i) - 2] : chatmsgs?.[chatmsgs.indexOf(i) - 1],
  4090. msgGM = $('.memberType').text() == 'GOLD MEMBER' ? chatmsgs?.[chatmsgs.indexOf(i) - 1] : null;
  4091. if(msgBots?.message.includes('Restart your bots before they expire!')){
  4092. if(misc_settings.bots[currentUser] != null) misc_settings.bots[currentUser].chatAmt = msgBots.message.match(/\d+.+\d Hours/g)[0];
  4093. minsChatAmt[currentUser] = {...minsChatAmt[currentUser], ... {amt: msgBots.message.match(/\d+.+\d Hours/g)[0], started: true}};
  4094. setStorage("fsfb-misc", misc_settings);
  4095. } else if(msgBots?.message.match(/(?<=(You have a new bot pack available: )).+(?=(! Start your bots in the minion panel.))/g)?.length){
  4096. if(misc_settings.bots[currentUser] != null) misc_settings.bots[currentUser].chatAmt = msgBots.message.match(/\d+.+\d Hours/g)[0];
  4097. minsChatAmt[currentUser] = {...minsChatAmt[currentUser], ... {amt: msgBots.message.match(/\d+.+\d Hours/g)[0], started: false}};
  4098. setStorage("fsfb-misc", misc_settings);
  4099. }
  4100. if(msgGM != null && msgGM.message.match(/(?<=You have )\d+(?= Days left of Gold Member!)/gm)?.length){
  4101. accGoldMem[currentUser] = {...accGoldMem[currentUser], ... {days: msgGM.message.match(/(?<=You have )\d+(?= Days left of Gold Member!)/gm)[0], has: true}};
  4102. } else {
  4103. accGoldMem[currentUser] = {...accGoldMem[currentUser], ... {has: false}};
  4104. }
  4105. } else if(i.message.match(/(?<=(You have a new bot pack available: )).+(?=(! Start your bots in the minion panel.))/g)?.length){
  4106. if(misc_settings.bots[currentUser] != null) misc_settings.bots[currentUser].chatAmt = i.message.match(/\d+.+\d Hours/g)[0];
  4107. minsChatAmt[currentUser] = {...minsChatAmt[currentUser], ... {amt: i.message.match(/\d+.+\d Hours/g)[0], started: false}};
  4108. setStorage("fsfb-misc", misc_settings);
  4109. }
  4110. }
  4111. }
  4112. }
  4113. }
  4114. if(currentServerId === 0){
  4115. try {
  4116. for(let { isCurrent, id } of JSON.parse(localStorage.gameservers)){
  4117. if(isCurrent) currentServerId = id;
  4118. }
  4119. if(currentServerId !== 0) svSwitch = true;
  4120. } catch {};
  4121. }
  4122. uisdoa && (unsafeWindow.__currentServerId = currentServerId);
  4123. if(currentUser == 'Please Login First' || $('#level').text() == 0) lastLoggedOut = Date.now();
  4124. changeTitle(settings.checkboxes[4].active ? currentUser == 'Please Login First' ? "Agma.io" : "Agma.io - " + currentUser : "Agma.io - A free multiplayer MMO game");
  4125.  
  4126. if($('#friendAcceptAll').length > 0 && userPreferences.friendDeclineAll && $('#friendRejectAll').length < 1 && currentUser != 'Please Login First') addFriendDecline();
  4127.  
  4128. if($('#friendDialogMessage').text() != 'Login to see your friendlist' && $('#friendDialogMessage').text() == 'Loading...' && $('#friendsRequestsAmt').text() == '' && userPreferences.friendDeclineAll && currentUser != 'Please Login First'){
  4129. $('#btnFriends').click().click();
  4130. }
  4131.  
  4132. fpsArr.push(+document.getElementById("fps").innerText);
  4133. if(fpsArr.length == 6) fpsArr.shift();
  4134. avgFps = mean(fpsArr);
  4135. setTimeout(mainInterval, 1e3);
  4136. }
  4137. setTimeout(mainInterval, 1e3);
  4138. // }, 1e3);
  4139.  
  4140.  
  4141. const antiAFK = () => {
  4142. setTimeout(antiAFK, 3e4);
  4143. if(!$('#fsfb-antiAFK').is(':checked')) return; // move mouse every 30sec
  4144. let [moveX, moveY] = linesplitting ? [pointMove.x, pointMove.y] : [mosX, mosY];
  4145. [++moveX, --moveX].forEach(x => $('#canvas').trigger($.Event('mousemove', {clientX: x, clientY: moveY})));
  4146. }
  4147.  
  4148. setTimeout(antiAFK, 3e4);
  4149.  
  4150. uisdoa ?? $('#fsfb-recospeed').parent().hide();
  4151.  
  4152. const updateScriptSettingsUI = () => {
  4153. for(let i of settings.checkboxes) $('#' + i.id).prop("checked", i.active).trigger("change");
  4154. for(let i of settings.hotkeys) $('#' + i.id).text(getName(i.key));
  4155. for(let i of settings.quickSettings){
  4156. $('#' + i.id1).val(i.set);
  4157. $('#' + i.id).text(getName(i.key));
  4158. }
  4159. // slowfeed
  4160. $('#' + settings.slowFeed[0].id).text(getName(settings.slowFeed[0].key));
  4161. $('#' + settings.slowFeed[1].id).val(settings.slowFeed[1].val);
  4162.  
  4163. $('#' + settings.frozenvirus[0].id).text(getName(settings.frozenvirus[0].key));
  4164. $('#' + settings.frozenvirus[1].id).val(settings.frozenvirus[1].val);
  4165.  
  4166. for(let i of settings.fastsplit_hotkeys){
  4167. i.val == null ? $('#' + i.id).text(getName(i.key)) : $('#' + i.id).val(i.val);
  4168. }
  4169. for(let i of settings.uiScaling) $('#' + i.id).val(i.level).trigger("input");
  4170. for(let i of settings.export_import) $('#' + i.id).prop("checked", i.active).trigger("change");
  4171. for(let i of settings.theme){
  4172. $('#' + i.id).prop("checked", i.active).trigger("change");
  4173. $('#' + i.id1).val(i.color).trigger("input");
  4174. }
  4175. for(let i of settings.theme_boxes) $('#' + i.id).prop("checked", i.active).trigger("change");
  4176. for(let i of settings.anti_lag) $('#' + i.id).prop("checked", i.active).trigger("change");
  4177. for(let i of settings.chat_translate){
  4178. 'set' in i ? $('#' + i.id).val(i.set) : $('#' + i.id).prop('checked', i.active).trigger('change');
  4179. }
  4180. }
  4181. setTimeout(() => updateScriptSettingsUI(), 1e3);
  4182.  
  4183. $('body').append('<div id="fsfb-css-styles"><style id="hideUI-css" type="text/css"></style><style id="css-invsingleline" type="text/css"></style><style id="stats-input-css" type="text/css">#stats-table input{ display: none; }</style></div>');
  4184.  
  4185. const _replaceCSS = (a,b) => {
  4186. document.getElementById(a).innerHTML = b;
  4187. }
  4188. !function(){
  4189. $('body').append(`
  4190. <div class="fade fsfb-bug-modal modal" aria-hidden=true role="dialog" tabindex="-1">
  4191. <div class="modal-dialog modal-lg">
  4192. <div class="modal-content">
  4193. <div class="modal-interior">
  4194. <h2 class="fsfb-modal-title">Script Documentation</h2><button class="close fsfb-btn" data-dismiss="modal" type=button>×</button>
  4195. <section class=fsfb-modal-body>
  4196. <div><span>Chat Copy/Cut/Paste</span> - allows you to use the commands in chat (e.g. Ctrl + V becomes avaiable inside chat)</div>
  4197. <div><span>Anti-AFK</span> - prevents you from automatically disconnecting after 10 minutes</div>
  4198. <div><span>Anti-Invis</span> - shows you players even when they have the invisibility ability active</div>
  4199. <div><span>Linesplit Toggle</span> - enabled means that if you press the linesplit hotkey, it will turn linesplitting on until the key is pressed again (in contrast to stopping the linesplit when key is released)</div>
  4200. <div><span>Change Page Title</span> - changes the tab's title to just "Agma.io" with the current username</div>
  4201. <div><span>Hide Shouts</span> - prevent megaphone shouts from showing up at all</div>
  4202. <div><span>Hold To Spam</span> - while the powerup's hotkey is held, it will continuously use the powerup</div>
  4203. <div><span>Show Portal Mass</span> - displays the predicted amount of times the portals in rooms 1 & 2 have been fed by players (not 100% accurate & doesn't work at all servers)</div>
  4204. <div><span>Power Spawns Overlay</span> - show the locations of where powerups/minion packs spawn with lower opacity (thanks to Light for helping with getting all of the power locations)</div>
  4205. <div><span>Quick Buy</span> - click plus sign (+) next to your powers (only if you set it to true in the code), then click on the powerup you want to buy</div>
  4206. <div><span>Food/Virus/Mothercell Color</span> - changes the color that's filling these to a custom one</div>
  4207. <div><span>Virus/Mothercell Stroke</span> - changes the color of the stroke (border/outline) to a custom one</div>
  4208. <div><span>Spiked Cells</span> - render all cells with the spikes from the infecton gamemode</div>
  4209. <div><span>Show Mass</span> - show the mass of all players' cells</div>
  4210. <div><span>Only My Skin</span> - hide all skins besides the one you're using</div>
  4211. <div><span>Only Party Skin</span> - hide all skins besides the ones people in your party are using</div>
  4212. <div><span>Only My Nick</span> - hide all nicks besides the one you're using</div>
  4213. <div><span>Only Party Nick</span> - hide all nicks besides the ones people in your party are using</div>
  4214. <div><span>Shoot 7 Ejected</span> - press ejected mass hotkey 7 times (useful to prime room 1 or 2 portal when it's been reset</div>
  4215. <div><span>Linesplit Lock</span> - finds which direction your mouse is the closest to & puts mouse way off the map (towards that direction) so you can perform perfect linesplits without zooming out or precisely placing your mouse (feature and design inspired by <a href=https://greasyfork.org/en/scripts/404559-agma-io-linesplit-overlay target=_blank>Wynell's script</a>)</div>
  4216. <div><span>Macrosplit Bots</span> - hold this key to macrosplit your bots without switching controls off of yourself</div>
  4217. <div><span>Hide UI</span> - press this key to toggle showing the game UI (intended for recording/screenshots)</div>
  4218. <div><span>Toggle Slow Feed</span> - (toggle) this presses eject mass hotkey at the defined interval (intended for feeding the gold block while AFK)</div>
  4219. <div><span>Slow Feed Speed</span> - the speed at which eject mass is pressed when slow-feeding</div>
  4220. <div><span>Quick Settings</span> - when the hotkey assigned is pressed, it will toggle the setting that is selected</div>
  4221. <div><span>Toggle Cursor Lock</span> - when pressed, this will keep cursor lock active until you press it again (also works when the tab is unfocused)</div>
  4222. <div><span>Fast Onesplit</span> - performs a fast onesplit (Onesplit -> Freeze -> Unfreeze); speed is dependent fps (low fps indicates slow CPU, slow CPU can mess timings up)</div>
  4223. <div><span>Fast Doublesplit</span> - performs a fast Doublesplit (Doublesplit -> Freeze -> Unfreeze); speed is dependent fps (low fps indicates slow CPU, slow CPU can mess timings up)</div>
  4224. <div><span>Chat Size</span> - make the size of chat bigger/smaller</div>
  4225. <div><span>Inventory Size</span> - make the size of powerups inventory bigger/smaller</div>
  4226. <div><span>Statsbox Size</span> - make the size of XP/coins stats bigger/smaller</div>
  4227. <div><span>Export</span> - select the boxes of the settings you wish to export and press the button, a .txt file will be downloaded with your settings inside</div>
  4228. <div><span>Import</span> - select the boxes of the settings you wish to import and insert the exported settings into the input (note: settings will only be changed if they were selected in both export & import</div>
  4229. <table>
  4230. <tr>
  4231. <th>Chat Command</th>
  4232. <th>Description</th>
  4233. </tr>
  4234. <tr>
  4235. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}help</td>
  4236. <td>list all available chat commands</td>
  4237. </tr>
  4238. <tr>
  4239. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}bots</td>
  4240. <td>show which bot pack you have active and how much time is remaining</td>
  4241. </tr>
  4242. <tr>
  4243. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}pws</td>
  4244. <td>show the amount of powerups in your inventory (recommended to use ${userPreferences.chatPrefix}pws1, ${userPreferences.chatPrefix}pws2, and ${userPreferences.chatPrefix}pws3)</td>
  4245. </tr>
  4246. <tr>
  4247. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}totalpws</td>
  4248. <td>show the total amount of powerups you have in your inventory</td>
  4249. </tr>
  4250. <tr>
  4251. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}xp</td>
  4252. <td>show then amount of xp you've completed for this level</td>
  4253. </tr>
  4254. <tr>
  4255. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}lvl</td>
  4256. <td>show your level and how much of the level you've completed</td>
  4257. </tr>
  4258. <tr>
  4259. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}coins</td>
  4260. <td>show the amount of coins you have</td>
  4261. </tr>
  4262. <tr>
  4263. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}hours</td>
  4264. <td>show the hours you have on your account</td>
  4265. </tr>
  4266. <tr>
  4267. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}rank</td>
  4268. <td>show your ranking</td>
  4269. </tr>
  4270. <tr>
  4271. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}ping</td>
  4272. <td>show your current ping</td>
  4273. </tr>
  4274. <tr>
  4275. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}fps</td>
  4276. <td>show your current FPS</td>
  4277. </tr>
  4278. <tr>
  4279. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}topmass</td>
  4280. <td>show your highest mass</td>
  4281. </tr>
  4282. <tr>
  4283. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}cells</td>
  4284. <td>show your cell count</td>
  4285. </tr>
  4286. <tr>
  4287. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}pws1</td>
  4288. <td>show the first part of your powerup inventory</td>
  4289. </tr>
  4290. <tr>
  4291. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}pws2</td>
  4292. <td>show the second part of your powerup inventory</td>
  4293. </tr>
  4294. <tr>
  4295. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}pws3</td>
  4296. <td>show the third part of your powerup inventory</td>
  4297. </tr>
  4298. <tr>
  4299. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}friends</td>
  4300. <td>show how many friends you have and how many are online</td>
  4301. </tr>
  4302. <tr>
  4303. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}requests</td>
  4304. <td>show how many friend requests you have</td>
  4305. </tr>
  4306. <tr>
  4307. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}gold</td>
  4308. <td>show how many days of gold member you have left remaining</td>
  4309. </tr>
  4310. <tr>
  4311. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}alive</td>
  4312. <td>show the amount of time you've been alive</td>
  4313. </tr>
  4314. <tr>
  4315. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}mass</td>
  4316. <td>show your current mass</td>
  4317. </tr>
  4318. <tr>
  4319. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}user</td>
  4320. <td>show your current username</td>
  4321. </tr>
  4322. <tr>
  4323. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}customs</td>
  4324. <td>show the amount of custom skins you own</td>
  4325. </tr>
  4326. <tr>
  4327. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}wearables</td>
  4328. <td>show how many wearables you own</td>
  4329. </tr>
  4330. <tr>
  4331. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}cloak</td>
  4332. <td>show the remaining time of your cloak ability</td>
  4333. </tr>
  4334. <tr>
  4335. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}add [user]</td>
  4336. <td>type this command to quickly add a friend using chat</td>
  4337. </tr>
  4338. <tr>
  4339. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}partymembers</td>
  4340. <td>show how many people are in your party</td>
  4341. </tr>
  4342. <tr>
  4343. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}players</td>
  4344. <td>show how many players are online in your server and are online in all agma servers</td>
  4345. </tr>
  4346. <tr>
  4347. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}server</td>
  4348. <td>show which server you're currently in</td>
  4349. </tr>
  4350. <tr>
  4351. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}abils</td>
  4352. <td>show your currently active abilities</td>
  4353. </tr>
  4354. <tr>
  4355. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}xpproj</td>
  4356. <td>show the predicted amount of XP you will gain in an hour</td>
  4357. </tr>
  4358. <tr>
  4359. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}coinsproj</td>
  4360. <td>show the predicted amount of coins you will gain in an hour</td>
  4361. </tr>
  4362. <tr>
  4363. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}xprem</td>
  4364. <td>show the amount of XP remaining for your level</td></td>
  4365. </tr>
  4366. <tr>
  4367. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}xphour</td>
  4368. <td>show the amount of xp you've gained in the last hour</td>
  4369. </tr>
  4370. <tr>
  4371. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}coinshour</td>
  4372. <td>show the amount of coins you've gained in the last hour</td>
  4373. </tr>
  4374. <tr>
  4375. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}xpmin</td>
  4376. <td>show the amount of XP you've gained in the last minute</td>
  4377. </tr>
  4378. <tr>
  4379. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}coinsmin</td>
  4380. <td>show the amount of coins you've gained in the last minute</td>
  4381. </tr>
  4382. <tr>
  4383. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}xp12s</td>
  4384. <td>show the amount of coins you've gained in the last 12 seconds</td>
  4385. </tr>
  4386. <tr>
  4387. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}xpsesh</td>
  4388. <td>show the amount of XP you've gained in this session</td>
  4389. </tr>
  4390. <tr>
  4391. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}lifetimexp</td>
  4392. <td>show the total amount of XP you've earned in your account's lifetime</td>
  4393. </tr>
  4394. <tr>
  4395. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}seshcoins</td>
  4396. <td>show the amount of coins you've gained in this session</td>
  4397. </tr>
  4398. <tr>
  4399. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}seshcoins</td>
  4400. <td>show the amount of coins you've gained in this session</td>
  4401. </tr>
  4402. <tr>
  4403. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}waifu [user]</td>
  4404. <td>rate the waifu of the selected username (leave user blank to rate yourself)</td>
  4405. </tr>
  4406. <tr>
  4407. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}pro [user]</td>
  4408. <td>show how pro someone is (leave user blank to rate yourself)</td>
  4409. </tr>
  4410. <tr>
  4411. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}dog [user]</td>
  4412. <td>show how dog someone is (leave user blank to rate yourself)</td>
  4413. </tr>
  4414. <tr>
  4415. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}king [user]</td>
  4416. <td>show how king someone is (leave user blank to rate yourself)</td>
  4417. </tr>
  4418. <tr>
  4419. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}dice [sides]</td>
  4420. <td>roll a die with the desired number of sides</td>
  4421. </tr>
  4422. <tr>
  4423. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}rng [min] [max]</td>
  4424. <td>generate a random number in a range</td>
  4425. </tr>
  4426. <tr>
  4427. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}coinflip</td>
  4428. <td>flip a coin and see if it lands on heads or tails</td>
  4429. </tr>
  4430. <tr>
  4431. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}script</td>
  4432. <td>show the current script you're using & which version</td>
  4433. </tr>
  4434. <tr>
  4435. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}time</td>
  4436. <td>show your current locale date & time</td>
  4437. </tr>
  4438. <tr>
  4439. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}skins</td>
  4440. <td>show your bought skins and their worth (limited as well)</td>
  4441. </tr>
  4442. <tr>
  4443. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}ratefriends [user1] [user2]</td>
  4444. <td>show how what percent friends two usernames are</td>
  4445. </tr>
  4446. <tr>
  4447. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}rateenemies [user1] [user2]</td>
  4448. <td>show how what percent enemies two usernames are</td>
  4449. </tr>
  4450. <tr>
  4451. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}leaderboard</td>
  4452. <td>show your leaderboard position</td>
  4453. </tr>
  4454. <tr>
  4455. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}altcaps [text]</td>
  4456. <td>anything written after this command will have alternating lowercase/capital letters</td>
  4457. </tr>
  4458. <tr>
  4459. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}sparkles [text]</td>
  4460. <td>anything written after this command will be surrounded with star emojis</td>
  4461. </tr>
  4462. <tr>
  4463. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}cfix</td>
  4464. <td>resets canvas, can sometimes fix bugs with rendering</td>
  4465. </tr>
  4466. <tr>
  4467. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}fact</td>
  4468. <td>sends a random fact in chat</td>
  4469. </tr>
  4470. <tr>
  4471. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}fastsplit</td>
  4472. <td>show your fastsplit delays</td>
  4473. </tr>
  4474. <tr>
  4475. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}progress</td>
  4476. <td>show your progress for the daily challenge</td>
  4477. </tr>
  4478. <tr>
  4479. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}sorapoints</td>
  4480. <td>show how many SoraPoints you have</td>
  4481. </tr>
  4482. <tr>
  4483. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}keys</td>
  4484. <td>show how many myster box keys you have</td>
  4485. </tr>
  4486. <tr>
  4487. <td class="fsfb-cmd-title">${userPreferences.chatPrefix}stars</td>
  4488. <td>show how many challenge stars you have</td>
  4489. </tr>
  4490. <table>
  4491. <div><span>Hide Ads</span> - both video and image ads will be removed from the screen</div>
  4492. <div><span>Skin Search</span> - search through skins by their names/ids</div>
  4493. <div><span>Improved Shop</span> - added larger amounts that can be purchased at a time, can also buy a specified amount at one time</div>
  4494. <div><span>Sort Wearables</span> - wearables are automatically sorted by owned (the ones you own will be before all others)</div>
  4495. <div><span>Extra Bot Packs</span> - added hidden bot packs that can be purchased with coins (originally discovered by firebone)</div>
  4496. <div><span>Context Menu Copy Info</span> - right click on a player, then click on their cell icon to copy their skin ID to clipboard or click on their name to copy their nickname to clipboard</div>
  4497. <div><span>Copy Chat</span> - right click on screen, then click "Copy Chat Messages" to copy the currently visible chat messages to your clipboard</div>
  4498. <div><span>Abilities Remaining Time</span> - shows the remaining time left of abilities (only works if the abilities were purchased in the same browser)</div>
  4499. <div><span>Unlock Free Skins</span> - gives you access to the facebook & youtube free skins</div>
  4500. <div><span>Hover For Skin ID</span> - hovering skins in the skin menu will shop their ID</div>
  4501. <div><span>In Depth XP/Coins Stats</span> - click on coins/xp progress bar in top left to view respective statistics</div>
  4502. <div><span>XP Bar Decimals</span> - show the percentage up to 2 decimal places</div>
  4503. <div><span>White Border For Black Cells</span> - show a white border around black cells (from minion nuker) so they're easier to see with dark backgrounds</div>
  4504. <div><span>Inventory Single Row</span> - put powerups inventory on a single row instead of on 2 seperate rows (inspired by Principito)</div>
  4505. <div><span>Custom Backgrounds</span> - a few <a href=https://imgur.com/a/sTANNBE target=_blank>backgrounds</a></div>
  4506. </section>
  4507. </div>
  4508. </div>
  4509. </div>
  4510. </div>`);
  4511. }();
  4512.  
  4513. !function(){
  4514. const styles = document.createElement('style');
  4515. styles.innerHTML = `
  4516. p.fsfb-lag::after{
  4517. content: '*';
  4518. position: absolute;
  4519. margin-left: 1px;
  4520. }
  4521. input.fsfb-lag+p::after{
  4522. content: '*';
  4523. position: absolute;
  4524. margin-left: 1px;
  4525. }
  4526. #fsfb-settings-left .hotkey-paragraph{
  4527. margin: 0;
  4528. }
  4529. ${userPreferences.stylizeActiveSettings ? `
  4530. #fsfb-settings-main input:checked+p {
  4531. color: #df901c;
  4532. }
  4533. .fsfb-active-setting{
  4534. color: #df901c;
  4535. }` : ''}
  4536. #fsfb-freeze_yourself {
  4537. background-image: url(../img/store/freeze_yourself.png);
  4538. }
  4539. #fsfb-minion_nuker {
  4540. background-image: url(../img/store/minion_nuker.png);
  4541. }
  4542. #fsfb-megaphone_shout {
  4543. background-image: url(../img/store/megaphone_shout.png);
  4544. }
  4545. .fsfb-update-swal .cancel{
  4546. background-color: #29b962 !important;
  4547. }
  4548. .fsfb-update-swal button:hover{
  4549. opacity: 75%;
  4550. }
  4551. a.fsfb-curser-anchor{
  4552. color: #8CEFFF;
  4553. }
  4554. a.fsfb-curser-anchor:hover{
  4555. opacity: 70%;
  4556. }
  4557. select.fsfb-changelang{
  4558. background: #a8a8a833;
  4559. border-radius: 3px;
  4560. border: none;
  4561. height: 20px;
  4562. color: #ffffffad;
  4563. margin: 2px 0 0 0;
  4564. }
  4565. select.fsfb-changelang:focus-visible{
  4566. outline: none;
  4567. }
  4568. select.fsfb-changelang option{
  4569. background: #222;
  4570. }
  4571. #fsfb-wearsloaded{
  4572. display: none;
  4573. width: 0;
  4574. height: 0;
  4575. }
  4576. #fsfb-skinsearch{
  4577. border: 1px solid #2e6da4;
  4578. background-color: #222328;
  4579. font-size: 17px;
  4580. border-radius: 4px;
  4581. width: 100%;
  4582. padding: 4px 4px 4px 8px;
  4583. margin: 4px 0;
  4584. color: white;
  4585. }
  4586. #stats-table td{
  4587. font-size: 17px;
  4588. }
  4589. #stats-table label{
  4590. padding-right: 10px;
  4591. margin: 0 6px 0 2px;
  4592. }
  4593. #stats-table input{
  4594. transform: scale(1.8);
  4595. margin: 5px 8px 5px 5px;
  4596. }
  4597. #friendRejectAll{
  4598. color: #ff4000 !important;
  4599. }
  4600. div.fsfb-slider{
  4601. display: flex;
  4602. align-items: center;
  4603. }
  4604. input[type="range"].fsfb-slider{
  4605. width: 58px;
  4606. display: inline;
  4607. position: absolute;
  4608. right: 5px;
  4609. }
  4610. #fsfb-minion_nuker img{
  4611. margin-top: 2px;
  4612. }
  4613. .fsfb-shown{
  4614. display: block !important;
  4615. }
  4616. .fsfb-hidden{
  4617. display: none !important;
  4618. }
  4619. #fsfb-quickbuy{
  4620. background-image: url('data:image/svg+xml;charset=UTF-8,<svg xmlns="http://www.w3.org/2000/svg" fill="white" viewBox="0 0 601 601"><path d="M1 301c0-31 23-54 49-54l198 1V50c0-26 23-49 53-49s53 23 53 49v198l197-1c26 0 49 23 49 54 0 30-23 53-49 53l-197-1v198c0 26-23 50-53 50s-53-24-53-50V354H50c-26 0-50-23-50-53"></path></svg>');
  4621. background-size: 80% 80%;
  4622. }
  4623. .fsfb-bug-modal>div>div{
  4624. -webkit-box-shadow: 0 5px 15px rgb(0 0 0 / 50%);
  4625. background: linear-gradient(to bottom,#3b414e 0,#302f33 100%);
  4626. border: 3px solid #232630;
  4627. }
  4628. .close.fsfb-btn{
  4629. position: absolute;
  4630. right: 10px;
  4631. top: 3px;
  4632. font-size: 60px;
  4633. }
  4634. .fsfb-modal-body>div{
  4635. color: ffffffb0;
  4636. margin: 10px 20px;
  4637. }
  4638. .fsfb-modal-body>table{
  4639. width: 90%;
  4640. margin: 10px 20px;
  4641. }
  4642. .fsfb-cmd-title{
  4643. color: white;
  4644. white-space: nowrap;
  4645. padding-right: 12px !important;
  4646. }
  4647. .fsfb-modal-body>table th{
  4648. color: white;
  4649. }
  4650. .fsfb-modal-body>table td{
  4651. padding: 4px 0;
  4652. }
  4653. .fsfb-modal-body>div>span{
  4654. color: white;
  4655. }
  4656. .fsfb-modal-body{
  4657. margin: 10px 10px;
  4658. font-size: 20px;
  4659. max-height: 600px;
  4660. overflow-y: auto;
  4661. }
  4662. .fsfb-modal-title{
  4663. text-align: center;
  4664. color: white;
  4665. }
  4666. .fsfb-hotkey{
  4667. background-color: #df901c;
  4668. color: #fff;
  4669. cursor: pointer;
  4670. text-align: center;
  4671. min-width: 40px;
  4672. max-width: 60px;
  4673. height: 18px;
  4674. line-height: 18px;
  4675. vertical-align: middle;
  4676. border-radius: 9px;
  4677. right: 5px;
  4678. position: absolute;
  4679. display: inline-block;
  4680. padding: 0 5px;
  4681. overflow: hidden;
  4682. opacity: 1;
  4683. }
  4684. .fsfb-modal-body::-webkit-scrollbar-thumb {
  4685. background-color: #57595b;
  4686. border: 1px solid black;
  4687. border-radius: 12px;
  4688. }
  4689. .fsfb-modal-body::-webkit-scrollbar {
  4690. border: 1px solid black;
  4691. background-color: #2523239e;
  4692. width: 15px;
  4693. border-radius: 12px;
  4694. }
  4695. .fsfb-modal-body::-webkit-scrollbar-track {
  4696. -webkit-box-shadow: inset 0px 0px 2px 2px rgba(0, 0, 0, 0.75);
  4697. box-shadow: inset 0px 0px 2px 2px rgba(0, 0, 0, 0.75);
  4698. border-radius: 12px;
  4699. }
  4700. .fsfb-hotkey:hover {
  4701. background-color: #f1a02d;
  4702. }
  4703. .fsfb-hotkey.selected{
  4704. background-color: #ff4;
  4705. color: #444;
  4706. }
  4707. #fsfb-settings-main p{
  4708. margin: 0;
  4709. display: inline-block;
  4710. margin-left: 4px;
  4711. }
  4712. #fsfb-settings-main{
  4713. display: -ms-grid;
  4714. display: grid;
  4715. -ms-grid-columns: 50% 50%;
  4716. grid-template-columns: 50% 50%;
  4717. }
  4718. #settingPage4::-webkit-scrollbar-thumb {
  4719. background-color: #ff9800c2;
  4720. border-radius: 12px;
  4721. border: 1px #000000c2 solid;
  4722. }
  4723. #settingPage4::-webkit-scrollbar {
  4724. border: 1px solid #00000085;
  4725. background-color: #2523239e;
  4726. width: 9px;
  4727. border-radius: 12px;
  4728. }
  4729. #settingPage4{
  4730. display: none;
  4731. max-height: 660px;
  4732. overflow-x: hidden;
  4733. }
  4734. .padbot10{
  4735. padding-bottom: 10px;
  4736. }
  4737. #fsfb-slowfeedtime, #fsfb-dubsecdelay, #fsfb-dubfirstdelay, #fsfb-secdelay, #fsfb-fvsdelay, #fsfb-firstdelay{
  4738. border: none;
  4739. width: 40px;
  4740. }
  4741. select.fsfb-quickchange{
  4742. background: none;
  4743. border: none;
  4744. height: 20px;
  4745. }
  4746. select.fsfb-quickchange:focus-visible{
  4747. outline: none;
  4748. }
  4749. select.fsfb-quickchange option{
  4750. background: #222;
  4751. }
  4752. .fsfb-sect-ch label{
  4753. display: flex;
  4754. align-items: center;
  4755. }
  4756. .fsfb-sect-ch label input{
  4757. margin: 0 2px 0 0;
  4758. }
  4759. #fsfb-sect-theme label div{
  4760. height: 18px;
  4761. aspect-ratio: 1;
  4762. }
  4763. #fsfb-sect-theme label input[type="color"]{
  4764. width: 100%;
  4765. height: 100%;
  4766. opacity: 0;
  4767. border: none;
  4768. background-color: white;
  4769. margin: 0;
  4770. cursor: pointer;
  4771. }
  4772. #fsfb-sect-theme label p{
  4773. min-width: 120px;
  4774. margin-left: 5px;
  4775. }
  4776. #fsfb-sect-theme label div{
  4777. border-radius: 4px;
  4778. border: 1px solid #ffffff29;
  4779. }
  4780. #fsfb-ximport-cont{
  4781. display: flex;
  4782. justify-content: space-around;
  4783. margin-top: 7px;
  4784. }
  4785. .fsfb-eximport{
  4786. background-color: #df901c;
  4787. color: white;
  4788. padding: 5px 17px;
  4789. border-radius: 25px;
  4790. cursor: pointer;
  4791. }
  4792. .hideMegaphone{
  4793. display: none !important;
  4794. }
  4795. .fsfb-fake{
  4796. padding: 0 0 0 43px;
  4797. color: #cbff4e !important;
  4798. }
  4799. #fsfb-extra-info{
  4800. margin: 10% 0 0 90%;
  4801. cursor: pointer;
  4802. }
  4803. #linesplit-markers div {
  4804. background-color: transparent;
  4805. height: 15px;
  4806. aspect-ratio: 1;
  4807. position: fixed;
  4808. z-index: 999;
  4809. border: 2px solid rgb(255 255 255 / 80%);
  4810. border-radius: 50%;
  4811. display: none;
  4812. }
  4813. #linesplit-top {
  4814. top: -7.5px;
  4815. transform: translateX(-50%);
  4816. left: 50%;
  4817. }
  4818. #stats-container{
  4819. background: rgba(0,0,0,.5);
  4820. top: 200px;
  4821. position: absolute;
  4822. border: 1px white solid;
  4823. border-radius: 12px;
  4824. color:white;
  4825. left:30px;
  4826. }
  4827. #stats-main{
  4828. padding: 10px;
  4829. }
  4830. #stats-extra-info{
  4831. color: #00bbff;
  4832. font-size: 15px;
  4833. margin-bottom: 2px;
  4834. display: block;
  4835. }
  4836. #stats-info div{
  4837. display:flex;
  4838. }
  4839. #stats-info div p{
  4840. margin: 0;
  4841. }
  4842. .stats-completed{
  4843. margin: 9px 5px 0;
  4844. font-size: 12px;
  4845. bottom: 0;
  4846. color: rgb(255, 255, 255, .8);
  4847. }
  4848. #stats-title{
  4849. display: flex;
  4850. align-items: center;
  4851. justify-content: space-between;
  4852. }
  4853. #stats-title>div{
  4854. font-size: 15px;
  4855. margin-left: 8px;
  4856. cursor: pointer;
  4857. }
  4858. #stats-title>div>div{
  4859. padding: 0 5px;
  4860. }
  4861. #linesplit-right {
  4862. right: -7.5px;
  4863. transform: translateY(-50%);
  4864. top: 50%;
  4865. }
  4866. #linesplit-bottom {
  4867. bottom: -7.5px;
  4868. transform: translateX(-50%);
  4869. left: 50%;
  4870. }
  4871. #linesplit-left {
  4872. left: -7.5px;
  4873. transform: translateY(-50%);
  4874. top: 50%;
  4875. }
  4876. .fsfb-removeAds{
  4877. transform: translateX(9999%) !important;
  4878. }`;
  4879. document.querySelector('body').append(styles);
  4880. }();
  4881.  
  4882. if(userPreferences.level1kFix){
  4883. $('.innerBoxDashboard2>div>div').css('max-width', '210px');
  4884. $('#levelTopLeft').css({'min-width': '30%', 'max-width': '90px', width: 'unset'});
  4885. $('#fpsBox').css('left', '220px');
  4886. }
  4887.  
  4888. const scripts = document.createElement('script');
  4889. scripts.innerHTML = `const onlyNumberKey = (e, key) => (key = e.which || e.keyCode, 48 <= key && key <= 57);`;
  4890.  
  4891. let script_id = 446564; // main script
  4892. let version_timestamp = 1676737129303;
  4893. // @changelog fsfb 20k install update! Agma FPS improvements, new antilag settings, frozen-virus hotkey, change settings from TM menu, and bugfixes
  4894. if (+getStorage("lastUpdateCheck", "0") + 864e5 <= Date.now() && typeof GM_xmlhttpRequest == 'function' && userPreferences.notifyNewUpdates) {
  4895. try {
  4896. GM_xmlhttpRequest({
  4897. method: "GET",
  4898. url: `https://greasyfork.org/en/scripts/${script_id}/code?${Date.now()}`,
  4899. headers: {
  4900. 'Cache-Control': 'no-cache'
  4901. },
  4902. onload: function(xhrResponse) {
  4903. setStorage("lastUpdateCheck", String(Date.now()));
  4904. const rt = xhrResponse.responseText.replace(/&nbsp;?/gm, " ").replace(/&#x000A;/g, "\n").replace(/<li>/gm, "\n").replace(/<[^>]*>/gm, "");
  4905. if (+(rt?.match(/version_timestamp\s*=\s*([0-9]+)/)?.[1] ?? 0) > version_timestamp) {
  4906. let changelog = rt.match(/(?<=@changelog\s+)(?:\S).+$/gm)?.[0] ?? 'unable to find',
  4907. version = rt.match(/(?<=@version\s+)(?:\S).+$/gm)?.[0] ?? 'unable to find';
  4908. swal({
  4909. title: `<span style="color: #8CEFFF;">fsfb update!</span>`,
  4910. text: `<span style="color: #BBF6FF;">It appears there's a new update available for fsfb script. (version: ${version})</br>Changelog: ${changelog}</span>`,
  4911. type: "info",
  4912. confirmButtonColor: "#2cb7f7",
  4913. confirmButtonText: 'Install fsfb Update',
  4914. html: true,
  4915. focusCancel: true,
  4916. // cancelButtonColor: "#29b962",
  4917. // cancelButtonText: 'Install Auto-Updating fsfb',
  4918. // showCancelButton: true,
  4919. customClass: 'fsfb-update-swal'
  4920. }, function(val) {
  4921. if(val){ // install new update
  4922. unsafeWindow.open(`https://greasyfork.org/scripts/${script_id}/`);
  4923. }
  4924. });
  4925. }
  4926. }
  4927. });
  4928. } catch (err) {
  4929. console.error("FSFB: An error occurred while checking for updates:\n" + err);
  4930. }
  4931. }
  4932.  
  4933. document.querySelector('body').append(scripts);
  4934. unsafeWindow.fsfbScriptsLoaded = true;
  4935. // $('#gameSettingsTab a')[0].click(); // att
  4936. // $('#settingTab4')[0].click();
  4937. };
  4938.  
  4939.  
  4940. if(unsafeWindow.fsfbScriptsLoaded || unsafeWindow.fsfbEvListenerAdded) alert('It appears fsfb scripts is already loaded. It\'s recommended to only use one instance of fsfb at a time.');
  4941. (document.readyState === "complete" || document.readyState === "interactive" ? setTimeout(afterLoaded, 0) : (document.addEventListener("DOMContentLoaded", afterLoaded), unsafeWindow.fsfbEvListenerAdded = true));