fsfb scripts

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

目前為 2022-11-23 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name fsfb scripts
  3. // @namespace http://tampermonkey.net/
  4. // @homepage https://gf.qytechs.cn/en/scripts/446564/
  5. // @version 1.3.03
  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. // @run-at document-start
  10. // @icon https://i.imgur.com/8AASK55.png
  11. // @license GPL-3.0-or-later
  12. // @changelog improved swal that notifies about updates, added auto-updating fsfb option
  13. // @connect translate.google.com
  14. // @connect gf.qytechs.cn
  15. // @grant unsafeWindow
  16. // @grant GM_setValue
  17. // @grant GM_getValue
  18. // @grant GM_xmlhttpRequest
  19. // ==/UserScript==
  20.  
  21.  
  22. /* settings that you can't change in UI but might interfere with other scripts */
  23. let hideAds = true,
  24. improvedShop = true,
  25. extraBotPacks = true,
  26. rightClickCopyChat = true,
  27. rightClickCopyInfo = true,
  28. showRemainingAbilityTime = true,
  29. unlockFreeSkins = true,
  30. hoverShowSkinID = true,
  31. coinXPstats = true,
  32. saveStatsBoxPosition = true,
  33. showXPdecimals = true,
  34. whiteBorder4BlackCells = true,
  35. sortWearablesByOwned = true,
  36. linesplitClosestSide = false, // make the linesplit go to the closest side (adjusted) instead of closest bubble
  37. inventorySingleRow = false,
  38. friendDeclineAll = true,
  39. rainbowMapBorder = false,
  40. rainbowBorderSpeed = 5,
  41. extraChatCommands = true,
  42. chatPrefix = '/f ',
  43. bypassConfirmChatRules = true,
  44. publicSkinSearch = true,
  45. extraOneFastSplitDelay = false,
  46. notifyNewUpdates = true;
  47.  
  48.  
  49.  
  50. // ~~~~~~~~~ Don't change anything below this unless you know what you are doing ~~~~~~~~~
  51. const version = '1.3.03';
  52. let settings = {
  53. hotkeys: [
  54. {title: "Shoot 7 Ejected", id: "fsfb-key7Feed", key: 0, active: false}, // 0
  55. {title: "Linesplit Lock", id: "fsfb-linesplit", key: 0, active: false}, // 1
  56. {title: "Macro Split Bots", id: "fsfb-MacroSplitBots", key: 0, active: false}, // 2
  57. {title: "Hide UI", id: "fsfb-hideUI", key: 0, active: false}, // 3
  58. {title: "Toggle Cursor", id: "fsfb-togglecursor", key: 0, active: false} // 4
  59. ],
  60. fastsplit_hotkeys: [
  61. {title: "Fast Onesplit", id: "fsfb-fsOne", keyName: "", keyCode: 0, active: false}, // 0
  62. {title: "Fast Doublesplit", id: "fsfb-fsTwo", keyName: "", keyCode: 0, active: false}, // 1
  63. {title: "1st Delay (ms)", id: "fsfb-firstdelay", val: 60, active: false}, // 2
  64. {title: "2nd Delay (ms)", id: "fsfb-secdelay", val: 60, active: false} // 3
  65. ],
  66. checkboxes: [
  67. {title: "Chat Copy/Cut/Paste", id: "fsfb-copycutpaste", active: false}, // 0
  68. {title: "Anti-AFK", id: "fsfb-antiAFK", active: false}, // 1
  69. {title: "Anti-Invis", id: "fsfb-anticloak", active: false}, // 2
  70. {title: "Linesplit Toggle", id: "fsfb-linetoggle", active: false}, // 3
  71. {title: "Change Page Title", id: "fsfb-changetitle", active: false}, // 4
  72. {title: "Hide Shouts", id: "fsfb-hideshouts", active: false}, // 5
  73. {title: "Hold To Spam Rec/Spd", id: "fsfb-recospeed", active: false}, // 6
  74. {title: "Show Portal Mass", id: "fsfb-portalmass", active: false}, // 7
  75. {title: "Pow Spawns Overlay", id: "fsfb-powsoverlay", active: false}, // 8
  76. {title: "Quick Buy", id: "fsfb-qBuy", active: false} // 9
  77. ],
  78. slowFeed: [
  79. {title: "Toggle Feed", id: "fsfb-slowFeed", key: 0, active: false},
  80. {title: "Feed Speed", id: "fsfb-slowfeedtimer", val: 100, active: false}
  81. ],
  82. quickSettings: [
  83. {id: "fsfb-quick-hotkey1", id1: "fsfb-quick-select1", set: "cSkins", key: 0, active: false},
  84. {id: "fsfb-quick-hotkey2", id1: "fsfb-quick-select2", set: "cWearables", key: 0, active: false},
  85. {id: "fsfb-quick-hotkey3", id1: "fsfb-quick-select3", set: "cFood", key: 0, active: false},
  86. {id: "fsfb-quick-hotkey4", id1: "fsfb-quick-select4", set: "cBubbleCells", key: 0, active: false},
  87. {id: "fsfb-quick-hotkey5", id1: "fsfb-quick-select5", set: "cNames", key: 0, active: false}
  88. ],
  89. uiScaling: [
  90. // {title: "Chat Size", id: "fsfb-chatSize", level: 5},
  91. {title: "Inventory Size", id: "fsfb-invSize", level: 5},
  92. {title: "Statsbox Size", id: "fsfb-statsSize", level: 5}
  93. ],
  94. theme: [
  95. {title: "Food Color", id: "fsfb-check-foodcolor", id1: "fsfb-color-foodcolor", color: "#FFFFFF", active: false}, // 0
  96. {title: "Virus Color", id: "fsfb-check-viruscolor", id1: "fsfb-color-viruscolor", color: "#00ff00", active: false}, // 1
  97. {title: "Virus Stroke", id: "fsfb-check-virusstroke", id1: "fsfb-color-virusstroke", color: "#00ff00", active: false}, // 2
  98. {title: "Mothercell Color", id: "fsfb-check-msColor", id1: "fsfb-color-msColor", color: "#cd5564", active: false}, // 3
  99. {title: "Mothercell Stroke", id: "fsfb-check-msStroke", id1: "fsfb-color-msStroke", color: "#cd5564", active: false}, // 4
  100. {title: "Border Color", id: "fsfb-check-border", id1: "fsfb-color-border", color: "#CC3030", active: false} // 5
  101. ],
  102. theme_boxes: [
  103. {title: "Fancy Bubble Cells", id: "fsfb-bublecell", active: false}, // 0
  104. {title: "Show Player Mass", id: "fsfb-showmass", active: false}, // 1
  105. {title: "Only My Skin", id: "fsfb-myskins", active: false}, // 2
  106. {title: "Only Party Skins", id: "fsfb-partyskins", active: false}, // 3
  107. {title: "Only My Nick", id: "fsfb-mynick", active: false}, // 4
  108. {title: "Only Party Nicks", id: "fsfb-partynicks", active: false}, // 5
  109. {title: "Spiked Cells", id: "fsfb-spikedcells", active: false}, // 6
  110. {title: "Reverse Cell Order", id: "fsfb-revcell", active: false}, // 7
  111. {title: "Render Portals Top", id: "fsfb-portalstop", active: false} // 8
  112. ],
  113. chat_translate: [
  114. {title: "Translate Chat", id: "fsfb-tranchat", active: false}, // 0
  115. {title: "Translate Server", id: "fsfb-tranplyr", active: false}, // 1
  116. {title: "Show Original", id: "fsfb-tranorig", active: false}, // 2
  117. {title: "Translate From:", id: "fsfb-tran1", set: "auto"}, // 3
  118. {title: "Translate To:", id: "fsfb-tran2", set: "auto"} // 4
  119. ],
  120. export_import: [
  121. {title: "Game Settings", id: "fsfb-game-settings", active: false},
  122. {title: "Game Controls", id: "fsfb-game-controls", active: false},
  123. {title: "Custom Background", id: "fsfb-custom-bg", active: false},
  124. {title: "Script Settings", id: "fsfb-script-settings", active: false},
  125. {title: "Script Theme", id: "fsfb-theme-settings", active: false}
  126. ]
  127. }, misc_settings = {
  128. abil: {},
  129. bots: {},
  130. statsPos: null,
  131. statsSettings: {
  132. xp: {
  133. lvlcomp: true,
  134. rem: true,
  135. projhr: true,
  136. lasthr: true,
  137. lastmin: true,
  138. lastsec: true,
  139. mean: true,
  140. median: true,
  141. sd: true,
  142. sesh: true,
  143. seshlength: true,
  144. lifetime: true
  145. },
  146. coins: {
  147. rem: true,
  148. projhr: true,
  149. lasthr: true,
  150. lastmin: true,
  151. mean: true,
  152. median: true,
  153. sd: true,
  154. sesh: true,
  155. seshlength: true
  156. }
  157. }
  158. }
  159.  
  160. const txtMappings = { "": 0, "BACKSPACE": 8, "TAB": 9, "ENTER": 13, "SHIFT": 16, "CTRL": 17, "ALT": 18, "PAUSE": 19, "CAPSLOCK": 20, "ESC": 27, "SPACE": 32, "PAGEUP": 33, "PAGEDOWN": 34, "END": 35, "HOME": 36, "LEFT": 37, "UP": 38, "RIGHT": 39, "DOWN": 40, "PRTSCN": 44, "INS": 45, "DEL": 46, "WIN": 91, "CONTEXTMENU": 93, "NUM 0": 96, "NUM 1": 97, "NUM 2": 98, "NUM 3": 99, "NUM 4": 100, "NUM 5": 101, "NUM 6": 102, "NUM 7": 103, "NUM 8": 104, "NUM 9": 105, "NUM *": 106, "NUM +": 107, "NUM -": 109, "NUM .": 110, "NUM /": 111, "F1": 112, "F2": 113, "F3": 114, "F4": 115, "F5": 116, "F6": 117, "F7": 118, "F8": 119, "F9": 120, "F10": 121, "F11": 122, "F12": 123, "F13": 124, "F14": 125, "F15": 126, "F16": 127, "F17": 128, "F18": 129, "F19": 130, "F20": 131, "F21": 132, "F22": 133, "F23": 134, "F24": 135, "NUMLOCK": 144, "SCROLLLOCK": 145, ";": 186, "=": 187, ",": 188, "-": 189, ".": 190, "/": 191, "`": 192, "[": 219, "\\": 220, "]": 221, "'": 222 }
  161. 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: "'"};
  162.  
  163. const set = (name, obj) => typeof GM_setValue != "function" ? localStorage.setItem(name, JSON.stringify(obj)) : GM_setValue(name, obj),
  164. get = (name, default_obj) => typeof GM_getValue != "function" ? localStorage.getItem(name) != null ? JSON.parse(localStorage.getItem(name)) : set(name, default_obj) : GM_getValue(name, default_obj);
  165.  
  166. if(typeof unsafeWindow === 'undefined') unsafeWindow = window;
  167.  
  168. const saveSettings = () => set("fsfb-scripts", settings);
  169. const getSettings = () => {
  170. let settingsPrev = get("fsfb-scripts", settings);
  171. for(let i in settingsPrev) {
  172. for(let j in settingsPrev[i]) {
  173. for(let x in settings){
  174. for(let y in settings[x]){
  175. if(settingsPrev[i][j].id == settings[x][y].id) settings[x][y] = settingsPrev[i][j];
  176. }
  177. }
  178. }
  179. }
  180. }
  181. const getMiscSettings = () => {
  182. let prevSettings = get("fsfb-misc", misc_settings);
  183. misc_settings = {...misc_settings, ... prevSettings};
  184. }
  185. getSettings();
  186. getMiscSettings();
  187. const getKey = id => {
  188. const $ = unsafeWindow.jQuery;
  189. const name = $("#" + id).text();
  190. return txtMappings[name] == null ? name.charCodeAt(0) : txtMappings[name];
  191. }
  192.  
  193. const getName = key => keyCodeMappings[key] == null ? String.fromCharCode(key) : keyCodeMappings[key];
  194.  
  195. const levelSum = lvl => lvl * (lvl - 1) / 2,
  196. range = arr => Math.max(...arr) - Math.min(...arr),
  197. sigma = arr => arr.reduce((a, b) => a + b),
  198. mean = arr => sigma(arr) / arr.length,
  199. variance = arr => arr.reduce((a, b) => a + (b - mean(arr)) ** 2, 0) / arr.length,
  200. standardDeviation = arr => Math.sqrt(variance(arr)),
  201. ascending = arr => arr.sort((a, b) => a - b),
  202. getIQR = arr => quartile(arr, .75) - quartile(arr, .25),
  203. round = (num, places) => Math.round(num * +("1e" + (places ?? 0))) / +("1e" + (places ?? 0));
  204. const median = arr => {
  205. const mid = ~~(arr.length / 2),
  206. asc = ascending(arr);
  207. return arr.length % 2 !== 0 ? asc[mid] : (asc[mid - 1] + asc[mid]) / 2;
  208. }
  209. const getProperty = (arr, property) => {
  210. let newArr = [];
  211. for (let i of arr) newArr.push(i[property]);
  212. return newArr;
  213. }
  214. const quartile = (arr, q) => {
  215. const sorted = ascending(arr),
  216. pos = (sorted.length - 1) * q,
  217. base = ~~pos,
  218. rest = pos - base;
  219. return sorted[base + 1] !== null ? sorted[base] + rest * (sorted[base + 1] - sorted[base]) : sorted[base]
  220. }
  221. const checkOutliers = (arr, returnOutliers) => {
  222. let nonOutliers = [], outliers = [];
  223. const IQR = getIQR(arr),
  224. Q1 = quartile(arr, .25),
  225. Q3 = quartile(arr, .75);
  226. for (let i of arr) i < Q1 - 1.5 * IQR || i > Q3 + 1.5 * IQR ? outliers.push(i) : nonOutliers.push(i);
  227. return returnOutliers ? outliers : nonOutliers;
  228. }
  229. const msToTime = ms => {
  230. let sec = ~~((ms / 1e3) % 60),
  231. min = ~~((ms / 6e4) % 60),
  232. hr = ~~(ms / 36e5);
  233. return (hr < 10 ? "0" + hr : hr) + ":" + (min < 10 ? "0" + min : min) + ":" + (sec < 10 ? "0" + sec : sec);
  234. }
  235. const changeTitle = title => {
  236. if(document.title != title) document.title = title;
  237. };
  238.  
  239. if(!settings.checkboxes[4].active && document.title == 'Agma.io - A free multiplayer MMO game') changeTitle('Agma.io');
  240.  
  241. if(unlockFreeSkins){
  242. const ytSkins = ["", 56, 1657, 2281, 2282, 2297, 2331, 2529, 2626, 2683, 2816, 2832];
  243. for (let i of ytSkins) localStorage.setItem('ytSkin' + i, '1');
  244. localStorage.setItem('fbSkin', '1');
  245. }
  246.  
  247. const sanitize = str => {
  248. const map = {
  249. '&': '&amp;',
  250. '<': '&lt;',
  251. '>': '&gt;',
  252. '"': '&quot;',
  253. "'": '&#x27;',
  254. "/": '&#x2F;',
  255. };
  256. return str.replace(/[&<>"'/]/ig, match => map[match]);
  257. }
  258.  
  259. ['paste', 'copy', 'cut'].forEach(a => {
  260. unsafeWindow.addEventListener(a, e => {
  261. const $ = unsafeWindow.jQuery;
  262. if($ != null && !$('#fsfb-copycutpaste').is(':checked')) return;
  263. e.stopImmediatePropagation();
  264. }, true)
  265. });
  266.  
  267.  
  268.  
  269. const afterLoaded = () => {
  270. unsafeWindow.fsfbStartedLoading = true;
  271. const $ = unsafeWindow.jQuery,
  272. swal = unsafeWindow.swal;
  273. // attempt to prevent the script from being active on subpages of agma.io
  274. if($ == null || $('#friendResizer').length < 1 || $('#megaholder').length < 1 || $('#preroll').length < 1) return;
  275. if(bypassConfirmChatRules) localStorage.setItem('crc', 'true');
  276.  
  277. $('.setting-tablink').css({'width' : '30%'});
  278. $('#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: 1.25em; color: lightgray;"></div></button>`);
  279. $('#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 padbot10"></section><section id="fsfb-sect-translate" style="padding-top: 20px;" 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-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>`);
  280. $('.container').eq(0).css("max-width", "1250px");
  281. $('#fsfb-sect-checkbox').append(`<p class="hotkey-paragraph">Script Features</p>`);
  282.  
  283. // add checkbox HTML
  284. for(let i of settings.checkboxes){
  285. $('#fsfb-sect-checkbox').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  286. $( "#" + i.id).change(function() {
  287. changeSettings(this.id, $(this).is(':checked'));
  288. });
  289. }
  290.  
  291. // add import/export HTML
  292. $('#fsfb-sect-imexport').append(`<p class="hotkey-paragraph">Import/Export</p>`);
  293. for(let i of settings.export_import){
  294. $('#fsfb-sect-imexport').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  295. $( "#" + i.id).change(function() {
  296. changeSettings(this.id, $(this).is(':checked'));
  297. });
  298. }
  299. $('#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>`);
  300.  
  301. $('#fsfb-sect-theme').append(`<p class="hotkey-paragraph">Game Theme</p`);
  302. for(let i of settings.theme){
  303. $('#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>`);
  304. $( "#" + i.id).change(function() {
  305. changeSettings(this.id, $(this).is(':checked'));
  306. });
  307. $( "#" + i.id1).change(function() {
  308. changeSettings(this.id, this.value);
  309. $(this).parent().css('background-color', this.value); // bcs the regular [input="color"] looks rly shit, change color of overlayed div instead
  310. });
  311. }
  312. for(let i of settings.theme_boxes){
  313. $('#fsfb-sect-theme').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  314. $( "#" + i.id).change(function(e) {
  315. changeSettings(this.id, $(this).is(':checked'), e);
  316. });
  317. }
  318.  
  319. $('#fsfb-sect-translate').append(`<p class="hotkey-paragraph">Chat Translate</p`); // ATT
  320.  
  321. for(let i of settings.chat_translate){
  322. if(!('set' in i)){ //checkboxes
  323. $('#fsfb-sect-translate').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  324. $( "#" + i.id).change(function(e) {
  325. changeSettings(this.id, $(this).is(':checked'), e);
  326. });
  327. } else {
  328. $('#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>`);
  329. }
  330. };
  331.  
  332. $('.fsfb-changelang').on("change", function(){
  333. for(let i = 0; i < settings.chat_translate.length; i++){
  334. if(settings.chat_translate[i].id == this.id) settings.chat_translate[i].set = this.value;
  335. }
  336. saveSettings();
  337. });
  338.  
  339. $('#fsfb-sect-hotkeys').append(`<p class="hotkey-paragraph">Script Hotkeys</p>`);
  340.  
  341. const checkHotkeyClicked = (e, thing) => {
  342. if (e.target.id == thing.id && !thing.active){
  343. $('#' + thing.id).addClass('selected');
  344. thing.active = true;
  345. keysChanging = true; // prevent features from triggering when setting hotkey
  346. } else {
  347. thing.active = false;
  348. $('#' + thing.id).removeClass('selected');
  349. }
  350. }
  351. const checkNewHotkey = (e, thing) => {
  352. if (!thing.active) return;
  353. thing.key = e.keyCode;
  354. $('#' + thing.id).text(getName(e.keyCode));
  355. $('#' + thing.id).removeClass('selected');
  356. thing.active = false;
  357. saveSettings();
  358. e.preventDefault();
  359. }
  360. const checkFsHotkey = (e, thing) => {
  361. if (!thing.active) return;
  362. thing.key = e.keyCode;
  363. thing.keyName = e.key;
  364. $('#' + thing.id).text(getName(e.keyCode));
  365. $('#' + thing.id).removeClass('selected');
  366. thing.active = false;
  367. saveSettings();
  368. e.preventDefault();
  369. }
  370.  
  371. const checkPowerupClicked = e => {
  372. if(!quickBuying || !e?.originalEvent?.isTrusted || $(e.target).attr('class') == 'purchase-btn confirmation' || $(e.target).attr('class') == 'megaphone-btn') return;
  373. let pwID;
  374. switch (e.target.id) {
  375. case 'invWall':
  376. pwID = 33;
  377. break;
  378. case 'invAntiFreeze':
  379. pwID = 35;
  380. break;
  381. case 'invAntiRecombine':
  382. pwID = 34;
  383. break;
  384. case 'invShield':
  385. pwID = 38;
  386. break;
  387. case 'invFrozenVirus':
  388. pwID = 36;
  389. break;
  390. case 'invRecombine':
  391. pwID = 1;
  392. break;
  393. case 'invSpeed':
  394. pwID = 2;
  395. break;
  396. case 'invGrowth':
  397. pwID = 6;
  398. break;
  399. case 'invSpawnVirus':
  400. pwID = 7;
  401. break;
  402. case 'invSpawnMothercell':
  403. pwID = 8;
  404. break;
  405. case 'invSpawnPortal':
  406. pwID = 9;
  407. break;
  408. case 'invSpawnGoldOre':
  409. pwID = 10;
  410. break;
  411. case 'invFreeze':
  412. pwID = 5;
  413. break;
  414. case 'inv360Shot':
  415. pwID = 30;
  416. break;
  417. case 'fsfb-minionNuker':
  418. pwID = 39;
  419. break;
  420. case 'fsfb-quickMega':
  421. pwID = 14;
  422. break;
  423. case 'fsfb-quickbuy-img':
  424. quickBuying = true;
  425. return;
  426. default:
  427. quickBuying = false;
  428. $('.inventory-box').removeClass('fsfb-shown').find('p').show();
  429. $('#fsfb-quickbuy').removeClass('activatedInv')
  430. curserMsg('Quick buy deactivated.', 'red');
  431. return;
  432. }
  433. $('.confirm').attr('disabled', 'true'); // disable so user doesn't buy early - swal is slow to load text
  434. const waitUntil1 = (condition) => new Promise(resolve => {
  435. let interval = setInterval(() => {
  436. $('.confirm')[0].click();
  437. condition() && (clearInterval(interval), resolve());
  438. }, 25);
  439. setTimeout(() => { (clearInterval(interval), resolve()) }, 1e4);
  440. });
  441. setTimeout(() => {
  442. $('.confirm')[0].addEventListener('click', async e => {
  443. if(!e.isTrusted) return;
  444. $('.sweet-alert, .sweet-overlay').addClass('fsfb-hidden');
  445. setTimeout(async() => {
  446. await waitUntil1(() => !$('.sweet-alert').hasClass('visible'));
  447. if($('.sweet-alert').hasClass('visible')) return;
  448. await sleep(100);
  449. $('.sweet-alert, .sweet-overlay').removeClass('fsfb-hidden');
  450. }, 1e3);
  451. })
  452. }, 500);
  453. setTimeout(() => $('.confirm').removeAttr('disabled'), 600);
  454.  
  455. if(pwID == 14) $('.megaphone-btn')[0].click();
  456. else $('.purchase-btn.confirmation[item="' + pwID + '"]')[0].click();
  457.  
  458. $('.inventory-box').removeClass('fsfb-shown').find('p').show();
  459. $('#fsfb-quickbuy').removeClass('activatedInv')
  460. quickBuying = false;
  461. }
  462.  
  463. const slowfeedhotkey = settings.slowFeed[0];
  464.  
  465. for(let i of settings.hotkeys){
  466. $('#fsfb-sect-hotkeys').append(`<br><p>${i.title}</p><div id="${i.id}" class="fsfb-hotkey"></div>`);
  467. $('#' + i.id).on('contextmenu', e => {
  468. $('#' + i.id).text('');
  469. i.key = 0;
  470. saveSettings();
  471. e.preventDefault();
  472. });
  473. }
  474.  
  475.  
  476. const fields = $('input, textarea');
  477. const typing = () => fields.is(':focus');
  478. const sleep = ms => new Promise(r => setTimeout(r, ms));
  479. const press = key => {
  480. unsafeWindow.onkeydown({ keyCode: key });
  481. unsafeWindow.onkeyup({ keyCode: key });
  482. }
  483. let fsCheck;
  484. const fastSplit = async(a) => {
  485. if(!fsCheck) return void(fsCheck = !0);
  486. if([39, 37, 2, 4, 6].indexOf(currentServerId) != -1 || !a || extraOneFastSplitDelay) await sleep(settings.fastsplit_hotkeys[2].val);
  487. // else if(Date.now() - lastTimeFrzPressed < settings.fastsplit_hotkeys[3].val) await sleep(settings.fastsplit_hotkeys[3].val); // try to detect if frz was recently pressed - can't send the same input too quickly (need to add some delay)
  488. press(getKey("keyFreezeSelf"));
  489. await sleep(settings.fastsplit_hotkeys[3].val);
  490. press(getKey("keyFreezeSelf"));
  491. }
  492. setTimeout(fastSplit, 120);
  493.  
  494. let cursorLockActivated = false, _onblur;
  495. const waitForBlur = () => {
  496. if(unsafeWindow.onblur != null){
  497. const oldBlur = unsafeWindow.onblur
  498. unsafeWindow.onblur = function(){
  499. spamRec = false;
  500. spamSpeed = false;
  501. splittingbots = false;
  502. if(!$('#fsfb-linetoggle').is(':checked')){
  503. $("#linesplit-markers div").hide();
  504. linesplitting = false;
  505. }
  506. return oldBlur.apply(this, arguments);
  507. }
  508. _onblur = unsafeWindow.onblur
  509. } else {
  510. setTimeout(waitForBlur, 400);
  511. }
  512. };
  513. waitForBlur();
  514.  
  515. // hook for typing in chat w/ cursor lock
  516. let lastTimeFrzPressed = Date.now();
  517. const waitForKeyup = () => {
  518. if(unsafeWindow.onkeyup != null){
  519. const _keydown = unsafeWindow.onkeyup;
  520. unsafeWindow.onkeyup = function(){
  521. if(arguments[0]?.keyCode == getKey('keyFreezeSelf')) lastTimeFrzPressed = Date.now();
  522. if(arguments[0]?.keyCode != getKey('keyFixedMouse') || arguments[0]?.target?.id != 'chtbox' || !cursorLockActivated) return _keydown.apply(this, arguments);
  523. }
  524. } else {
  525. setTimeout(waitForKeyup, 400);
  526. }
  527. };
  528. waitForKeyup();
  529.  
  530. const toggleCursorLock = () => {
  531. cursorLockActivated = !cursorLockActivated;
  532. if(cursorLockActivated){
  533. unsafeWindow.onblur = function(){
  534. unsafeWindow.onkeyup({keyCode: getKey('keyMacroSplit')});
  535. unsafeWindow.onkeyup({keyCode: getKey('keyMacroFeed')});
  536. unsafeWindow.onkeyup({keyCode: getKey('keyMultiFeed')}); // ATT
  537. spamRec = false;
  538. spamSpeed = false;
  539. splittingbots = false;
  540. if(!$('#fsfb-linetoggle').is(':checked')){
  541. $("#linesplit-markers div").hide();
  542. linesplitting = false;
  543. }
  544. }
  545. } else {
  546. unsafeWindow.onblur = _onblur;
  547. }
  548. cursorLockActivated ? unsafeWindow.onkeydown({keyCode: 67}) : unsafeWindow.onkeyup({keyCode: 67});
  549. }
  550. unsafeWindow.globalCursorLock = toggleCursorLock;
  551.  
  552. Object.defineProperty(KeyboardEvent.prototype, 'keyCode', {
  553. get: function() {
  554. switch (this.key.toLowerCase()) {
  555. case settings.fastsplit_hotkeys[0].keyName.toLowerCase(): return getKey("keySplit");
  556. case settings.fastsplit_hotkeys[1].keyName.toLowerCase(): return getKey("keyDoubleSplit");
  557. default: return this.which;
  558. }
  559. }
  560. });
  561.  
  562. let slowfeeding = !1, linesplitting = !1, hiddenUI = !1, splittingbots = !1, spamRec = !1, spamSpeed = !1;
  563. const pressed = e => {
  564. const key = e.which ? e.which : e.keyCode;
  565. if($('textarea').is(':focus')) e.stopImmediatePropagation();
  566. if(typing() || keysChanging || e.key == undefined || e.keyCode == undefined) return;
  567. if(key == 27 && quickBuying){ // esc pressed
  568. quickBuying = false;
  569. $('#fsfb-quickbuy').removeClass('activatedInv');
  570. curserMsg('Quick buy deactivated.', 'red');
  571. $('.inventory-box').removeClass('fsfb-shown').find('p').show();
  572. e.preventDefault();
  573. e.stopImmediatePropagation();
  574. }
  575. if(key == settings.hotkeys[0].key){ // 7 feed
  576. let i = 1;
  577. let interval = setInterval(() => {
  578. press(getKey("keyMacroFeed"));
  579. if(++i > 7) clearInterval(interval);
  580. }, 85);
  581. e.preventDefault();
  582. }
  583. if(key == settings.hotkeys[1].key && !$('#fsfb-linetoggle').is(':checked')){ // linesplit lock
  584. linesplitting = true;
  585. linesplit();
  586. $("#linesplit-markers div").show();
  587. e.preventDefault();
  588. }
  589. if(key == settings.hotkeys[1].key && $('#fsfb-linetoggle').is(':checked')){ // linesplit lock
  590. linesplitting = !linesplitting;
  591. if(linesplitting){
  592. $("#linesplit-markers div").show();
  593. linesplit();
  594. } else {
  595. $("#linesplit-markers div").hide();
  596. $('#canvas').trigger($.Event('mousemove', {clientX: mosX, clientY: mosY})); // return mouse to where the cursor is
  597. }
  598. e.preventDefault();
  599. }
  600. if(key == settings.hotkeys[2].key){ // macro split bots
  601. splittingbots = true;
  602. const splittingBots = () => {
  603. if(!splittingbots) return;
  604. press(getKey("keySplitBots"));
  605. setTimeout(splittingBots, 50);
  606. }
  607. splittingBots();
  608. e.preventDefault();
  609. }
  610. if(key == settings.hotkeys[3].key){ // hide ui
  611. hiddenUI = !hiddenUI;
  612. hiddenUI ? _replaceCSS('hideUI-css', '.hideUI{ display: none !important;}') : _replaceCSS('hideUI-css', ''); // replacing CSS bcs class is less likely to be overriden
  613. e.preventDefault();
  614. }
  615. if(key == settings.hotkeys[4].key){ // toggle cursor lock
  616. toggleCursorLock();
  617. e.preventDefault();
  618. }
  619. if(key == getKey("keyFixedMouse")){ // real cursor lock key is pressed
  620. cursorLockActivated = false;
  621. unsafeWindow.onblur = _onblur;
  622. }
  623. if(e.key.toLowerCase() == settings.fastsplit_hotkeys[0]?.keyName?.toLowerCase() && e.keyCode != 0){ // fast onesplit
  624. fastSplit(!0);
  625. }
  626. if(e.key.toLowerCase() == settings.fastsplit_hotkeys[1]?.keyName?.toLowerCase() && e.keyCode != 0){ // fast doublesplit
  627. fastSplit(!1);
  628. }
  629. if(key == settings.slowFeed[0].key){ // toggle feed
  630. slowfeeding = !slowfeeding;
  631. const feeding = () => {
  632. if(!slowfeeding) return;
  633. press(getKey("keyMacroFeed"));
  634. setTimeout(feeding, settings.slowFeed[1].val);
  635. }
  636. feeding();
  637. e.preventDefault();
  638. }
  639. if(key == settings.quickSettings[0].key){ // quick settings 1
  640. let el = $('#' + settings.quickSettings[0].set);
  641. if($('#fsfb-settings-main')[0].contains(el[0])) el.prop('checked', !el.prop('checked')).trigger('change');
  642. else el.unbind().click();
  643. e.preventDefault();
  644. }
  645. if(key == settings.quickSettings[1].key){ // quick settings 2
  646. let el = $('#' + settings.quickSettings[1].set);
  647. if($('#fsfb-settings-main')[0].contains(el[0])) el.prop('checked', !el.prop('checked')).trigger('change');
  648. else el.unbind().click();
  649. e.preventDefault();
  650. }
  651. if(key == settings.quickSettings[2].key){ // quick settings 3
  652. let el = $('#' + settings.quickSettings[2].set);
  653. if($('#fsfb-settings-main')[0].contains(el[0])) el.prop('checked', !el.prop('checked')).trigger('change');
  654. else el.unbind().click();
  655. e.preventDefault();
  656. }
  657. if(key == settings.quickSettings[3].key){ // quick settings 4
  658. let el = $('#' + settings.quickSettings[3].set);
  659. if($('#fsfb-settings-main')[0].contains(el[0])) el.prop('checked', !el.prop('checked')).trigger('change');
  660. else el.unbind().click();
  661. e.preventDefault();
  662. }
  663. if(key == settings.quickSettings[4].key){ // quick settings 5
  664. let el = $('#' + settings.quickSettings[3].set);
  665. if($('#fsfb-settings-main')[0].contains(el[0])) el.prop('checked', !el.prop('checked')).trigger('change');
  666. else el.unbind().click();
  667. e.preventDefault();
  668. }
  669. if($('#fsfb-recospeed').is(':checked') && key == getKey("keyRecombine")){
  670. spamRec = true;
  671. const spammingRec = () => {
  672. if(!spamRec) return;
  673. press(getKey("keyRecombine"));
  674. setTimeout(spammingRec, 10);
  675. }
  676. spammingRec();
  677. e.preventDefault();
  678. }
  679. if($('#fsfb-recospeed').is(':checked') && key == getKey("keySpeed")){
  680. spamSpeed = true;
  681. const spammingSpeed = () => {
  682. if(!spamSpeed) return;
  683. press(getKey("keySpeed"));
  684. setTimeout(spammingSpeed, 10);
  685. }
  686. spammingSpeed();
  687. e.preventDefault();
  688. }
  689. }
  690.  
  691. const released = key => {
  692. if(typing() || keysChanging) return;
  693. if(key == settings.hotkeys[2].key) splittingbots = false; // macro split bots
  694. if(key == settings.hotkeys[1].key && !$('#fsfb-linetoggle').is(':checked')){ // linesplit lock
  695. linesplitting = false;
  696. $('#canvas').trigger($.Event('mousemove', {clientX: mosX, clientY: mosY})); // return mouse to where the cursor is
  697. $("#linesplit-markers div").hide();
  698. }
  699. if(key == getKey("keyRecombine")) spamRec = false;
  700. if(key == getKey("keySpeed")) spamSpeed = false;
  701. }
  702. let pwSpawnCheck = false, translateChanged = false;
  703. const changeSettings = (ID, a, e) => { // a = active, e = event (optional)
  704. for(let i of settings.uiScaling) if(i.id == ID) i.level = a;
  705. for(let i of settings.checkboxes) if(i.id == ID) i.active = a;
  706. for(let i of settings.export_import) if(i.id == ID) i.active = a;
  707. for(let i of settings.theme_boxes) if(i.id == ID) i.active = a;
  708. for(let i of settings.chat_translate){
  709. if(i.id == ID){
  710. if(ID == "fsfb-tranchat" && typeof GM_xmlhttpRequest != 'function' && e?.originalEvent?.isTrusted){
  711. 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);
  712. $('#' + ID).prop('checked', false);
  713. return;
  714. }
  715. translateChanged = true, i.active = a;
  716. }
  717. }
  718. for(let i of settings.theme){
  719. if(i.id == ID) i.active = a;
  720. if(i.id1 == ID) i.color = a;
  721. }
  722. customCells = $("#fsfb-sect-theme>label>input, #fsfb-powsoverlay, #fsfb-anticloak").is(":checked");
  723. if(ID == "fsfb-hideshouts") a ? $('#megaholder').addClass('hideMegaphone') : $('#megaholder').removeClass('hideMegaphone');
  724. if(ID == "fsfb-qBuy") a ? $('#fsfb-quickbuy').css('display', 'flex') : $('#fsfb-quickbuy').hide();
  725. if(ID == "fsfb-bublecell" && !$("#cBubbleCells").is(":checked") && a && e?.originalEvent?.isTrusted) $('#cBubbleCells').unbind().click();
  726. if(ID == "fsfb-bublecell" && $("#cBubbleCells").is(":checked") && !a && e?.originalEvent?.isTrusted) $('#cBubbleCells').unbind().click();
  727. if(ID == "fsfb-showmass" && !$("#cMass").is(":checked") && a && e?.originalEvent?.isTrusted) $('#cMass').unbind().click();
  728. if(ID == "fsfb-showmass" && $("#cMass").is(":checked") && !a && e?.originalEvent?.isTrusted) $('#cMass').unbind().click();
  729. if((ID == "fsfb-myskins" || ID == "fsfb-partyskins") && !$("#cSkins").is(":checked") && a) $('#cSkins').unbind().click();
  730. if((ID == "fsfb-mynick" || ID == "fsfb-partynicks") && !$("#cNames").is(":checked") && a) $('#cNames').unbind().click();
  731. if(ID == "fsfb-myskins" && a) $('#fsfb-partyskins').prop('checked', false).trigger('change');
  732. if(ID == "fsfb-partyskins" && a) $('#fsfb-myskins').prop('checked', false).trigger('change');
  733. if(ID == "fsfb-mynick" && a) $('#fsfb-partynicks').prop('checked', false).trigger('change');
  734. if(ID == "fsfb-partynicks" && a) $('#fsfb-mynick').prop('checked', false).trigger('change');
  735.  
  736. // if(ID == "fsfb-revcell" && a) $('#fsfb-portalstop').prop('checked', false).trigger('change');
  737. // if(ID == "fsfb-portalstop" && a) $('#fsfb-revcell').prop('checked', false).trigger('change');
  738.  
  739. if(ID == "fsfb-powsoverlay"){
  740. if(!a && pwSpawnCheck) curserMsg('To hide powerup spawns, you need to respawn or switch servers.', 'red');
  741. pwSpawnCheck = true;
  742. }
  743. if(ID == "fsfb-powsoverlay") svSwitch = true;
  744. let zoomLvl = "100%";
  745. let map = {
  746. 1: 50,
  747. 2: 70,
  748. 3: 80,
  749. 4: 90,
  750. 5: 100,
  751. 6: 110,
  752. 7: 125,
  753. 8: 150,
  754. 9: 200
  755. }
  756. if(ID == "fsfb-invSize") $('#inventory').css('zoom', (map[+a] ?? 100)+ '%');
  757. if(ID == "fsfb-statsSize") $('#stats-container').css('zoom', (map[+a] ?? 100) + '%');
  758. saveSettings();
  759. }
  760. // add slowfeed HTML
  761. $('#fsfb-sect-slowfeed').append(`<p class="hotkey-paragraph">Slow-Feed</p>`);
  762. $('#fsfb-sect-slowfeed').append(`<br><p>${slowfeedhotkey.title}</p><div id="${slowfeedhotkey.id}" class="fsfb-hotkey"></div>`);
  763. $('#fsfb-sect-slowfeed').append(`<br><p>${settings.slowFeed[1].title}</p><input id="${settings.slowFeed[1].id}" class="fsfb-hotkey" onkeypress="return onlyNumberKey(event)" maxlength="3"></input>`);
  764.  
  765. $('#' + slowfeedhotkey.id).on('contextmenu', e => {
  766. $('#' + slowfeedhotkey.id).text('');
  767. slowfeedhotkey.key = 0;
  768. saveSettings();
  769. e.preventDefault();
  770. });
  771.  
  772. document.getElementById(settings.slowFeed[1].id).addEventListener("keypress", function(e){
  773. setTimeout(() => { // goes too fast or smth
  774. settings.slowFeed[1].val = +$('#' + settings.slowFeed[1].id).val();
  775. if($('#' + settings.slowFeed[1].id).val() == "") settings.slowFeed[1].val = 0;
  776. saveSettings();
  777. }, 5);
  778. });
  779.  
  780.  
  781. // add fastsplit HTML
  782. $('#fsfb-sect-fastsplit').append(`<p class="hotkey-paragraph">Fast-Split</p>`); // bookmark
  783. // for(let i of settings.fastsplit_hotkeys){
  784. for(let j = 0; j < settings.fastsplit_hotkeys.length; j++){
  785. let i = settings.fastsplit_hotkeys[j];
  786. $('#fsfb-sect-fastsplit').append(i.val == null ? `<br><p>${i.title}</p><div id="${i.id}" class="fsfb-hotkey"></div>` : `<br><p>${i.title}</p><input id="${i.id}" class="fsfb-hotkey" onkeypress="return onlyNumberKey(event)" maxlength="3"></input>`)
  787. if(i.val == null){
  788. $('#' + i.id).on('contextmenu', e => {
  789. $('#' + i.id).text('');
  790. i.key = 0;
  791. i.keyName = '';
  792. saveSettings();
  793. e.preventDefault();
  794. });
  795. } else {
  796. document.getElementById(i.id).addEventListener("keypress", function(e){
  797. setTimeout(() => { // goes too fast or smth
  798. settings.fastsplit_hotkeys[j].val = +$('#' + i.id).val();
  799. if($('#' + i.id).val() == "") settings.fastsplit_hotkeys[j].val = 0;
  800. saveSettings();
  801. }, 5);
  802. });
  803. }
  804. }
  805.  
  806. // add quick settings HTML
  807. $('#fsfb-sect-quickSettings').append(`<p class="hotkey-paragraph">Quick Settings</p>`);
  808.  
  809. for(let i of settings.quickSettings){
  810. $('#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="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="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></select><div id="${i.id}" class="fsfb-hotkey"></div>`);
  811. $('#' + i.id).on('contextmenu', e =>{
  812. $('#' + i.id).text('');
  813. i.key = 0;
  814. saveSettings();
  815. e.preventDefault();
  816. });
  817. };
  818.  
  819. $('.fsfb-quickchange').on("change", function(){
  820. settings.quickSettings[+this.id.replace('fsfb-quick-select', '') - 1].set = this.value;
  821. saveSettings();
  822. });
  823.  
  824. // add UI scaling
  825. $('#fsfb-sect-uiScale').append(`<p class="hotkey-paragraph">UI Scaling</p>`);
  826. for(let i of settings.uiScaling){
  827. $('#fsfb-sect-uiScale').append(`<div class="fsfb-slider"><p>${i.title}</p><input id="${i.id}" class="fsfb-slider" type="range" min="1" max="9" value="5"></input></div>`);
  828. $( "#" + i.id).change(function() {
  829. changeSettings(this.id, $(this).val());
  830. });
  831. };
  832.  
  833. // add event listeners
  834. let mosX, mosY,
  835. keys = {},
  836. keysChanging = false;
  837.  
  838. $(document).on('click', e => {
  839. for(let i = 0; i < settings.hotkeys.length; i++) checkHotkeyClicked(e, settings.hotkeys[i]);
  840. for(let i = 0; i < settings.fastsplit_hotkeys.length; i++) settings.fastsplit_hotkeys[i].val == null && checkHotkeyClicked(e, settings.fastsplit_hotkeys[i]);
  841. for(let i = 0; i < settings.quickSettings.length; i++) checkHotkeyClicked(e, settings.quickSettings[i]);
  842. checkHotkeyClicked(e, settings.slowFeed[0]);
  843. checkPowerupClicked(e);
  844. })
  845. .on('mousemove', e => {
  846. if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
  847. ({clientX: mosX, clientY: mosY} = e);
  848. if (linesplitting) linesplit();
  849. })
  850.  
  851. .on("keydown", e => {
  852. // if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
  853. for(let i = 0; i < settings.hotkeys.length; i++) checkNewHotkey(e, settings.hotkeys[i]);
  854. for(let i = 0; i < settings.fastsplit_hotkeys.length; i++) checkFsHotkey(e, settings.fastsplit_hotkeys[i]);
  855. for(let i = 0; i < settings.quickSettings.length; i++) checkNewHotkey(e, settings.quickSettings[i]);
  856. checkNewHotkey(e, settings.slowFeed[0]);
  857. if (!(e.keyCode in keys)){
  858. keys[e.keyCode] = !0;
  859. pressed(e);
  860. }
  861. keysChanging = false;
  862. })
  863. .on("keyup", e => {
  864. if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
  865. delete keys[e.keyCode];
  866. released(e.keyCode);
  867. if(e.keyCode == getKey("keyFixedMouse") && cursorLockActivated){
  868. e.stopImmediatePropagation();
  869. e.preventDefault();
  870. }
  871. });
  872.  
  873.  
  874. const hashCode = (str, shift) => {
  875. let char, hash = 0;
  876. if (str.length == 0) return hash;
  877. for (let i = 0; i < str.length; i++) hash = (hash << shift) - hash + (char = str.charCodeAt(i)), hash |= 0;
  878. return hash;
  879. };
  880.  
  881. const RNG = (min, max) => Math.random() * (max - min) + min;
  882.  
  883. // chat cmds
  884. if(extraChatCommands){
  885. const $chtbox = $('#chtbox');
  886. $chtbox.on('keydown', e => {
  887. if(e.keyCode != 13) return;
  888. let newMsg = '', params, command, firstMatch = $chtbox.val().match(new RegExp(`(?<=((^|(^\/party +)|(^\/pm +\\S+ ))${chatPrefix}))\\S+(\\s+\\S+)*(?=($|\\s+))`, 'gmi'));
  889. if(firstMatch != null) {
  890. [command, ...params] = firstMatch[0].replace(/\s+/g, ' ').split(' ');
  891. } else return;
  892. const originalMsg = $chtbox.val().replace(new RegExp(`(?<=((^|(^\/party +)|(^\/pm +\\S+ ))))${chatPrefix}${firstMatch[0]}(?=($|\\s+))`, 'gmi'), '');
  893. coinsInfo();
  894. xpInfo();
  895. switch(command.toLowerCase()){
  896. case 'help':
  897. $('#fsfb-extra-info')[0].click();
  898. setTimeout(() => $('.fsfb-modal-body').scrollTop(1850), 200);
  899. newMsg = '';
  900. break;
  901. case 'bots': case 'bot': case 'min': case 'mins': case 'minion': case 'minions': {
  902. let minInfo = misc_settings.bots?.[currentUser], minsAmt, minsTimeRem;
  903. $('#infoContent').children().each(function(){
  904. if($(this).text().includes('Minion Time:')) minsTimeRem = $(this).find('span').text();
  905. if($(this).text().includes('Minions:')) minsAmt = $(this).find('span').text()
  906. })
  907. 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) */
  908. newMsg = `${originalMsg}Minion Pack Unstarted: ${minsChatAmt[currentUser].amt}`;
  909. } else if(minsChatAmt[currentUser] != null && minsChatAmt[currentUser]?.started && minsChatAmt[currentUser]?.amt && minInfo == null ){
  910. newMsg = `${originalMsg}Minion Pack Activated: ${minsChatAmt[currentUser].amt}`;
  911. } else if(minsChatAmt[currentUser] != null && minsChatAmt[currentUser].amt && minInfo != null && (minInfo.active || minsChatAmt[currentUser].started) && minInfo.rem - (Date.now() - minInfo.currMs) > 0){
  912. newMsg = `${originalMsg}Minion Pack Activated: ${minsChatAmt[currentUser].amt}, with ${msToTime(minInfo.rem - (Date.now() - minInfo.currMs))} remaining`;
  913. } else if(minsAmt != null && minsTimeRem != null){
  914. let timeArr = minsTimeRem.split(':'), msBotsTime = (3.6e6 * +timeArr[0]) + (6e4 * +timeArr[1]) + (1e3 * +timeArr[2]);
  915. newMsg = `${originalMsg}Minion Pack Activated: ${minsAmt}, with ${msToTime(msBotsTime)} remaining`;
  916. } else {
  917. newMsg = `${originalMsg}Minion Pack Activated: none`;
  918. }
  919. break;
  920. }
  921. case 'pws': case 'pw': case 'powers': case 'power': case 'inv': case 'inventory': {
  922. curserMsg(`It's recommended to use ${chatPrefix + command}1, ${chatPrefix + command}2, and ${chatPrefix + command}3 instead, so your chat messages aren't cut-off by the chat maxlength`, 'red', 8e3);
  923. updatePwCount();
  924. newMsg = `${originalMsg}Inv: `;
  925. for(let i in pws){ // rly shitty solution but can't think of a way better way to do it rn
  926. const amt = pws[i];
  927. newMsg += (amt + (amt == '' ? '' : ' ') + (amt == '' ? '' : (i + ', ')));
  928. }
  929. if(newMsg.length > 100){
  930. newMsg = `${originalMsg}Inv: `;
  931. for(let i in pws){
  932. const amt = pws[i];
  933. newMsg += (amt + (amt == '' ? '' : (i + ', ')));
  934. }
  935. if(newMsg.length > 100){
  936. newMsg = `${originalMsg}Inv: `;
  937. for(let i in pws){
  938. const amt = pws[i];
  939. newMsg += (amt + (amt == '' ? '' : (i + ',')));
  940. }
  941. if(newMsg.length > 100){
  942. newMsg = `${originalMsg}Inv: `;
  943. for(let i in pws){
  944. const amt = pws[i];
  945. newMsg += (amt + (amt == '' ? '' : '') + (amt == '' ? '' : (i[0] + i[1] + i[2]) + ','));
  946. }
  947. if(newMsg.length > 100){
  948. newMsg = `${originalMsg}Inv: `;
  949. for(let i in pws){
  950. const amt = pws[i];
  951. newMsg += (amt + (amt == '' ? '' : (i[0] + i[1]) + ','));
  952. }
  953. if(newMsg.length > 100){
  954. newMsg = `${originalMsg}Inv: `;
  955. for(let i in pws){
  956. const amt = pws[i];
  957. newMsg += (amt + (amt == '' ? '' : (i[0] + ',')));
  958. }
  959. }
  960. }
  961.  
  962. }
  963. }
  964. }
  965. newMsg = newMsg.replace(/,[^,]*$/g, '');
  966. if(newMsg == 'Inv: ') newMsg = 'Inv: no powers';
  967. break;
  968. }
  969. case 'totalpws': case 'totalpw': case 'totalpowers': case 'totalpower': case 'total': case 'totpw':
  970. newMsg = `${originalMsg}Total Powerups: ${$('.inventory-box>p').toArray().map(x => +x.innerText).reduce((a, b) => a + b)}`
  971. break;
  972. case 'xplevel': case 'levelxp': case 'lvlxp': case 'xpcompleted': case 'xp':
  973. newMsg = `${originalMsg}XP Completed: ${currentPercent ? String(Math.round(currentPercent * currentLevel * 1e3, 1)).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "/" + (currentLevel * 1e3).toLocaleString('en-US') : "0/0"}`
  974. break;
  975. case 'level': case 'levels': case 'lvls': case 'lvl': case 'lvlcompleted': case 'lvlscompleted':
  976. newMsg = `${originalMsg}Level: ${currentLevel}, with ${round(currentPercent * 100, 3)}% completed`
  977. break;
  978. case 'coin': case 'coins':
  979. newMsg = `${originalMsg}Coins: ${String(currentCoins).replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`
  980. break;
  981. case 'hours': case 'hrs': case 'hour': case 'hr': case 'timeplayed':
  982. newMsg = originalMsg + $('.timePlayed>span').text();
  983. break;
  984. case 'rank': case 'lvlrank':
  985. newMsg = `${originalMsg}My Rank: ${$('.ranking.text-left>span').text().match(/(?<=^Your rank: )\d+$/gm)?.[0]}`
  986. break;
  987. case 'ping': case 'delay': case 'ms':
  988. newMsg = `${originalMsg}My Ping: ${$('#ping').text()}`
  989. break;
  990. case 'fps': case 'frames':
  991. newMsg = `${originalMsg}My FPS: ${$('#fps').text()}`
  992. break;
  993. case 'topmass': case 'highscore': case 'highmass': case 'highestmass':
  994. newMsg = `${originalMsg}My Top Mass: ${$('#topMass').text()}`
  995. break;
  996. case 'cells': case 'cellcount': case 'cell':
  997. newMsg = `${originalMsg}My Cell Count: ${$('#cellsAmount').text()}`
  998. break;
  999. case 'pw1': case 'pws1': case 'power1': case 'powers1': case 'inv1': case 'inventory1':
  1000. newMsg = getPowerMessage(1, originalMsg);
  1001. break;
  1002. case 'pw2': case 'pws2': case 'power2': case 'powers2': case 'inv2': case 'inventory2':
  1003. newMsg = getPowerMessage(2, originalMsg);
  1004. break;
  1005. case 'pw3': case 'pws3': case 'power3': case 'powers3': case 'inv3': case 'inventory3':
  1006. newMsg = getPowerMessage(3, originalMsg);
  1007. break;
  1008. case 'friends': case 'friend': case 'friendsonline': case 'friendonline':
  1009. newMsg = `${originalMsg}Friends Online: ${$('#friendsLoggedInAmt').text() + $('#friendsTotalAmt').text()}`;
  1010. break;
  1011. case 'request': case 'requests': case 'friendrequest': case 'friendrequests': case 'friendreq': case 'req':
  1012. newMsg = `${originalMsg}Friend Requests: ${$('#friendsRequestsAmt').text() === '' ? 'none' : $('#friendsRequestsAmt').text()}`;
  1013. break;
  1014. case 'gold': case 'goldmem': case 'goldmember': case 'golddays': case 'gm': {
  1015. const obj = accGoldMem[currentUser];
  1016. if(obj?.has == null){
  1017. newMsg = `${originalMsg}Days of Goldmember Remaining: none`;
  1018. break;
  1019. }
  1020. newMsg = `${originalMsg}Days Of Goldmember Remaining: ${obj.has || obj.days != null ? (obj.days + (obj.days == 1 ? ' Day' : ' Days')) : 'none'}`;
  1021. break;
  1022. }
  1023. case 'alive': case 'alivetime': case 'timealive':
  1024. newMsg = `${originalMsg}Time Alive: ${unsafeWindow.playerIsAlive() ? msToTime(Date.now() - timeAlive) : 'not alive'}`;
  1025. break;
  1026. case 'mass': case 'currentmass':
  1027. newMsg = `${originalMsg}Current Mass: ${unsafeWindow.playerIsAlive() ? currentMass.toLocaleString('en-US') : 'none, not alive'}`;
  1028. break;
  1029. case 'user': case 'username': case 'usr': case 'account': case 'acc':
  1030. newMsg = `${originalMsg}My Username: ${currentUser == "Please Login First" ? "logged out" : currentUser}`
  1031. break;
  1032. case 'custom': case 'customs': case 'customskins': case 'totalcustoms':
  1033. if($('#publicSkinsPage').children().length == 0){
  1034. curserMsg(`Please load your custom skins before using this command`, 'red', 6e3);
  1035. newMsg = '';
  1036. break;
  1037. }
  1038. newMsg = `${originalMsg}My Total Custom Skins: ${$('[id^="skinCustomImg"').length}, worth ${($('[id^="skinCustomImg"').length * 1e6).toLocaleString('en-US')}`
  1039. break;
  1040. case 'skins': case 'boughtskins': case 'ownedskins': case 'totalskins': {
  1041. if($('#skinsBuy [id^="skinContainer"]').length == 0){
  1042. curserMsg(`Please load your owned skins from the agma.io skin store`, 'red', 6e3);
  1043. newMsg = '';
  1044. break;
  1045. }
  1046. let ownedSkinsArr = [], edSkins = 0, totalValue = 0;
  1047. $('#skinsBuy [id^="skinUseBtn"').each(function(){
  1048. ownedSkinsArr.push(+$(this)[0].id.match(/\d+$/gm)[0]);
  1049. })
  1050. for(let i of skinsArr){
  1051. if(ownedSkinsArr.indexOf(i.id) != -1){
  1052. i.price > 2e6 ? ++edSkins : totalValue += i.price;
  1053. }
  1054. }
  1055. newMsg = `${originalMsg}My Total Bought Skins: ${$('#skinsBuy [id^="skinUseBtn"').length}, worth ${totalValue.toLocaleString('en-US')} (${edSkins} lim. ed.)`
  1056. break;
  1057. }
  1058. case 'ownedwearables': case 'wearables': case 'wears': case 'totalwearables': case 'totalwears': {
  1059. if($('#phpWearables li').length <= 55){
  1060. curserMsg(`Please load your wearables before using this command`, 'red', 6e3);
  1061. newMsg = '';
  1062. break;
  1063. }
  1064. let wearsPrice = [0];
  1065. $('[id^="wearableUseBtn"]').each(function(){
  1066. wearsPrice.push(+$(this).parents().eq(0).siblings('span.win-price').text().replace(/,/g, ''))
  1067. });
  1068. newMsg = `${originalMsg}My Total Wearables: ${$('[id^="wearableUseBtn"]').length}, worth ${sigma(wearsPrice).toLocaleString('en-US')} coins`
  1069. break;
  1070. }
  1071. case 'cloak': case 'invis': case 'invisibility': {
  1072. let status, time;
  1073. $('#infoContent>p').each(function(){
  1074. if($(this).text().includes('Cloaked: ')) status = $(this).text().match(/(?<=Cloaked: ).+/gm);
  1075. if($(this).text().includes('Cloak Time: ')) time = $(this).text().match(/(?<=Cloak Time: ).+/gm);
  1076. })
  1077. newMsg = status == null || time == null ? `${originalMsg}Cloak inactive` : `${originalMsg}Cloak ${status}, with ${time} remaining`
  1078. break;
  1079. }
  1080. case 'addfriend': case 'add':
  1081. if(params?.length == 0){
  1082. curserMsg(`Please add a username after the command, such as: ${chatPrefix}${command} Fishyyy`, 'red', 6e3);
  1083. newMsg = '';
  1084. break;
  1085. }
  1086. unsafeWindow.friendAdd(params[0]);
  1087. break;
  1088. case 'partymembers': case 'partymember':
  1089. newMsg = $('#partyContent').children().length - 1 > 0 ? `${originalMsg}In a party, with ${$('#partyContent').children().length - 1} members` : `${originalMsg}Not in a party`;
  1090. break;
  1091. case 'players': case 'ply': case 'plyrs': case 'serverplayers': case 'totalplayers': case 'online': {
  1092. let players = 0, max = 0;
  1093. $('[id^="serverPlayers"]').toArray().map(a => a.innerText).forEach( e => {
  1094. let [plys, mx] = e.split('/')
  1095. players += +plys;
  1096. max += +mx;
  1097. })
  1098. newMsg = `${originalMsg}Server Players: ${$('[id^="serverRow"].active>[id^="serverPlayers"]').text()}, Total Players: ${players.toLocaleString('en-US') + ' / ' + max.toLocaleString('en-US')}`
  1099. break;
  1100. }
  1101. case 'server':
  1102. newMsg = `${originalMsg}Currently playing in ${$('[id^="serverRow"].active>*').first().text()}`;
  1103. break;
  1104. case 'abil': case 'abils': case 'ability': case 'abilities': { // not finished
  1105. let arr = [], newStr = '';
  1106. $('.checkmark').each(function(){
  1107. if($(this).css('display') == 'block') arr.push($(this).siblings().eq(1).attr('item'));
  1108. })
  1109. if(misc_settings.abil?.[currentUser] != null || arr.length){
  1110. const abil = misc_settings.abil?.[currentUser],
  1111. map = {
  1112. 18: 'Freeze',
  1113. 22: 'Invis Cloak',
  1114. 20: '2x Spawn',
  1115. 23: '2x Exp'
  1116. }
  1117. if(currentUser == "Please Login First"){
  1118. newMsg = `${originalMsg}Active Abilities: none`;
  1119. break;
  1120. }
  1121. 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, ') : ', ');
  1122. newMsg = `${originalMsg}Active Abilities: ${newStr == '' ? 'none' : newStr}`;
  1123. newMsg = newMsg.replace(/,[^,]*$/g, '');
  1124. } else {
  1125. newMsg = `${originalMsg}Active Abilities: none`;
  1126. }
  1127. break;
  1128. }
  1129. case 'xpproj': case 'xpprojected': case 'projectedxp': case 'projxp': case 'levelprojected': case 'lvlproj': case 'lvlprojected': {
  1130. const projectedHr = lastHrXP.length > 0 ? sigma(getProperty(lastHrXP.slice(-5), "gained")) * 12 : 0;
  1131. newMsg = `${originalMsg}Next Hour Projected XP: ${xpStatsInPercentages ? round(convertToPerc(projectedHr, currentLevel) * 100, 2) + '%' : round(projectedHr).toLocaleString('en-US')}`;
  1132. break;
  1133. }
  1134. case 'projcoins': case 'projcoin': case 'projectedcoin': case 'projectedcoins': case 'coinsproj': case 'coinproj': case 'coinprojected': case 'coinsprojected': {
  1135. const projectedHr = lastHrCoins.length > 5 ? Math.round(sigma(getProperty(lastHrCoins.slice(-5), "gained")) * 12) : 0;
  1136. newMsg = `${originalMsg}Next Hour Projected Coins: ${projectedHr.toLocaleString('en-US')}`;
  1137. break;
  1138. }
  1139. case 'xpremaining': case 'xprem': case 'remainingxp': case 'remxp': {
  1140. const xpRemaining = currentLevel ? currentLevel * 1e3 - currentPercent * currentLevel * 1e3 : 0,
  1141. newMsg = `${originalMsg}XP Remanining: ${xpStatsInPercentages ? round(convertToPerc(xpRemaining, currentLevel) * 100, 2) + '%' : round(xpRemaining).toLocaleString('en-US')}`;
  1142. break;
  1143. }
  1144. case 'coinsremaining': case 'coinsrem': case 'remainingcoins': case 'remcoins': {
  1145. const coinsRemaining = currentCoins ? 25e4 - currentCoins % 25e4 : 0;
  1146. newMsg = `${originalMsg}Coins Remanining: ${coinsRemaining.toLocaleString('en-US')}`;
  1147. break;
  1148. }
  1149. case 'xplasthour': case 'lasthourxp': case 'hrxp': case 'xphr': case 'hourxp': case 'xphour': {
  1150. const lastHr = lastHrXP.length > 0 ? sigma(getProperty(lastHrXP, "gained")) : 0;
  1151. newMsg = `${originalMsg}XP Last Hour: ${xpStatsInPercentages ? round(convertToPerc(lastHr, currentLevel) * 100, 2) + '%' : round(lastHr).toLocaleString('en-US')}`;
  1152. break;
  1153. }
  1154. case 'coinslasthour': case 'lasthourcoins': case 'hrcoins': case 'coinshr': case 'hourcoins': case 'coinshour': {
  1155. const lastHr = lastHrCoins.length > 0 ? Math.round(sigma(getProperty(lastHrCoins, "gained"))) : 0
  1156. newMsg = `${originalMsg}Coins Last Hour: ${lastHr.toLocaleString('en-US')}`;
  1157. break;
  1158. }
  1159. case 'xplastmin': case 'xplastminute': case 'lastminxp': case 'lastminutexp': case 'minxp': case 'minutexp': case 'xpmin': {
  1160. const lastMin = lastMinXP.length > 0 ? sigma(getProperty(lastMinXP, "gained")) : 0;
  1161. newMsg = `${originalMsg}XP Last Minute: ${xpStatsInPercentages ? round(convertToPerc(lastMin, currentLevel) * 100, 2) + '%' : round(lastMin).toLocaleString('en-US')}`;
  1162. break;
  1163. }
  1164. case 'coinslastmin': case 'coinslastminute': case 'lastmincoins': case 'lastminutecoins': case 'coinsxp': case 'minutecoins': case 'coinsmin': {
  1165. const lastMin = lastMinCoins.length > 0 ? Math.round(sigma(getProperty(lastMinCoins, "gained"))) : 0;
  1166. newMsg = `${originalMsg}Coins Last Minute: ${lastMin.toLocaleString('en-US')}`;
  1167. break;
  1168. }
  1169. case 'xplast12s': case 'xplast12sec': case 'xplast12seconds': case 'last12secondsxp': case 'lastminutexp': case 'xp12s': case '12sxp': case 'xp12sec': {
  1170. const last12sec = lastMinXP.length > 0 ? lastMinXP[lastMinXP.length - 1].gained : 0;
  1171. newMsg = `${originalMsg}XP Last 12 Seconds: ${xpStatsInPercentages ? round(convertToPerc(last12sec, currentLevel) * 100, 2) + '%' : round(last12sec).toLocaleString('en-US')}`;
  1172. break;
  1173. }
  1174. case 'xpsession': case 'sessionxp': case 'xpsesh': case 'sesh': case 'session': case 'seshxp': {
  1175. const sessionXP = currentCoins && accounts[currentUser] ? Math.round(currentCoins - accounts[currentUser].coins): 0;
  1176. newMsg = `${originalMsg}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)}`;
  1177. break;
  1178. }
  1179. case 'lifetimexp': case 'xplifetime':
  1180. newMsg = `${originalMsg}Lifetime XP: ${round(currentXP ?? 0).toLocaleString('en-US')}`;
  1181. break;
  1182. case 'coinssession': case 'sessioncoins': case 'coinssesh': case 'seshcoins': {
  1183. const sessionCoins = currentCoins && accounts[currentUser] ? Math.round(currentCoins - accounts[currentUser].coins): 0;
  1184. newMsg = `${originalMsg}Session Coins: ${sessionCoins.toLocaleString('en-US')}, Session Length:${msToTime(Date.now() - scriptStartCoins)}`;
  1185. break;
  1186. }
  1187. case 'ratewaifu': case 'waifu': case 'waifurating': case 'waifurate': case 'howwaifu':
  1188. if(params?.length == 0){
  1189. if(currentUser == "Please Login First"){
  1190. newMsg = `${originalMsg}My Waifu Rating: 0 / 10`;
  1191. break;
  1192. }
  1193. newMsg = `${originalMsg}My Waifu Rating: ${Math.round(+String(Math.round(hashCode(currentUser, 5) * 102.2)).slice(-3, -1) / 10)} / 10`;
  1194. break;
  1195. }
  1196. newMsg = `${originalMsg}${params[0]}'s Waifu Rating: ${Math.round(+String(Math.round(hashCode(params[0], 5) * 102.2)).slice(-3, -1) / 10)} / 10`;
  1197. break;
  1198. case 'ratepro': case 'pro': case 'prorating': case 'prorate': case 'howpro':
  1199. if(params?.length == 0){
  1200. if(currentUser == "Please Login First"){
  1201. newMsg = `${originalMsg}I am 0% pro`;
  1202. break;
  1203. }
  1204. newMsg = `${originalMsg}I am ${Math.round(+String(Math.round(hashCode(currentUser, 5) * 194.3)).slice(-4, -1) / 10)}% pro`;
  1205. break;
  1206. }
  1207. newMsg = `${originalMsg}${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 5) * 194.3)).slice(-4, -1) / 10)}% pro`;
  1208. break;
  1209. case 'ratedog': case 'dog': case 'dograting': case 'dograte': case 'howdog':
  1210. if(params?.length == 0){
  1211. if(currentUser == "Please Login First"){
  1212. newMsg = `${originalMsg}I am 0% dog`;
  1213. break;
  1214. }
  1215. newMsg = `${originalMsg}I am ${Math.round(+String(Math.round(hashCode(currentUser, 6) * 189.3)).slice(-4, -1) / 10)}% dog`;
  1216. break;
  1217. }
  1218. newMsg = `${originalMsg}${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 6) * 189.3)).slice(-4, -1) / 10)}% dog`;
  1219. break;
  1220. case 'rateking': case 'king': case 'kingrating': case 'kingrate': case 'howking':
  1221. if(params?.length == 0){
  1222. if(currentUser == "Please Login First"){
  1223. newMsg = `${originalMsg}I am 0% king`;
  1224. break;
  1225. }
  1226. newMsg = `${originalMsg}I am ${Math.round(+String(Math.round(hashCode(currentUser, 5) * 389.3)).slice(-4, -1) / 10)}% king`;
  1227. break;
  1228. }
  1229. newMsg = `${originalMsg}${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 5) * 389.3)).slice(-4, -1) / 10)}% king`;
  1230. break;
  1231. case 'dice': case 'die': case 'roll': case 'rolldice': {
  1232. const sides = params?.length == 0 || isNaN(+params[0]) ? 6 : +params[0];
  1233. newMsg = `${originalMsg}Rolled a dice with ${sides.toLocaleString('en-US')}, landed on ${Math.round(RNG(1, sides)).toLocaleString('en-US')}`;
  1234. break;
  1235. }
  1236. case 'rng': case 'random': case 'randomnumber': case 'number': case 'num': {
  1237. const min = params?.length < 1 || isNaN(+params[0]) ? 0 : +params[0];
  1238. const max = params?.length < 2 || isNaN(+params[1]) ? 10 : +params[1];
  1239. if(min > max){
  1240. curserMsg(`Please make sure your minimum (${min}) is less than your maximum (${max})`, 'red', 5e3);
  1241. newMsg = '';
  1242. break;
  1243. }
  1244. newMsg = `${originalMsg}Generated a random number between ${min.toLocaleString('en-US')} and ${max.toLocaleString('en-US')}, chose: ${Math.round(RNG(min, max)).toLocaleString('en-US')}`;
  1245. break;
  1246. }
  1247. case 'ratefriends': case 'friendsrating': case 'friendsrate': case 'howfriends':
  1248. if(params?.length == 0){
  1249. if(currentUser == "Please Login First"){
  1250. newMsg = `${originalMsg}I am 0% friends with a frog`;
  1251. break;
  1252. }
  1253. newMsg = `${originalMsg}I am ${Math.round(+String(Math.round(hashCode(currentUser, 4) * 283.7)).slice(-4, -1) / 10)}% friends with a frog`;
  1254. break;
  1255. }
  1256. if(params?.length == 1){
  1257. if(currentUser == "Please Login First"){
  1258. newMsg = `${originalMsg}I am 0% friends with a ${params[0]}`;
  1259. break;
  1260. }
  1261. newMsg = `${originalMsg}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]}`;
  1262. break;
  1263. } else {
  1264. newMsg = `${originalMsg}${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]}`;
  1265. break;
  1266. }
  1267. case 'rateenemies': case 'enemiesrating': case 'enemiesrate': case 'howenemies': case 'enemies':
  1268. if(params?.length == 0){
  1269. if(currentUser == "Please Login First"){
  1270. newMsg = `${originalMsg}I am 0% enemies with a frog`;
  1271. break;
  1272. }
  1273. newMsg = `${originalMsg}I am ${Math.round(+String(Math.round(hashCode(currentUser, 4) * 164.45)).slice(-5, -2) / 10)}% enemies with a frog`;
  1274. break;
  1275. }
  1276. if(params?.length == 1){
  1277. if(currentUser == "Please Login First"){
  1278. newMsg = `${originalMsg}I am 0% enemies with a ${params[0]}`;
  1279. break;
  1280. }
  1281. newMsg = `${originalMsg}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]}`;
  1282. break;
  1283. } else {
  1284. newMsg = `${originalMsg}${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]}`;
  1285. break;
  1286. }
  1287. case 'flip': case 'coinflip': case 'heads': case 'tails': case 'flipcoin': {
  1288. newMsg = `${originalMsg}Coin flipped! Landed on ${Math.round(RNG(0, 1)) ? 'heads' : 'tails'}`;
  1289. break;
  1290. }
  1291. case 'script': case 'version': case 'fsfb':
  1292. newMsg = `${originalMsg}Using fsfb script! Version ${version}`
  1293. break;
  1294. case 'time': case 'localtime': case 'localetime': case 'date':
  1295. newMsg = `${originalMsg}My Time: ${new Date().toLocaleString('en-US')}`
  1296. break;
  1297. case 'leaderboard': case 'leader': case 'lb':
  1298. newMsg = `${originalMsg}My leaderboard position: ${!unsafeWindow.playerIsAlive() || leaderboardPos ? leaderboardPos : 'none, not alive'}`
  1299. break;
  1300. default:
  1301. newMsg = $chtbox.val();
  1302. break;
  1303. }
  1304. // (unsafeWindow.kfjsdafl != null) ? $chtbox.val(newMsg) : $chtbox.val(''), console.log(newMsg);;
  1305. $chtbox.val(newMsg)
  1306. })
  1307. }
  1308.  
  1309. // check if player is alive
  1310. let playerAlive = false, timeAlive;
  1311. unsafeWindow.playerIsAlive = () => playerAlive && $('#advert').css('display') != 'none' ? false : playerAlive;
  1312. const playerIsAlive = unsafeWindow.playerIsAlive;
  1313. const _setNick = unsafeWindow.setNick
  1314. unsafeWindow.setNick = function(){
  1315. if(arguments[1] || !playerAlive) timeAlive = Date.now();// respawned
  1316. playerAlive = true;
  1317. setTimeout(() => { svSwitch = true }, 1000);
  1318. return _setNick.apply(this, arguments);
  1319. }
  1320. const _closeAdvert = unsafeWindow.closeAdvert
  1321. unsafeWindow.closeAdvert = function(){
  1322. playerAlive = false;
  1323. return _closeAdvert.apply(this, arguments);
  1324. }
  1325.  
  1326. const getPowerMessage = (line, firstMsg) => {
  1327. let obj = {
  1328. string1: firstMsg + 'Inv (1/?): ',
  1329. string2: firstMsg + 'Inv (2/?): ',
  1330. string3: firstMsg + 'Inv (3/?): '
  1331. }
  1332. updatePwCount();
  1333. const newPws = {
  1334. Recombine: pws.rec,
  1335. Speed: pws.spd,
  1336. Growth: pws.grw,
  1337. Virus: pws.vrs,
  1338. Mothercell: pws.mtcl,
  1339. Portal: pws.prtl,
  1340. 'Gold Block': pws.gblk,
  1341. Freeze: pws.fz,
  1342. Push: pws.psh,
  1343. Wall: pws.wall,
  1344. 'Anti-Freeze': pws.afz,
  1345. 'Anti-Recombine': pws.arc,
  1346. Shield: pws.shld,
  1347. 'Frozen Virus': pws.fzvs
  1348. }
  1349. for(let i in newPws){
  1350. let add = '';
  1351. add += newPws[i] == '' ? '' : newPws[i];
  1352. add += newPws[i] == '' ? '' : ' ';
  1353. add += newPws[i] == '' ? '' : (i + ', ');
  1354. if(obj.string1.length + add.length <= 100) obj.string1 += add;
  1355. else if(obj.string2.length + add.length <= 100) obj.string2 += add;
  1356. else obj.string3 += add;
  1357. }
  1358. if(obj.string1 == firstMsg + `Inv (1/?): ` && line == 1) return 'Inv (1/1): no powers';
  1359. if(obj['string' + line] == firstMsg + `Inv (${line}/?): `){
  1360. curserMsg(`This inventory line doesn't exist! Try a smaller number`, 'red', 6e3);
  1361. return ''
  1362. }
  1363. let totalLines = 1;
  1364. if(obj.string3 == firstMsg + 'Inv (3/?): ' && obj.string2 != firstMsg + 'Inv (2/?): ') totalLines = 2;
  1365. else if(obj.string3 != firstMsg + 'Inv (3/?): ' && obj.string2 != firstMsg + 'Inv (2/?): ') totalLines = 3;
  1366. const repl = str => str.replace(/,[^,]*$/g, '').replace(/\?/g, totalLines);
  1367. return line == 1 ? repl(obj.string1) : line == 2 ? repl(obj.string2) : repl(obj.string3);
  1368. }
  1369.  
  1370. let pws = {rec: '', spd: '', grw: '', vrs: '', mtcl: '', prtl: '', gblk: '', fz: '', psh: '', wall: '', afz: '', arc: '', shld: '', fzvs: ''};
  1371. const updatePwCount = () => {
  1372. $('.inventory-box').each(function(){
  1373. const val = $(this).css('display') != 'none' ? $(this).children().eq(0).text() || '1' : '';
  1374. const map = {
  1375. 'invWall' : 'wall',
  1376. 'invAntiFreeze': 'afz',
  1377. 'invAntiRecombine': 'arc',
  1378. 'invShield': 'shld',
  1379. 'invFrozenVirus': 'fzvs',
  1380. 'invRecombine': 'rec',
  1381. 'invSpeed': 'spd',
  1382. 'invGrowth': 'grw',
  1383. 'invSpawnVirus': 'vrs',
  1384. 'invSpawnMothercell': 'mtcl',
  1385. 'invSpawnPortal': 'prtl',
  1386. 'invSpawnGoldOre': 'gblk',
  1387. 'invFreeze': 'fz',
  1388. 'inv360Shot': 'psh'
  1389. }
  1390. if(map[$(this)[0].id] != null) pws[map[$(this)[0].id]] = val;
  1391. })
  1392. }
  1393.  
  1394. $('#fsfb-export-btn').on('click', () => {
  1395. if(!$('#fsfb-sect-imexport>label>input').is(':checked')) return void(curserMsg('You need to select at least one setting to export!', 'red'));
  1396. if($('#fsfb-custom-bg').is(':checked')) curserMsg('Exporting settings with custom background included is likely to increase the file size', 'red', 4e3);
  1397. let script_settings = {},
  1398. script_themes = {};
  1399. for (let i in settings) { // put the settings & themes into different objects
  1400. if (i == "theme" || i == "theme_boxes") script_themes[i] = settings[i];
  1401. else if (i == "export_import") {}
  1402. else script_settings[i] = settings[i];
  1403. }
  1404. let bgImg = null;
  1405. const downloadFile = () => {
  1406. let obj = {
  1407. selected: {
  1408. game_settings: $('#fsfb-game-settings').is(':checked'),
  1409. game_controls: $('#fsfb-game-controls').is(':checked'),
  1410. game_background: $('#fsfb-custom-bg').is(':checked'),
  1411. script_settings: $('#fsfb-script-settings').is(':checked'),
  1412. script_theme: $('#fsfb-theme-settings').is(':checked')
  1413. },
  1414. game_settings: localStorage.settings,
  1415. game_controls: localStorage.hotkeys,
  1416. game_background: bgImg,
  1417. script_settings: script_settings,
  1418. script_theme: script_themes
  1419. }
  1420. // if the setting is turned off, then dont need to export it - set it to null
  1421. if(!obj.selected.game_settings) obj.game_settings = null;
  1422. if(!obj.selected.game_controls) obj.game_controls = null;
  1423. if(!obj.selected.game_background) obj.game_background = null;
  1424. if(!obj.selected.script_settings) obj.script_settings = null;
  1425. if(!obj.selected.script_theme) obj.script_theme = null;
  1426.  
  1427. const a = document.createElement('a'),
  1428. file = new Blob([JSON.stringify(obj)], {type: "text/plain"});
  1429. a.href = URL.createObjectURL(file), a.download = "fsfb script settings", a.click(), URL.revokeObjectURL(a.href); // download a txt file of exported settings
  1430. }
  1431. if($('#fsfb-custom-bg').is(':checked')){
  1432. let db, req = unsafeWindow.indexedDB.open("AgmaDB", 1);
  1433. req.onsuccess = function (x) {
  1434. db = req.result;
  1435. db.onclose = (c) => { db = null; }
  1436. db.onversionchange = (c) => { db.close(), db = null; }
  1437. let _ = db.transaction("general", "readonly").objectStore("general").get("cbgDataURL");
  1438. _.onsuccess = () => {
  1439. bgImg = _.result;
  1440. downloadFile();
  1441. }
  1442. }, req.onupgradeneeded = function (c) {
  1443. let req = c.target.result;
  1444. if (!req.objectStoreNames.contains("general")) {
  1445. req.createObjectStore("general");
  1446. }
  1447. };
  1448. } else {
  1449. downloadFile();
  1450. }
  1451. });
  1452.  
  1453. $('#fsfb-import-btn').on('click', () => {
  1454. if(!$('#fsfb-sect-imexport>label>input').is(':checked')) return void(curserMsg('You need to select at least one setting to import!', 'red'));
  1455. if($('#fsfb-custom-bg').is(':checked')) curserMsg('Pasting in settings that include a background image is likely to cause initial lag', 'red', 0);
  1456. swal({
  1457. title: "Import Settings",
  1458. text: "Add your exported settings below and press OK to continue.",
  1459. type: "input",
  1460. showCancelButton: true,
  1461. closeOnConfirm: false,
  1462. animation: "slide-from-top",
  1463. inputPlaceholder: "Paste exported settings here"
  1464. },
  1465. function(inputVal){
  1466. if (inputVal == null) return false;
  1467. if (inputVal == "") {
  1468. swal.showInputError("Please don't leave the input empty!");
  1469. return false
  1470. }
  1471. try {
  1472. let val = JSON.parse(inputVal);
  1473. if(val.selected.script_settings && $('#fsfb-script-settings').is(':checked')){
  1474. for (let i in val.script_settings) {
  1475. for (let j in val.script_settings[i]) {
  1476. for(let x in settings){
  1477. for(let y in settings[x]){
  1478. if(val.script_settings[i][j].id == settings[x][y].id) settings[x][y] = val.script_settings[i][j];
  1479. }
  1480. }
  1481. }
  1482. }
  1483. }
  1484. if(val.selected.script_theme && $('#fsfb-theme-settings').is(':checked')){
  1485. for (let i in val.script_theme) {
  1486. for (let j in val.script_theme[i]) {
  1487. for(let x in settings){
  1488. for(let y in settings[x]){
  1489. if(val.script_theme[i][j].id == settings[x][y].id) settings[x][y] = val.script_theme[i][j];
  1490. }
  1491. }
  1492. }
  1493. }
  1494. }
  1495. if(val.selected.game_settings && $('#fsfb-game-settings').is(':checked')) localStorage.setItem('settings', val.game_settings);
  1496. if(val.selected.game_controls && $('#fsfb-game-controls').is(':checked')) localStorage.setItem('hotkeys', val.game_controls);
  1497. if(val.selected.game_background && $('#fsfb-custom-bg').is(':checked')){
  1498. let db, req = unsafeWindow.indexedDB.open("AgmaDB", 1);
  1499. req.onsuccess = function (x) {
  1500. db = req.result;
  1501. db.onclose = (c) => { db = null; }
  1502. db.onversionchange = (c) => { db.close(), db = null; }
  1503. let _ = db.transaction("general", "readwrite").objectStore("general").put(val.game_background, "cbgDataURL");
  1504. }, req.onupgradeneeded = function (c) {
  1505. let req = c.target.result;
  1506. if (!req.objectStoreNames.contains("general")) {
  1507. req.createObjectStore("general");
  1508. }
  1509. };
  1510. }
  1511. // if any agma controls were imported, need to refresh bc it's set using localstorage
  1512. 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'))){
  1513. swal({
  1514. title: "Please Refresh!",
  1515. text: "Refreshing the page is required for your imported agma settings to take effect",
  1516. type: "warning",
  1517. });
  1518. } else swal({ title: "Settings Successfully Imported!", type: "success" });
  1519. saveSettings();
  1520. updateScriptSettingsUI();
  1521. } catch (error){
  1522. swal({
  1523. title: "Something went wrong!",
  1524. text: "Please make sure you've entered in valid settings",
  1525. type: "error"
  1526. });
  1527. }
  1528. });
  1529.  
  1530. });
  1531.  
  1532. $('#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>`)
  1533.  
  1534. if(hideAds){
  1535. localStorage.ad_l_time = "9e99"; // smth like time since last ad
  1536. $('[id^="agma-io_"], [id^="adWrapper"], #preroll').addClass("fuckAds"); // move ads way off the screen
  1537. } else localStorage.ad_l_time = Date.now();
  1538.  
  1539. let width = unsafeWindow.innerWidth, height = unsafeWindow.innerHeight;
  1540. $(unsafeWindow).on('resize', function(){
  1541. width = unsafeWindow.innerWidth, height = unsafeWindow.innerHeight;
  1542. });
  1543.  
  1544. let pointMove;
  1545. const linesplit = () => {
  1546. if(!linesplitting) return;
  1547. 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}];
  1548. if(linesplitClosestSide){
  1549. let closestSide = [mosY / height, (height - mosY) / height, mosX / width, (width - mosX) / width]; // top, bottom, left, right
  1550. closest = points[closestSide.indexOf(Math.min(...closestSide))];
  1551. } else {
  1552. let distance = p => Math.sqrt(Math.pow(mosX - p.x, 2) + Math.pow(mosY - p.y, 2)),
  1553. closestPoint = points.reduce((a, b) => distance(a) < distance(b) ? a : b);
  1554. for (let i = 0; i < points.length; i++) {
  1555. if (closestPoint.x == points[i].x && closestPoint.y == points[i].y) closest = points[i]
  1556. }
  1557. }
  1558. pointMove = {x: closest.nx, y: closest.ny};
  1559. $('canvas').trigger($.Event('mousemove', { clientX: closest.nx, clientY: closest.ny }));
  1560. $("#linesplit-markers div").css('background-color', 'transparent');
  1561. $('#linesplit-' + closest.n).css('background-color', '#e25615');
  1562. }
  1563.  
  1564. let confBtns = document.getElementsByClassName('purchase-btn confirmation');
  1565. const btnsArr = Array.from(document.getElementsByClassName('purchase-btn confirmation'));
  1566.  
  1567. const changeHTML = (index, price, id, name) => {
  1568. setTimeout(() => {
  1569. const amtDropdown = document.getElementById('shopAmountDropdown')
  1570. 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.';
  1571. const dropdownChange = () => {
  1572. if (amtDropdown.value == "custom") return buyCstmAmt(price, id, name);
  1573. let priceSum = (amtDropdown.value * price).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  1574. 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.';
  1575. };
  1576. amtDropdown.addEventListener('change', dropdownChange);
  1577. }, 30);
  1578. };
  1579. const buyCstmAmt = (price, id, name) => {
  1580. swal({
  1581. title: "Enter Purchase Amount",
  1582. text: "<span>How many <b>" + name + "</b> would you like to buy?</span>",
  1583. html: true,
  1584. type: "input",
  1585. showCancelButton: true,
  1586. closeOnConfirm: false,
  1587. inputPlaceholder: "Input amount here"
  1588. },
  1589. function(input){
  1590. let pwAmt = +input,
  1591. priceTotal = pwAmt * price;
  1592. if (input == null) return false;
  1593. if (input == "") {
  1594. swal.showInputError("Please don't leave the input empty!");
  1595. return false
  1596. }
  1597. if(!/^[0-9]+$/.test(input) || !(+input >>> 0 === parseFloat(+input))){
  1598. swal.showInputError("Please only enter positive integers!");
  1599. return false
  1600. }
  1601. swal({
  1602. title: 'Confirm',
  1603. 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>',
  1604. html: true,
  1605. type: 'warning',
  1606. showCancelButton: true,
  1607. closeOnConfirm: false,
  1608. confirmButtonColor: '#4CAF50',
  1609. confirmButtonText: 'Yes, confirm purchase',
  1610. cancelButtonText: 'No, cancel purchase'
  1611. }, function(confirmed){
  1612. if(confirmed) buyPw(id, pwAmt);
  1613. })
  1614.  
  1615. });
  1616. };
  1617. // buy a certain amount of powers including > 255
  1618. const buyPw = (shopID, pwAmt) => {
  1619. if (pwAmt < 1) return;
  1620. if (pwAmt > 255) {
  1621. for (let i = 0; i < ~~(pwAmt / 255); i++) setTimeout(() => purchaseItem(shopID, 255), 250 * i + 250);
  1622. purchaseItem(shopID, pwAmt % 255);
  1623. } else purchaseItem(shopID, pwAmt);
  1624. };
  1625.  
  1626.  
  1627. if(improvedShop){
  1628. // add event listeners to dropdown buttons
  1629. for (let i = 0; i < confBtns.length; i++) {
  1630. confBtns[i].onclick = function () {
  1631. setTimeout(() => {
  1632. 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>';
  1633. }, 5);
  1634. }
  1635. }
  1636. // add event listeners to shop buttons
  1637. for (let i = 0; i < 16; i++) {
  1638. if (i != 10 && i != 11){
  1639. btnsArr[i].addEventListener('click', () => {
  1640. let index = i;
  1641. changeHTML(index, btnsArr[index].getAttribute('price'), btnsArr[index].getAttribute('item'), $('.purchase-btn.confirmation[item="' + btnsArr[index].getAttribute('item') + '"]').prev().find('h3').eq(0).text());
  1642. });
  1643. }
  1644. }
  1645. }
  1646.  
  1647.  
  1648. // add extra bot packs
  1649. const newBots = [
  1650. {title: "100 Bots", time: "24 HOURS", price: "700,000", id: 9},
  1651. {title: "125 Bots", time: "48 HOURS", price: "900,000", id: 10},
  1652. {title: "300 Bots", time: "24 HOURS", price: "900,000", id: 7},
  1653. {title: "100 MASS Bots", time: "1 HOURS", price: "800,000", id: 8},
  1654. {title: "300 Bots", time: "72 HOURS", price: "2,000,000", id: 11},
  1655. {title: "100 MASS Bots", time: "24 HOURS", price: "2,600,000", id: 12}
  1656. ]
  1657.  
  1658. const minul = document.getElementsByClassName('tab-container-section minion scroll')[0].children[0].children[0]
  1659. $('.confirm-minion[item=7], .confirm-minion[item=8]').parent().hide();
  1660. document.getElementById('extraTab').childNodes[0].setAttribute('onclick', '');
  1661.  
  1662. function createEl(i) {
  1663. const botEl = document.createElement('li');
  1664. botEl.setAttribute('class', 'masterTooltip extra-min');
  1665. 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.');
  1666. minul.appendChild(botEl);
  1667. botEl.innerHTML = `
  1668. <div class="title_prch">
  1669. <img src="img/store/minions/minions_tab.png" width="70px" height="60px">
  1670. <div class="minionDescription">
  1671. <h4 style="font-size: 18px;">` + i.title + `</h4>
  1672. <h3 style="margin-top:10px;color:white;"> ` + i.time + `</h3>
  1673. </div> <span class="win-price">` + i.price + `</span>
  1674. </div>
  1675. <a href="#" price="` + i.price + `" item="` + i.id + `" class="purchase-btn2 confirm-minion extra-min">Buy</a>
  1676. `;
  1677. }
  1678. if(extraBotPacks){
  1679. for(let i of newBots) createEl(i);
  1680. }
  1681.  
  1682. function warnBeforeMinion(linkURL, priceK,itemId) {
  1683. swal({
  1684. title: "Confirm",
  1685. text: 'If you click "Buy", you will purchase these minions. They cost ' + priceK,
  1686. type: "warning",
  1687. showCancelButton: true,
  1688. confirmButtonColor: "#4CAF50",
  1689. confirmButtonText: "Yes, confirm purchase",
  1690. cancelButtonText: "No, cancel purchase"
  1691. }, function() {
  1692. purchaseMinion(itemId);
  1693. });
  1694. }
  1695.  
  1696. // listen for when a min pack is bought
  1697. let minBoughtAmt = {};
  1698. if(extraChatCommands){
  1699. let lastClickedPrice, currentPrice, lastID;
  1700. const confirmClicked = () => {
  1701. setTimeout(() => {
  1702. if($('.sweet-alert h2').text() != "Success!" || lastClickedPrice != currentPrice) return;
  1703. const map = {
  1704. 1: '10 Bots 1 Hour',
  1705. 2: '40 Bots 1 Hour',
  1706. 3: '50 Bots 2 Hours',
  1707. 4: '80 Bots 1 Hour',
  1708. 5: '100 Bots 4 Hours',
  1709. 6: '125 Bots 8 Hours',
  1710. 7: '300 Bots 24 Hours',
  1711. 8: '100 MASS Bots 1 Hour',
  1712. 9: '100 Bots 24 Hours',
  1713. 10: '125 Bots 48 Hours',
  1714. 11: '300 Bots 72 Hours',
  1715. 12: '100 MASS Bots 24 Hours'
  1716. }
  1717. minBoughtAmt[currentUser] = {...minsChatAmt[currentUser], ... {chatAmt: map[lastID], started: false}};
  1718. }, 900);
  1719. };
  1720. $('.purchase-btn2.confirm-minion[item]').on('click', function () {
  1721. lastClickedPrice = this.getAttribute('price').replace(/,/g, '');
  1722. lastID = this.getAttribute('item');
  1723. $('.confirm').attr('disabled', 'true'); // disable so user doesn't buy early - swal is slow to load text
  1724. setTimeout(() => {
  1725. $('.confirm').removeAttr('disabled');
  1726. currentPrice = $('.sweet-alert.showSweetAlert.visible p').eq(0).text().replace(/\D+/g, '');
  1727. }, 750);
  1728. setTimeout(() => $('.confirm')[0].addEventListener('click', confirmClicked), 500);
  1729. })
  1730. }
  1731.  
  1732. // context menu: click on skin -> skin ID to clipboard, click on name -> name to clipboard
  1733. $('#contextPlayer').on('click', e => {
  1734. if(!rightClickCopyInfo) return;
  1735. if($('#contextPlayerSkin').width() + $('#contextPlayerSkin').offset().left + 5 > e.pageX){ // bcs #contextMenu event :dog:
  1736. // cell was clicked
  1737. if($('#contextPlayerSkin').css('background-image') != "none"){
  1738. let skinID = $('#contextPlayerSkin').css('background-image').match(/(?<=\/skins\/)[0-9]+/gm)[0]; // get the skin image, then match only the skin's ID
  1739. navigator.clipboard.writeText(skinID).then(function() {
  1740. curserMsg('Skin ID of ' + skinID + ' was copied to your clipboard.', 'green');
  1741. }, function() {
  1742. curserMsg('Something went wrong. Nothing was added to your clipboard.', 'red');
  1743. });
  1744. } else if($('#contextPlayerSkin').css('background-color') == 'rgb(51, 51, 51)') curserMsg('No player selected. Nothing was added to your clipboard.', 'red');
  1745. else curserMsg('No skin equipped. Nothing was added to your clipboard.', 'red');
  1746. } else { // name was clicked
  1747. if($('#contextPlayerSkin').css('background-color') == 'rgb(51, 51, 51)') curserMsg('No player selected. Nothing was added to your clipboard.', 'red');
  1748. else {
  1749. navigator.clipboard.writeText($('#contextPlayerName').text()).then(function() {
  1750. curserMsg('Nickname: "' + $('#contextPlayerName').text() + '" was copied to your clipboard', 'green');
  1751. }, function() {
  1752. curserMsg("Something went wrong. Nothing was added to your clipboard.", "red");
  1753. });
  1754. }
  1755. }
  1756. });
  1757.  
  1758.  
  1759. // little bar at the top of the screen that tells u stuff
  1760. const curserMsg = (msg, color, time) => {
  1761. if(color == "green") color = "rgb(0, 192, 0)";
  1762. if(color == "red") color = "rgb(255, 0, 0)";
  1763. if(color == "gray") color = "rgb(153, 153, 153)";
  1764. $('#curser').text(msg).show().css('color', color)
  1765. if(time != 0) setTimeout(() => $('#curser').fadeOut(400), time ?? 4e3);
  1766. }
  1767.  
  1768. let minTimeRemaining = 0;
  1769. const startMin = unsafeWindow.strMin;
  1770. unsafeWindow.strMin = function(){
  1771. minsStart();
  1772. return startMin.apply(this, arguments);
  1773. }
  1774. let minsChatAmt = {},
  1775. accGoldMem = {};
  1776. const minsStart = async() => {
  1777. let minAmt, minsStarted = false;
  1778. const waitUntil1 = (condition) => new Promise(resolve => {
  1779. let interval = setInterval(() => {
  1780. $('#infoContent').children().each(function(){
  1781. if($(this).text().includes('Minion Time:')) minsStarted = true;
  1782. })
  1783. condition() && (clearInterval(interval), resolve());
  1784. }, 100);
  1785. setTimeout(() => { (clearInterval(interval), resolve()) }, 1e4);
  1786. });
  1787. await waitUntil1(() => minsStarted == true);
  1788. if(!minsStarted) return;
  1789. $('#infoContent').children().each(function(){
  1790. if($(this).text().includes('Minion Time:')) minTimeRemaining = $(this).find('span').text();
  1791. if($(this).text().includes('Minions:')) minAmt = $(this).find('span').text()
  1792. })
  1793. let timeArr = minTimeRemaining.split(':'), msBotsTime = (3.6e6 * +timeArr[0]) + (6e4 * +timeArr[1]) + (1e3 * +timeArr[2]);
  1794. misc_settings.bots[currentUser] = {...misc_settings.bots[currentUser], ...{active: true, amt: minAmt, chatAmt: null, rem: msBotsTime, currMs: Date.now()}};
  1795. set("fsfb-misc", misc_settings);
  1796. }
  1797.  
  1798. // ability time remaining
  1799. if(showRemainingAbilityTime){
  1800. let lastClickedPrice, currentPrice, lastID;
  1801. const confirmClicked = () => {
  1802. setTimeout(() => {
  1803. if($('.sweet-alert h2').text() != "Success!" || lastClickedPrice != currentPrice) return;
  1804. misc_settings.abil[currentUser] = {...misc_settings.abil[currentUser], ...{[lastID] : Date.now()}};
  1805. set("fsfb-misc", misc_settings);
  1806. }, 900);
  1807. };
  1808. [18, 20, 22, 23].forEach(el => {
  1809. let h5 = $(`.purchase-btn.confirmation[item="${el}"]`).parents().eq(0).find('div h5');
  1810. h5.clone().insertAfter(h5).addClass('fsfb-fake').hide();
  1811. $(`.purchase-btn[item="${el}"]`).on('click', function () {
  1812. lastClickedPrice = this.getAttribute('price');
  1813. lastID = this.getAttribute('item');
  1814. $('.confirm').attr('disabled', 'true'); // disable so user doesn't buy early - swal is slow to load text
  1815. setTimeout(() => {
  1816. $('.confirm').removeAttr('disabled');
  1817. currentPrice = $('.sweet-alert.showSweetAlert.visible p').eq(0).text().replace(/\D+/g, '');
  1818. }, 750);
  1819. setTimeout(() => $('.confirm')[0].addEventListener('click', confirmClicked), 500);
  1820. })
  1821. });
  1822. }
  1823.  
  1824. // sort wearables by owned
  1825. const waitUntil = condition => new Promise(resolve => {
  1826. let interval = setInterval(() => {
  1827. condition() && (clearInterval(interval), resolve());
  1828. }, 100);
  1829. setTimeout(() => { (clearInterval(interval), resolve()) }, 15e3);
  1830. });
  1831. if(sortWearablesByOwned){
  1832. $('#wearablesTab').on('click', async()=> {
  1833. await waitUntil(() => $('#phpWearables li').length > 55);
  1834. if($('#phpWearables li').length <= 55) return;
  1835. $($('[id^="wearableUseBtn"]').get().reverse()).each(function(){
  1836. $($(this).parents().get(2)).insertBefore($("#phpWearables li:eq(0)"));
  1837. })
  1838. $('#phpWearables').append('<div id="fsfb-wearsloaded"></div>');
  1839. });
  1840. }
  1841. // https://stackoverflow.com/questions/2424191/how-do-i-make-an-element-draggable-in-jquery
  1842. $.fn.draggable = function(){
  1843. var $this = this,
  1844. ns = 'draggable_'+(Math.random()+'').replace('.',''),
  1845. mm = 'mousemove.'+ns,
  1846. mu = 'mouseup.'+ns,
  1847. $w = $(window),
  1848. isFixed = ($this.css('position') === 'fixed'),
  1849. adjX = 0, adjY = 0;
  1850.  
  1851. $this.mousedown(function(ev){
  1852. var pos = $this.offset();
  1853. if (isFixed) {
  1854. adjX = $w.scrollLeft(); adjY = $w.scrollTop();
  1855. }
  1856. var ox = (ev.pageX - pos.left), oy = (ev.pageY - pos.top);
  1857. $this.data(ns,{ x : ox, y: oy });
  1858. $w.on(mm, function(ev){
  1859. ev.preventDefault();
  1860. ev.stopPropagation();
  1861. if (isFixed) {
  1862. adjX = $w.scrollLeft(); adjY = $w.scrollTop();
  1863. }
  1864. var offset = $this.data(ns);
  1865. $this.css({left: ev.pageX - adjX - offset.x, top: ev.pageY - adjY - offset.y});
  1866. });
  1867. $w.on(mu, function(){
  1868. $w.off(mm + ' ' + mu).removeData(ns);
  1869. });
  1870. });
  1871. return this;
  1872. };
  1873.  
  1874.  
  1875. /* xp/coins statistics */
  1876.  
  1877. const updateTimeXP = 12e3; // xp bar updates every 12 seconds, don't change
  1878. let lastMinXP = [];
  1879. let lastHrXP = [];
  1880. let currentPercent, currentLevel, currentXP, currentCoins;
  1881.  
  1882. unsafeWindow.logStatsScriptXP = !1;
  1883. unsafeWindow.logStatsScriptCoins = !1;
  1884. let scriptStartCoins = Date.now();
  1885. let scriptStartXP = Date.now();
  1886. let accounts = {};
  1887. const guiDisplay = "none";
  1888. let coinsHTMLactive = false;
  1889.  
  1890. const updateTimeCoins = 6e3;
  1891. let lastMinCoins = [];
  1892. let lastHrCoins = [];
  1893.  
  1894. // add stats box html
  1895.  
  1896. const statsBody = document.createElement('div');
  1897. statsBody.setAttribute('id', 'stats-container');
  1898. statsBody.style.display = guiDisplay;
  1899. statsBody.innerHTML = `<div id="stats-main"><div id="stats-title"><title id="stats-extra-info">XP Stats - Updating Every 12s</title><div><div title="Toggle Percentages Shown" id="stats-perc-btn" class="fa fa-percent"></div><div title="Toggle Stats Shown" id="stats-change-shown" class="fa fa-eye-slash"></div><div title="Reset Stats" class="fa fa-refresh" id="stats-reset-btn"></div></div></div><div><table id="stats-table"><tbody><tr><td><label><input type="checkbox">Lvl Completed:</label></td><td>0/0</td></tr><tr><td><label><input type="checkbox">Remaining:</label></td><td>100,000</td></tr><tr><td><label><input type="checkbox">Projected (hr):</label></td><td>0</td></tr><tr><td><label><input type="checkbox">Last Hour:</label></td><td>100,000</td></tr><tr><td><label><input type="checkbox">Last Minute:</label></td><td>100,000</td></tr><tr><td><label><input type="checkbox">Last 12s:</label></td><td>100,000</td></tr><tr><td><label><input type="checkbox">Minute Mean:</label></td><td>100,000</td></tr><tr><td><label><input type="checkbox">Minute Median:</label></td><td>100,000</td></tr><tr><td><label><input type="checkbox">Minute Sd:</label></td><td>100,000</td></tr><tr><td><label><input type="checkbox">Latest Outliers:</label></td><td>100,000</td></tr><tr><td><label><input type="checkbox">Session XP:</label></td><td>100,000</td></tr><tr><td><label><input type="checkbox">Session Length:</label></td><td>100,000</td></tr><tr><td><label><input type="checkbox">Lifetime XP:</label></td><td>100,000</td></tr></tbody></table></div></div>`;
  1900. document.querySelector('body').append(statsBody)
  1901.  
  1902. $("#stats-container").draggable();
  1903. let statsboxPos = misc_settings?.statsPos ?? {top: $("#stats-container")[0].style.top, left: $("#stats-container")[0].style.left};
  1904. if(saveStatsBoxPosition && statsboxPos) $("#stats-container").css({'top' : statsboxPos.top, 'left' : statsboxPos.left});
  1905.  
  1906. const convertToPerc = (xpAmt, lvl = 0) => isNaN(xpAmt / (lvl * 1000)) ? 0 : xpAmt / (lvl * 1000);
  1907.  
  1908. $('#stats-reset-btn').on('click', () => {
  1909. if(coinsHTMLactive){
  1910. lastMinCoins = [];
  1911. lastHrCoins = [];
  1912. for(let i in accounts) accounts[i].coins = 0;
  1913. if(accounts[currentUser] !== null && currentUser !== 'Please Login First') accounts[currentUser].coins = currentCoins;
  1914. scriptStartCoins = Date.now();
  1915. } else {
  1916. lastMinXP = [];
  1917. lastHrXP = [];
  1918. for(let i in accounts) accounts[i].xp = 0;
  1919. if(accounts[currentUser] !== null && currentUser !== 'Please Login First') accounts[currentUser].xp = currentXP;
  1920. scriptStartXP = Date.now();
  1921. }
  1922. updateUI();
  1923. });
  1924. let xpStatsInPercentages = false;
  1925. $('#stats-perc-btn').on('click', () => {
  1926. xpStatsInPercentages = !xpStatsInPercentages;
  1927. updateUI();
  1928. });
  1929.  
  1930. let changingShownStats = false;
  1931. $('#stats-change-shown').on('click', () => {
  1932. changingShownStats = !changingShownStats;
  1933. if(changingShownStats){
  1934. $('#stats-table tr').show();
  1935. _replaceCSS('stats-input-css', '#stats-table input{ display: unset; }');
  1936. } else {
  1937. for(let i in misc_settings.statsSettings[coinsHTMLactive ? 'coins' : 'xp']){
  1938. misc_settings.statsSettings[coinsHTMLactive ? 'coins' : 'xp'][i] = $('#stats-' + i + ' input[type="checkbox"]').is(':checked');
  1939. $('#stats-' + i + ' input[type="checkbox"]').is(':checked') ? $('#stats-' + i).show() : $('#stats-' + i).hide();
  1940. }
  1941. set("fsfb-misc", misc_settings);
  1942. _replaceCSS('stats-input-css', '#stats-table input{ display: none; }');
  1943. }
  1944. });
  1945.  
  1946. class record {
  1947. constructor(val, lvl, user, arr) {
  1948. // this.type = this.findWhich(arr); // mostly for debugging
  1949. this.id = this.getID(arr);
  1950. this.user = user;
  1951. this.amount = val;
  1952. this.gained = this.calcGain(val, arr, this.id);
  1953. this.lvl = lvl;
  1954. // this.timestamp = Date.now(); // mostly for debugging
  1955. }
  1956. findWhich(arr) {
  1957. return ((arr == lastMinXP || arr == lastHrXP ? "xp " : "coins " ) + (arr == lastHrXP || arr == lastHrCoins ? "hour" : "minute"));
  1958. }
  1959. calcGain(val, arr, id){
  1960. const prevObj = arr[arr.length - 1];
  1961. if(prevObj && arr == lastHrXP && id == 1) return round(sigma(getProperty(lastMinXP, "gained")), 3);
  1962. if(prevObj && arr == lastHrCoins && id == 1) return round(sigma(getProperty(lastMinCoins, "gained")), 3);
  1963. return prevObj && val - prevObj.amount >= 0 && prevObj.user == this.user && prevObj.amount != 0 ? round(val - prevObj.amount, 3) : 0;
  1964. }
  1965. getID(arr) {
  1966. return arr[arr.length - 1] ? arr[arr.length - 1].id + 1 : 1;
  1967. }
  1968.  
  1969. }
  1970.  
  1971. const xpInfo = () => {
  1972. currentPercent = $('.progress-bar[role=progressbar]')[0].style.width.slice(0, -1) / 100;
  1973. currentLevel = +$('#level.user-level')[0].textContent;
  1974. currentXP = (levelSum(currentLevel) + currentLevel * currentPercent) * 1e3;
  1975. }
  1976. const coinsInfo = () => {
  1977. currentCoins = +($('#coinsDash')[0].textContent.replace(/ /g, ''));
  1978. }
  1979.  
  1980. const updateUI = () => {
  1981. if(changingShownStats) return;
  1982. if(!coinsHTMLactive){
  1983. const xpHr = lastHrXP.length,
  1984. xpMin = lastMinXP.length,
  1985. lvlCompleted = currentPercent ? String(Math.round(currentPercent * currentLevel * 1e3, 1)).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "/" + (currentLevel * 1e3).toLocaleString('en-US') : "0/0",
  1986. xpRemaining = currentLevel ? currentLevel * 1e3 - currentPercent * currentLevel * 1e3 : 0,
  1987. projectedHr = xpHr > 0 ? sigma(getProperty(lastHrXP.slice(-5), "gained")) * 12 : 0,
  1988. lastHr = xpHr > 0 ? sigma(getProperty(lastHrXP, "gained")) : 0,
  1989. lastHrCompleted = xpHr > 0 ? xpHr : 0,
  1990. lastMin = xpMin > 0 ? sigma(getProperty(lastMinXP, "gained")) : 0,
  1991. lastMinCompleted = xpMin > 0 ? xpMin : 0,
  1992. lastMinTotal = 6e4 / updateTimeXP,
  1993. last12sec = xpMin > 0 ? lastMinXP[lastMinXP.length - 1].gained : 0,
  1994. xBar = xpHr > 0 ? mean(getProperty(lastHrXP, "gained")) : 0,
  1995. xTilde = xpHr > 0 ? median(getProperty(lastHrXP, "gained")) : 0,
  1996. standardDev = xpHr > 0 ? standardDeviation(getProperty(lastHrXP, "gained")) : 0,
  1997. outliers = xpHr > 0 ? checkOutliers(getProperty(lastHrXP, "gained")) : 0,
  1998. sessionXP = currentXP && accounts[currentUser] ? currentXP - accounts[currentUser].xp : 0,
  1999. sessionLength = msToTime(Date.now() - scriptStartXP),
  2000. lifetimeXP = currentXP ? Math.round(currentXP) : 0,
  2001. updateTime = updateTimeXP;
  2002. // document.getElementById('stats-info').innerHTML = `<div><p class="stats-label">Lvl Completed:</p><span>${lvlCompleted.toLocaleString('en-US')}</span></div><div><p class="stats-label">Remaining:</p><span>${xpRemaining.toLocaleString('en-US')}</span></div><div><p class="stats-label">Projected (hr):</p><span>${projectedHr.toLocaleString('en-US')}</span></div><div><p class="stats-label">Last Hour:</p><span>${lastHr.toLocaleString('en-US')}</span><span class="stats-completed">(${lastHrCompleted}/60)</span></div><div><p class="stats-label">Last Minute:</p><span>${lastMin.toLocaleString('en-US')}</span><span class="stats-completed">(${lastMinCompleted + "/" + lastMinTotal})</span></div><div><p class="stats-label">Last 12s:</p><span>${last12sec.toLocaleString('en-US')}</span></div><div><p class="stats-label">Minute Mean:</p><span>${xBar.toLocaleString('en-US')}</span></div><div><p class="stats-label">Minute Median:</p><span>${xTilde.toLocaleString('en-US')}</span></div><div><p class="stats-label">Minute Sd:</p><span>${standardDev.toLocaleString('en-US')}</span></div><div><p class="stats-label" style="display:none">Latest Outliers:</p><span style="display:none">${outliers}</span></div><div><p class="stats-label">Session XP:</p><span>${sessionXP.toLocaleString('en-US')}</span></div><div><p class="stats-label">Session Length:</p><span id="stats-sesh-length">${sessionLength}</span></div><div><p class="stats-label">Lifetime XP:</p><span>${lifetimeXP.toLocaleString('en-US')}</span></div>`;
  2003. document.getElementById('stats-table').innerHTML = `<tbody><tr id="stats-lvlcomp"><td><label><input type="checkbox">Lvl Completed:</label></td><td>${lvlCompleted.toLocaleString('en-US')}</td></tr><tr id="stats-rem"><td><label><input type="checkbox">Remaining:</label></td><td>${xpStatsInPercentages ? round(convertToPerc(xpRemaining, currentLevel) * 100, 2) + '%' : round(xpRemaining).toLocaleString('en-US')}</td></tr><tr id="stats-projhr"><td><label><input type="checkbox">Projected (hr):</label></td><td>${xpStatsInPercentages ? round(convertToPerc(projectedHr, currentLevel) * 100, 2) + '%' : round(projectedHr).toLocaleString('en-US')}</td></tr><tr id="stats-lasthr"><td><label><input type="checkbox">Last Hour:</label></td><td>${xpStatsInPercentages ? round(convertToPerc(lastHr, currentLevel) * 100, 2) + '%' : round(lastHr).toLocaleString('en-US')}<span class="stats-completed">(${lastHrCompleted}/60)</span></td></tr><tr id="stats-lastmin"><td><label><input type="checkbox">Last Minute:</label></td><td>${xpStatsInPercentages ? round(convertToPerc(lastMin, currentLevel) * 100, 2) + '%' : round(lastMin).toLocaleString('en-US')}<span class="stats-completed">(${lastMinCompleted + "/" + lastMinTotal})</span></td></tr><tr id="stats-lastsec"><td><label><input type="checkbox">Last 12s:</label></td><td>${xpStatsInPercentages ? round(convertToPerc(last12sec, currentLevel) * 100, 2) + '%' : round(last12sec).toLocaleString('en-US')}</td></tr><tr id="stats-mean"><td><label><input type="checkbox">Minute Mean:</label></td><td>${xpStatsInPercentages ? round(convertToPerc(xBar, currentLevel) * 100, 2) + '%' : round(xBar).toLocaleString('en-US')}</td></tr><tr id="stats-median"><td><label><input type="checkbox">Minute Median:</label></td><td>${xpStatsInPercentages ? round(convertToPerc(xTilde, currentLevel) * 100, 2) + '%' : round(xTilde).toLocaleString('en-US')}</td></tr><tr id="stats-sd"><td><label><input type="checkbox">Minute Sd:</label></td><td>${xpStatsInPercentages ? round(convertToPerc(standardDev, currentLevel) * 100, 2) + '%' : round(standardDev).toLocaleString('en-US')}</td></tr><tr id="stats-sesh"><td><label><input type="checkbox">Session XP:</label></td><td>${xpStatsInPercentages ? round(currentLevel && accounts[currentUser] ? ((round(currentPercent, 3) + currentLevel) - accounts[currentUser].lvl) * 100 : 0, 2) + '%' : round(sessionXP).toLocaleString('en-US')}</td></tr><tr id="stats-seshlength"><td><label><input type="checkbox">Session Length:</label></td><td id="stats-sesh-length">${sessionLength}</td></tr><tr id="stats-lifetime"><td><label><input type="checkbox">Lifetime XP:</label></td><td>${lifetimeXP.toLocaleString('en-US')}</td></tr></tbody>`;
  2004. for(let i in misc_settings.statsSettings.xp){
  2005. misc_settings.statsSettings.xp[i] ? $('#stats-' + i).show() : $('#stats-' + i).hide();
  2006. $('#stats-' + i + ' input[type="checkbox"').prop("checked", misc_settings.statsSettings.xp[i]);
  2007. }
  2008. $('#stats-extra-info').text(`XP Stats - Updating Every ${updateTime / 1e3}s`).css('color', '#00bbff');
  2009. $('#stats-sesh-length').text(msToTime(Date.now() - scriptStartXP));
  2010. $('#stats-perc-btn').show();
  2011. } else {
  2012. const coinsHr = lastHrCoins.length,
  2013. coinsMin = lastMinCoins.length,
  2014. coinGoalCompleted = currentCoins ? currentCoins.toLocaleString('en-US') + "/" + Math.ceil(currentCoins / 25e4).toLocaleString('en-US') * 25e4 : 0,
  2015. coinsRemaining = currentCoins ? 25e4 - currentCoins % 25e4 : 0,
  2016. projectedHr = coinsHr > 5 ? Math.round(sigma(getProperty(lastHrCoins.slice(-5), "gained")) * 12) : 0,
  2017. lastHr = coinsHr > 0 ? Math.round(sigma(getProperty(lastHrCoins, "gained"))) : 0,
  2018. lastHrCompleted = coinsHr > 0 ? coinsHr : 0,
  2019. lastMin = coinsMin > 0 ? Math.round(sigma(getProperty(lastMinCoins, "gained"))) : 0,
  2020. lastMinCompleted = coinsMin > 0 ? coinsMin : 0,
  2021. lastMinTotal = 6e4 / updateTimeCoins,
  2022. last12sec = coinsMin > 0 ? getProperty(lastHrCoins.slice(-5), "amount")[0] : 0,
  2023. xBar = coinsHr > 0 ? Math.round(mean(getProperty(lastHrCoins, "gained"))) : 0,
  2024. xTilde = coinsHr > 0 ? Math.round(median(getProperty(lastHrCoins, "gained"))) : 0,
  2025. standardDev = coinsHr > 0 ? Math.round(standardDeviation(getProperty(lastHrCoins, "gained"))) : 0,
  2026. outliers = coinsHr > 0 ? checkOutliers(getProperty(lastHrCoins, "gained")) : 0,
  2027. sessionXP = currentCoins && accounts[currentUser] ? Math.round(currentCoins - accounts[currentUser].coins): 0,
  2028. sessionLength = msToTime(Date.now() - scriptStartCoins),
  2029. updateTime = updateTimeCoins;
  2030. // document.getElementById('stats-info').innerHTML = `<div style="display:none"><p class="stats-label">Coins Completed:</p><span>${coinGoalCompleted}</span></div><div><p class="stats-label">Remaining:</p><span>${coinsRemaining.toLocaleString('en-US')}</span></div><div><p class="stats-label">Projected (hr):</p><span>${projectedHr.toLocaleString('en-US')}</span></div><div><p class="stats-label">Last Hour:</p><span>${lastHr.toLocaleString('en-US')}</span><span class="stats-completed">(${lastHrCompleted}/60)</span></div><div><p class="stats-label">Last Minute:</p><span>${lastMin.toLocaleString('en-US')}</span><span class="stats-completed">(${lastMinCompleted + "/" + lastMinTotal})</span></div><div style="display:none"><p class="stats-label">Last 12s:</p><span></span></div><div><p class="stats-label">Minute Mean:</p><span>${xBar.toLocaleString('en-US')}</span></div><div><p class="stats-label">Minute Median:</p><span>${xTilde.toLocaleString('en-US')}</span></div><div><p class="stats-label">Minute Sd:</p><span>${standardDev.toLocaleString('en-US')}</span></div><div><p class="stats-label" style="display:none">Latest Outliers:</p><span style="display:none">${outliers}</span></div><div><p class="stats-label">Session Coins:</p><span>${sessionXP.toLocaleString('en-US')}</span></div><div><p class="stats-label">Session Length:</p><span id="stats-sesh-length">${sessionLength}</span></div>`;
  2031. document.getElementById('stats-table').innerHTML = `<tbody><tr id="stats-rem"><td><label><input type="checkbox">Remaining:</label></td><td>${coinsRemaining.toLocaleString('en-US')}</td></tr><tr id="stats-projhr"><td><label><input type="checkbox">Projected (hr):</label></td><td>${projectedHr.toLocaleString('en-US')}</td></tr><tr id="stats-lasthr"><td><label><input type="checkbox">Last Hour:</label></td><td>${lastHr.toLocaleString('en-US')}<span class="stats-completed">(${lastHrCompleted}/60)</span></td></tr><tr id="stats-lastmin"><td><label><input type="checkbox">Last Minute:</label></td><td>${lastMin.toLocaleString('en-US')}<span class="stats-completed">(${lastMinCompleted + "/" + lastMinTotal})</span></td></tr><tr id="stats-mean"><td><label><input type="checkbox">Minute Mean:</label></td><td>${xBar.toLocaleString('en-US')}</td></tr><tr id="stats-median"><td><label><input type="checkbox">Minute Median:</label></td><td>${xTilde.toLocaleString('en-US')}</td></tr><tr id="stats-sd"><td><label><input type="checkbox">Minute Sd:</label></td><td>${standardDev.toLocaleString('en-US')}</td></tr><tr id="stats-sesh"><td><label><input type="checkbox">Session Coins:</label></td><td>${sessionXP.toLocaleString('en-US')}</td></tr><tr id="stats-seshlength"><td><label><input type="checkbox">Session Length:</label></td><td id="stats-sesh-length">${sessionLength}</td></tr></tbody>`;
  2032. for(let i in misc_settings.statsSettings.coins){
  2033. misc_settings.statsSettings.coins[i] ? $('#stats-' + i).show() : $('#stats-' + i).hide();
  2034. $('#stats-' + i + ' input[type="checkbox"').prop("checked", misc_settings.statsSettings.coins[i]);
  2035. }
  2036. $('#stats-extra-info').text(`Coin Stats - Updating Every ${updateTime / 1e3}s`).css('color', '#ffc800');
  2037. $('#stats-sesh-length').text(msToTime(Date.now() - scriptStartCoins));
  2038. $('#stats-perc-btn').hide();
  2039. }
  2040. }
  2041.  
  2042. $('.progress-bar').eq(1).parent()[0].style.cursor = "pointer";
  2043.  
  2044. if(coinXPstats){
  2045. $('.progress-bar').eq(1).parent().on("click", () => {
  2046. const statsCont = $('#stats-container');
  2047. if(statsCont[0].style.display == "none") statsCont.fadeIn(400);
  2048. else if(!coinsHTMLactive) statsCont.fadeOut(400);
  2049. coinsHTMLactive = false;
  2050. updateUI();
  2051. }), [$(".dash-coin.dcTopBar").eq(0), $("#coinsTopLeft"), $(".progress-bar-coins").eq(1)].forEach(el => {
  2052. el.on('click', (e) => {
  2053. const statsCont = $('#stats-container');
  2054. if(statsCont[0].style.display == "none") statsCont.fadeIn(400);
  2055. else if(coinsHTMLactive) statsCont.fadeOut(400);
  2056. coinsHTMLactive = true;
  2057. updateUI();
  2058. e.stopImmediatePropagation();
  2059. });
  2060. });
  2061. }
  2062.  
  2063. // copy chat msgs
  2064. if(rightClickCopyChat){
  2065. $('#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>`)
  2066. $('#contextCopyChat').on('click', () => {
  2067. let arr = chatmsgs, str = "";
  2068. if(arr != null){
  2069. for(let i of arr.reverse()) str += ((new Date(i.time).toLocaleTimeString()) + " " + i.name + ': ' + i.message + "\n");
  2070. navigator.clipboard.writeText(str).then(function() {
  2071. curserMsg('Chat messages were successfully added to clipboard.', 'green');
  2072. }, function() {
  2073. curserMsg('Something went wrong. Nothing was added to your clipboard.', 'red');
  2074. });
  2075. }
  2076. $('#contextMenu').hide();
  2077. });
  2078. }
  2079.  
  2080. let quickBuying = false;
  2081. $('#invCloak').after(`<div class="inventory-box" id="fsfb-quickMega" style="background-image: url(../img/store/megaphone_shout.png); display: none;"></div>`);
  2082. $('#invCloak').after(`<div class="inventory-box" id="fsfb-minionNuker" style="background-image: url(../img/store/minion_nuker.png); display: none;"></div>`);
  2083. $(inventorySingleRow ? '#fsfb-quickMega' : '#inv360Shot').after(`<div class="inventory-box" id="fsfb-quickbuy" style="display: none;"><img id="fsfb-quickbuy-img" height="80%" src="https://i.imgur.com/tAjoEyU.png"></div>`);
  2084. $('#fsfb-quickbuy').on('click', function(){
  2085. quickBuying = !quickBuying;
  2086. if (quickBuying){
  2087. $(this).addClass('activatedInv');
  2088. curserMsg('Quick buy activated, click the powerup you would like to buy.', 'green');
  2089. $('.inventory-box').addClass('fsfb-shown').find('p').hide();
  2090. $('#invCloak').removeClass('fsfb-shown');
  2091. $('#fsfb-quickbuy').removeClass('fsfb-shown');
  2092. } else {
  2093. $(this).removeClass('activatedInv');
  2094. curserMsg('Quick buy deactivated.', 'red');
  2095. $('.inventory-box').removeClass('fsfb-shown').find('p').show();
  2096. }
  2097. });
  2098.  
  2099. // add linesplit bubbles
  2100. $('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
  2101. // add class to all elements that need to behidden
  2102. setTimeout(() => $('#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').addClass("hideUI"), 4e3);
  2103.  
  2104. // put inventory onto a single line
  2105. if(inventorySingleRow){
  2106. for (let i = 1; i < 3; i++) $("#inventory" + i).addClass("fsfb-inventories").css("order", i);
  2107. }
  2108.  
  2109. const addFriendDecline = () => {
  2110. if(!friendDeclineAll || currentUser == 'Please Login First') return;
  2111. $('#friendAcceptAll').text('Reject All').addClass('fsfb-temp').clone().insertAfter($('#friendAcceptAll')).attr('style', 'right: 93px;').text('Accept All').removeClass('fsfb-temp');
  2112. $('.fsfb-temp').attr('id', 'friendRejectAll').removeAttr('onclick').removeClass('fsfb-temp');
  2113. $('#friendRejectAll').on('click', () => {
  2114. $('#requestList>.friend>.btn-friends.remove').each(function(){ $(this)[0].click() })
  2115. });
  2116. }
  2117.  
  2118. if(friendDeclineAll){ // $('#friendAcceptAll').length
  2119. $('#btnFriends').on('click', async() => {
  2120. await waitUntil(() => $('#friendAcceptAll').length > 0);
  2121. if($('#friendAcceptAll').length == 0) return;
  2122. await sleep(50);
  2123. addFriendDecline();
  2124. })
  2125. }
  2126.  
  2127. let pushFn = Array.prototype.push,
  2128. spliceFn = Array.prototype.splice,
  2129. prop = null,
  2130. customCells = !0,
  2131. specialCells = !0,
  2132. customDc = false,
  2133. cellProto,
  2134. avgFps = 0,
  2135. fpsArr = [],
  2136. svSwitch = false,
  2137. entArr = null,
  2138. chatmsgs;
  2139.  
  2140. let r1Portal = {
  2141. portal: null,
  2142. lastMass: 0,
  2143. lastMassChange: 0,
  2144. lastValue: 0,
  2145. room: 1
  2146. }, r2Portal = {
  2147. portal: null,
  2148. lastMass: 0,
  2149. lastMassChange: 0,
  2150. lastValue: 0,
  2151. room: 2
  2152. };
  2153.  
  2154. let svInfo = {
  2155. "default": {
  2156. ejPortalMass: 12,
  2157. r1Id: 1,
  2158. r2Id: 7,
  2159. r3Id: null,
  2160. r1StartMass: 500,
  2161. r2StartMass: 500,
  2162. entities: []
  2163. },
  2164. 1: { // POPSPLIT
  2165. entities: [
  2166. // A1
  2167. {type: 1, x: 2500, y: 2500, size: 45},
  2168. // r1
  2169. {type: 0, x: 3400, y: 15300, size: 195},
  2170. {type: 1, x: 2500, y: 15000, size: 45},
  2171. {type: 2, x: 3000, y: 14500, size: 35},
  2172. {type: 3, x: 2700, y: 14600, size: 29},
  2173. // r2
  2174. {type: 0, x: 12000, y: 15500, size: 195},
  2175. {type: 1, x: 11000, y: 15000, size: 29},
  2176. {type: 2, x: 12600, y: 14900, size: 35},
  2177. {type: 2, x: 12000, y: 15200, size: 35},
  2178. {type: 2, x: 11300, y: 14900, size: 35},
  2179. {type: 3, x: 12800, y: 14500, size: 29}
  2180. ]
  2181. },
  2182. 2: { // SLOWSPLIT
  2183. ejPortalMass: 20,
  2184. r1Id: 1,
  2185. r2Id: 14, // rightmost portal
  2186. r3Id: 6,
  2187. r1StartMass: 500,
  2188. r2StartMass: 500,
  2189. entities: [
  2190. // A1
  2191. {type: 1, x: 2500, y: 2500, size: 45},
  2192. // r1
  2193. {type: 0, x: 7400, y: 21300, size: 195},
  2194. {type: 1, x: 3500, y: 21000, size: 45},
  2195. {type: 2, x: 5000, y: 20500, size: 35},
  2196. {type: 3, x: 3700, y: 20600, size: 29},
  2197. // r2
  2198. {type: 0, x: 14000, y: 22000, size: 195},
  2199. {type: 1, x: 11000, y: 21000, size: 29},
  2200. {type: 2, x: 11300, y: 20900, size: 35},
  2201. {type: 2, x: 12000, y: 21200, size: 35},
  2202. {type: 2, x: 12600, y: 20900, size: 35},
  2203. {type: 3, x: 12800, y: 20500, size: 29},
  2204. // r2 (2nd?)
  2205. {type: 0, x: 22000, y: 21500, size: 195},
  2206. {type: 2, x: 21300, y: 21000, size: 45}
  2207. ]
  2208. },
  2209. 4: { // FASTSPLIT
  2210. ejPortalMass: 20,
  2211. r1Id: 1,
  2212. r2Id: 14, // rightmost portal
  2213. r3Id: 6,
  2214. r1StartMass: 500,
  2215. r2StartMass: 500,
  2216. entities: [
  2217. // A1
  2218. {type: 1, x: 2500, y: 2500, size: 45},
  2219. // r1
  2220. {type: 0, x: 7400, y: 21300, size: 195},
  2221. {type: 1, x: 3500, y: 21000, size: 45},
  2222. {type: 2, x: 5000, y: 20500, size: 35},
  2223. {type: 3, x: 3700, y: 20600, size: 29},
  2224. // r2
  2225. {type: 0, x: 14000, y: 22000, size: 195},
  2226. {type: 1, x: 11000, y: 21000, size: 29},
  2227. {type: 2, x: 11300, y: 20900, size: 35},
  2228. {type: 2, x: 12000, y: 21200, size: 35},
  2229. {type: 2, x: 12600, y: 20900, size: 35},
  2230. {type: 3, x: 12800, y: 20500, size: 29},
  2231. // r2 (2nd?)
  2232. {type: 0, x: 22000, y: 21500, size: 195},
  2233. {type: 2, x: 21300, y: 21000, size: 35}
  2234. ]
  2235. },
  2236. 5: { // SPLITRUN
  2237. entities: [
  2238. // A1
  2239. {type: 1, x: 2500, y: 2500, size: 45},
  2240. // r1
  2241. {type: 0, x: 3400, y: 15300, size: 195},
  2242. {type: 1, x: 2500, y: 15000, size: 45},
  2243. {type: 2, x: 3000, y: 14500, size: 35},
  2244. {type: 3, x: 2700, y: 14600, size: 29},
  2245. // r2
  2246. {type: 0, x: 12000, y: 15500, size: 195},
  2247. {type: 1, x: 11000, y: 15000, size: 29},
  2248. {type: 2, x: 12600, y: 14900, size: 35},
  2249. {type: 2, x: 11300, y: 14900, size: 35},
  2250. {type: 2, x: 12000, y: 15200, size: 35},
  2251. {type: 3, x: 12800, y: 14500, size: 29}
  2252. ]
  2253. },
  2254. 6: { // XINSTA
  2255. ejPortalMass: 12,
  2256. r1Id: 1,
  2257. r2Id: 6,
  2258. r1StartMass: 500,
  2259. r2StartMass: 500,
  2260. entities: [
  2261. // A1
  2262. {type: 1, x: 2500, y: 2500, size: 45},
  2263. // r1
  2264. {type: 0, x: 3400, y: 26300, size: 195},
  2265. {type: 1, x: 2500, y: 26000, size: 45},
  2266. {type: 2, x: 3000, y: 25500, size: 45},
  2267. {type: 3, x: 2700, y: 25600, size: 29},
  2268. // r2
  2269. {type: 0, x: 15000, y: 26500, size: 195},
  2270. {type: 1, x: 14000, y: 26000, size: 29},
  2271. {type: 2, x: 14300, y: 25900, size: 45},
  2272. {type: 2, x: 15000, y: 26200, size: 45},
  2273. {type: 2, x: 15600, y: 25900, size: 45},
  2274. {type: 3, x: 15800, y: 25500, size: 29}
  2275. ]
  2276. },
  2277. 7: { // XY
  2278. entities: [
  2279. // A1
  2280. {type: 1, x: 2500, y: 2500, size: 45},
  2281. // r1
  2282. {type: 0, x: 3400, y: 15300, size: 195},
  2283. {type: 1, x: 2500, y: 15000, size: 45},
  2284. {type: 2, x: 3000, y: 14500, size: 35},
  2285. {type: 3, x: 2700, y: 14600, size: 29},
  2286. // r2
  2287. {type: 0, x: 12000, y: 15500, size: 195},
  2288. {type: 1, x: 11000, y: 15000, size: 29},
  2289. {type: 2, x: 11300, y: 14900, size: 35},
  2290. {type: 2, x: 12000, y: 15200, size: 35},
  2291. {type: 2, x: 12600, y: 14900, size: 35},
  2292. {type: 3, x: 12800, y: 14500, size: 29}
  2293. ]
  2294. },
  2295. 8: { // INSTANT EU
  2296. entities: [
  2297. // A1
  2298. {type: 1, x: 2500, y: 2500, size: 45},
  2299. // r1
  2300. {type: 0, x: 3400, y: 15300, size: 195},
  2301. {type: 1, x: 2500, y: 15000, size: 45},
  2302. {type: 2, x: 3000, y: 14500, size: 35},
  2303. {type: 3, x: 2700, y: 14600, size: 29},
  2304. // r2
  2305. {type: 0, x: 12000, y: 15500, size: 195},
  2306. {type: 1, x: 11000, y: 15000, size: 29},
  2307. {type: 2, x: 11300, y: 14900, size: 35},
  2308. {type: 2, x: 12000, y: 15200, size: 35},
  2309. {type: 2, x: 12600, y: 14900, size: 35},
  2310. {type: 3, x: 12800, y: 14500, size: 29}
  2311. ]
  2312. },
  2313. 9: { // CR EU
  2314. entities: [
  2315. // A1
  2316. {type: 1, x: 2500, y: 2500, size: 45},
  2317. // r1
  2318. {type: 0, x: 3400, y: 27300, size: 195},
  2319. {type: 1, x: 2500, y: 27000, size: 45},
  2320. {type: 2, x: 3000, y: 26500, size: 35},
  2321. {type: 3, x: 2700, y: 26600, size: 29},
  2322. // r2
  2323. {type: 0, x: 12000, y: 27500, size: 195},
  2324. {type: 1, x: 11000, y: 27000, size: 29},
  2325. {type: 2, x: 11300, y: 26900, size: 35},
  2326. {type: 2, x: 12000, y: 27200, size: 35},
  2327. {type: 2, x: 12600, y: 26900, size: 35},
  2328. {type: 3, x: 12800, y: 26500, size: 29}
  2329. ]
  2330. },
  2331. 11: { // GIGANTIC 1
  2332. entities: [
  2333. {type: 1, x: 2500, y: 2500, size: 45},
  2334. {type: 3, x: 5000, y: 33000, size: 45},
  2335. {type: 1, x: 18000, y: 18000, size: 45},
  2336. {type: 3, x: 24000, y: 12000, size: 45}
  2337. ]
  2338. },
  2339. 12: { // GIANT NA
  2340. entities: [
  2341. {type: 1, x: 2500, y: 2500, size: 45},
  2342. {type: 3, x: 5000, y: 33000, size: 45},
  2343. {type: 1, x: 18000, y: 18000, size: 45},
  2344. {type: 3, x: 24000, y: 12000, size: 45}
  2345. ]
  2346. },
  2347. 13: { // SS EU
  2348. ejPortalMass: 13.5,
  2349. r1Id: 12, //Lower room
  2350. r2Id: 11,
  2351. r1StartMass: 500,
  2352. r2StartMass: 500,
  2353. entities: [
  2354. // r1
  2355. {type: 0, x: 1500, y: 27500, size: 195},
  2356. {type: 1, x: 14000, y: 32000, size: 45},
  2357. {type: 3, x: 13200, y: 33500, size: 45},
  2358. {type: 4, x: 12500, y: 32200, size: 142},
  2359. {type: 4, x: 14500, y: 32200, size: 142},
  2360. // r2
  2361. {type: 0, x: 16000, y: 33500, size: 195},
  2362. {type: 1, x: 500, y: 24000, size: 45},
  2363. {type: 3, x: 2500, y: 24000, size: 45},
  2364. {type: 4, x: 900, y: 22000, size: 142},
  2365. {type: 4, x: 500, y: 15000, size: 224},
  2366. // r3 (?)
  2367. {type: 4, x: 1200, y: 5500, size: 224},
  2368. {type: 4, x: 2000, y: 3000, size: 224},
  2369. {type: 4, x: 4000, y: 6000, size: 224},
  2370. {type: 4, x: 4500, y: 3000, size: 224},
  2371. // on map
  2372. {type: 4, x: 8000, y: 3500, size: 224},
  2373. {type: 4, x: 10000, y: 4500, size: 224},
  2374. {type: 4, x: 16000, y: 3000, size: 224},
  2375. {type: 4, x: 15400, y: 2400, size: 224},
  2376. {type: 4, x: 7500, y: 19000, size: 224},
  2377. {type: 4, x: 19200, y: 14000, size: 224},
  2378. {type: 4, x: 24400, y: 24000, size: 224},
  2379. {type: 4, x: 17500, y: 14000, size: 142},
  2380. {type: 4, x: 30000, y: 16000, size: 142}
  2381. ]
  2382. },
  2383. 14: { // SS NA
  2384. ejPortalMass: 13.5,
  2385. r1Id: 12, //Lower room
  2386. r2Id: 11,
  2387. r1StartMass: 500,
  2388. r2StartMass: 500,
  2389. entities: [
  2390. // r1
  2391. {type: 0, x: 1500, y: 27500, size: 195},
  2392. {type: 1, x: 14000, y: 32000, size: 45},
  2393. {type: 3, x: 13200, y: 33500, size: 45},
  2394. {type: 4, x: 12500, y: 32200, size: 142},
  2395. {type: 4, x: 14500, y: 32200, size: 142},
  2396. // r2
  2397. {type: 0, x: 16000, y: 33500, size: 195},
  2398. {type: 1, x: 500, y: 24000, size: 45},
  2399. {type: 3, x: 2500, y: 24000, size: 45},
  2400. {type: 4, x: 900, y: 22000, size: 142},
  2401. {type: 4, x: 500, y: 15000, size: 224},
  2402. // r3 (?)
  2403. {type: 4, x: 1200, y: 5500, size: 224},
  2404. {type: 4, x: 2000, y: 3000, size: 224},
  2405. {type: 4, x: 4000, y: 6000, size: 224},
  2406. {type: 4, x: 4500, y: 3000, size: 224},
  2407. // on map
  2408. {type: 4, x: 8000, y: 3500, size: 224},
  2409. {type: 4, x: 10000, y: 4500, size: 224},
  2410. {type: 4, x: 16000, y: 3000, size: 224},
  2411. {type: 4, x: 15400, y: 2400, size: 224},
  2412. {type: 4, x: 7500, y: 19000, size: 224},
  2413. {type: 4, x: 19200, y: 14000, size: 224},
  2414. {type: 4, x: 24400, y: 24000, size: 224},
  2415. {type: 4, x: 17500, y: 14000, size: 142},
  2416. {type: 4, x: 30000, y: 16000, size: 142}
  2417. ]
  2418. },
  2419. 17: { // CR AS
  2420. entities: [
  2421. // A1
  2422. {type: 1, x: 2500, y: 2500, size: 45},
  2423. // r1
  2424. {type: 0, x: 3400, y: 27300, size: 195},
  2425. {type: 1, x: 2500, y: 27000, size: 45},
  2426. {type: 2, x: 3000, y: 26500, size: 35},
  2427. {type: 3, x: 2700, y: 26600, size: 29},
  2428. // r2
  2429. {type: 0, x: 12000, y: 27500, size: 195},
  2430. {type: 1, x: 11000, y: 27000, size: 29},
  2431. {type: 2, x: 11300, y: 26900, size: 35},
  2432. {type: 2, x: 12000, y: 27200, size: 35},
  2433. {type: 2, x: 12600, y: 26900, size: 35},
  2434. {type: 3, x: 12800, y: 26500, size: 29}
  2435. ]
  2436. },
  2437. 18: { // GIGA 1
  2438. entities: [
  2439. {type: 1, x: 2500, y: 2500, size: 45},
  2440. {type: 3, x: 5000, y: 33000, size: 45},
  2441. {type: 1, x: 18000, y: 18000, size: 45},
  2442. {type: 3, x: 24000, y: 12000, size: 45}
  2443. ]
  2444. },
  2445. 19: { // GIGANTIC 2
  2446. entities: [
  2447. {type: 1, x: 2500, y: 2500, size: 45},
  2448. {type: 3, x: 5000, y: 33000, size: 45},
  2449. {type: 1, x: 18000, y: 18000, size: 45},
  2450. {type: 3, x: 24000, y: 12000, size: 45}
  2451. ]
  2452. },
  2453. 20: { // CR NA
  2454. entities: [
  2455. // A1
  2456. {type: 1, x: 2500, y: 2500, size: 45},
  2457. // r1
  2458. {type: 0, x: 3400, y: 27300, size: 195},
  2459. {type: 1, x: 2500, y: 27000, size: 45},
  2460. {type: 2, x: 3000, y: 26500, size: 35},
  2461. {type: 3, x: 2700, y: 26600, size: 29},
  2462. // r2
  2463. {type: 0, x: 12000, y: 27500, size: 195},
  2464. {type: 1, x: 11000, y: 27000, size: 29},
  2465. {type: 2, x: 11300, y: 26900, size: 35},
  2466. {type: 2, x: 12000, y: 27200, size: 35},
  2467. {type: 2, x: 12600, y: 26900, size: 35},
  2468. {type: 3, x: 12800, y: 26500, size: 29}
  2469. ]
  2470. },
  2471. 23: { // GIGANTIC 3
  2472. entities: [
  2473. {type: 1, x: 2500, y: 2500, size: 45},
  2474. {type: 3, x: 5000, y: 33000, size: 45},
  2475. {type: 1, x: 18000, y: 18000, size: 45},
  2476. {type: 3, x: 24000, y: 12000, size: 45}
  2477. ]
  2478. },
  2479. 24: { // GIGANTIC 4
  2480. entities: [
  2481. {type: 1, x: 2500, y: 2500, size: 45},
  2482. {type: 3, x: 5000, y: 33000, size: 45},
  2483. {type: 1, x: 18000, y: 18000, size: 45},
  2484. {type: 3, x: 24000, y: 12000, size: 45}
  2485. ]
  2486. },
  2487. 25: { // GIANT 2 NA
  2488. entities: [
  2489. {type: 1, x: 2500, y: 2500, size: 45},
  2490. {type: 3, x: 5000, y: 33000, size: 45},
  2491. {type: 1, x: 18000, y: 18000, size: 45},
  2492. {type: 3, x: 24000, y: 12000, size: 45}
  2493. ]
  2494. },
  2495. 26: { // GIGA 2
  2496. entities: [
  2497. {type: 1, x: 2500, y: 2500, size: 45},
  2498. {type: 3, x: 5000, y: 33000, size: 45},
  2499. {type: 1, x: 18000, y: 18000, size: 45},
  2500. {type: 3, x: 24000, y: 12000, size: 45}
  2501. ]
  2502. },
  2503. 38: { // Solo Agf
  2504. ejPortalMass: 12,
  2505. r1Id: 1,
  2506. r2Id: 6,
  2507. r1StartMass: 500,
  2508. r2StartMass: 400,
  2509. entities: [
  2510. // A1
  2511. {type: 1, x: 2500, y: 2500, size: 45},
  2512. // room 1
  2513. {type: 0, x: 3500, y: 11500, size: 195},
  2514. {type: 1, x: 2500, y: 11500, size: 45},
  2515. {type: 2, x: 3000, y: 11000, size: 35},
  2516. {type: 3, x: 2700, y: 11100, size: 29},
  2517. // room 2
  2518. {type: 0, x: 9000, y: 11800, size: 180},
  2519. {type: 1, x: 8200, y: 10500, size: 29},
  2520. {type: 2, x: 8300, y: 10900, size: 35},
  2521. {type: 2, x: 9000, y: 11200, size: 35},
  2522. {type: 2, x: 9600, y: 10900, size: 35},
  2523. {type: 3, x: 9800, y: 10500, size: 29}
  2524. ]
  2525. },
  2526. 39: { // MEGASPLIT AS
  2527. entities: [
  2528. ]
  2529. },
  2530. 42: { // GIANT 3 NA
  2531. entities: [
  2532. {type: 1, x: 2500, y: 2500, size: 45},
  2533. {type: 3, x: 5000, y: 33000, size: 45},
  2534. {type: 1, x: 18000, y: 18000, size: 45},
  2535. {type: 3, x: 24000, y: 12000, size: 45}
  2536. ]
  2537. },
  2538. 43: { // Instant AS
  2539. ejPortalMass: 11, //might have to reevaluate
  2540. r1Id: 1,
  2541. r2Id: 13, // rightmost portal
  2542. r3Id: 7,
  2543. r1StartMass: 1004,
  2544. r2StartMass: 250,
  2545. r3StartMass: 1004,
  2546. entities: [
  2547. // A1
  2548. {type: 1, x: 2500, y: 2500, size: 45},
  2549. // r1
  2550. {type: 0, x: 3400, y: 20300, size: 275},
  2551. {type: 1, x: 2500, y: 20000, size: 45},
  2552. {type: 2, x: 3000, y: 19500, size: 35},
  2553. {type: 3, x: 2700, y: 19600, size: 29},
  2554. // r2
  2555. {type: 0, x: 13000, y: 21000, size: 150},
  2556. {type: 1, x: 11000, y: 20000, size: 45},
  2557. {type: 2, x: 11300, y: 19900, size: 35},
  2558. {type: 2, x: 12000, y: 20200, size: 35},
  2559. {type: 2, x: 12600, y: 19900, size: 35},
  2560. {type: 3, x: 12800, y: 19500, size: 45},
  2561. // r2 (2nd?)
  2562. {type: 0, x: 30000, y: 21000, size: 275},
  2563. {type: 1, x: 28000, y: 19900, size: 55},
  2564. {type: 2, x: 28300, y: 19900, size: 45},
  2565. {type: 2, x: 28450, y: 20200, size: 45},
  2566. {type: 2, x: 28600, y: 19900, size: 45},
  2567. {type: 3, x: 28900, y: 19900, size: 55}
  2568. ]
  2569. }
  2570. };
  2571. 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];
  2572. let currentServerId = 0;
  2573.  
  2574. try{
  2575. for(let i of JSON.parse(localStorage.gameservers)){
  2576. if(i.isCurrent) currentServerId = i.id;
  2577. }
  2578. } catch {};
  2579.  
  2580. setTimeout(() => {
  2581. svSwitch = true;
  2582. }, 250);
  2583.  
  2584. let ss = unsafeWindow.setserver;
  2585. unsafeWindow.setserver = (sv, sn) => {
  2586. playerAlive = false;
  2587. currentServerId = parseInt($(".server-tabmenu").find(".active")[0].id.slice(9));
  2588. r1Portal.portal = null;
  2589. r2Portal.portal = null;
  2590. svSwitch = true;
  2591. ss(sv, sn);
  2592. }
  2593.  
  2594. function getServerValue(value) {
  2595. return (svInfo[currentServerId] && svInfo[currentServerId][value]) ? svInfo[currentServerId][value] : svInfo.default[value]
  2596. }
  2597.  
  2598. function createCell(posX, posY, type, nSize){
  2599. if(!cellProto) return null;
  2600.  
  2601. let color,
  2602. colorDimmed = "#FFFFFF",
  2603. size = 0,
  2604. imageId = 0,
  2605. spikes = null;
  2606.  
  2607. switch(type){
  2608. case 0:
  2609. color = "#622373";
  2610. colorDimmed = "#4e1c5c";
  2611. size = nSize ? nSize : 200;
  2612. imageId = 1;
  2613. break;
  2614. case 1:
  2615. color = "#ff0000";
  2616. colorDimmed = "#cc0001";
  2617. size = nSize ? nSize : 32;
  2618. spikes = {x: posX, y: posY, s: size, p: size};
  2619. imageId = 2;
  2620. break;
  2621. case 2:
  2622. color = "#76ff54";
  2623. colorDimmed = "#66b319";
  2624. size = nSize ? nSize : 35;
  2625. imageId = 3;
  2626. break;
  2627. case 3:
  2628. color = "#ffd000";
  2629. colorDimmed = "#ccb300";
  2630. size = nSize ? nSize : 32;
  2631. spikes = {x: posX, y: posY, s: size, p: size};
  2632. imageId = 4;
  2633. break;
  2634. case 4:
  2635. color = "#00a2e8";
  2636. colorDimmed = "#0081b9";
  2637. size = nSize ? nSize : 150;
  2638. imageId = 5;
  2639. break;
  2640. default:
  2641. color = "#FFFFFF";
  2642. size = 500;
  2643. };
  2644.  
  2645. let cell = new cellProto.constructor();
  2646. cell[prop[41]] = imageId;
  2647. cell[prop[19]] = null;
  2648. cell[prop[50]] = 0;
  2649. cell[prop[40]] = spikes ? 1 : 0;
  2650. cell[prop[26]] = null;
  2651. cell[prop[52]] = false;
  2652. cell[prop[53]] = false;
  2653. cell[prop[39]] = [];
  2654. cell[prop[57]] = 0;
  2655. cell[prop[45]] = false;
  2656. cell[prop[37]] = true;
  2657. cell[prop[24]] = null;
  2658. cell[prop[44]] = false;
  2659. cell[prop[56]] = false;
  2660. cell[prop[47]] = false;
  2661. cell[prop[25]] = null;
  2662. cell[prop[35]] = Date.now();
  2663. cell[prop[51]] = 0;
  2664. cell[prop[42]] = null;
  2665. cell.clanCache = null;
  2666. cell.clanPart = null;
  2667. cell.color = color;
  2668. cell[prop[46]] = 9;
  2669. cell[prop[38]] = spikes;
  2670. cell[prop[33]] = 1;
  2671. cell[prop[36]] = 0;
  2672. cell[prop[31]] = posX;
  2673. cell[prop[32]] = posY;
  2674. cell[prop[3]] = 0;
  2675. cell.id = 1e9;
  2676. cell[prop[21]] = null;
  2677. cell[prop[20]] = null;
  2678. cell.massCache = null;
  2679. cell[prop[11]] = size;
  2680. cell.nSize = size;
  2681. cell.name = null;
  2682. cell.namePart = null;
  2683. cell.nameSize = 0;
  2684. cell.oid = 0;
  2685. cell.ox = posX;
  2686. cell.oy = posY;
  2687. cell[prop[58]] = 0;
  2688. cell.rotation = 0;
  2689. cell.shape = 0;
  2690. cell.size = size;
  2691. cell.skinId = 0;
  2692. cell.strokeSize = size + 4;
  2693. cell.textDrawn = null;
  2694. cell.transform = null;
  2695. cell[prop[18]] = true;
  2696. cell[prop[2]] = colorDimmed ? colorDimmed : dimmColor(color);
  2697. cell[prop[17]] = null;
  2698. cell.x = posX;
  2699. cell[prop[23]] = null;
  2700. cell.y = posY;
  2701. cell[prop[22]] = null;
  2702. cell[prop[54]] = false;
  2703. cell[prop[34]] = Date.now();
  2704. cell[prop[55]] = true;
  2705. return cell;
  2706. }
  2707.  
  2708. function customize(c){
  2709. cellProto = c.__proto__;
  2710. // this should stay independent unless sora changes the structure of cell class
  2711. let methods = [];
  2712. let a = c;
  2713. while (a = Reflect.getPrototypeOf(a)) {
  2714. let keys = Reflect.ownKeys(a)
  2715. keys.forEach((k) => methods.push(k));
  2716. }
  2717. let dc_fn_name = methods[7];
  2718. let dc = c.__proto__[dc_fn_name];
  2719. c.__proto__.oldDrawCell = dc;
  2720. c.__proto__[dc_fn_name] = function(){
  2721. let cell = this,
  2722. cellType = cell[prop[46]];
  2723. if(cellType == 0){
  2724. if(cell.color == "#622373"){
  2725. cell.color = "#622374"; // give purple cells a slightly diff color to make them detectable in .stroke fn
  2726. }
  2727. if(settings.theme_boxes[1].active){
  2728. if(cell.oOwnCell === undefined) cell.oOwnCell = cell[prop[45]];
  2729. cell[prop[45]] = true;
  2730. }
  2731. if(settings.theme_boxes[6].active){
  2732. if(cell.oSpiked === undefined) cell.oSpiked = cell[prop[56]];
  2733. cell[prop[56]] = settings.theme_boxes[6].active;
  2734. } else {
  2735. if(cell.oSpiked != undefined) cell[prop[56]] = cell.oSpiked;
  2736. }
  2737. if(settings.theme_boxes[2].active){
  2738. if(cell.oHasImage === undefined) cell.oHasImage = cell[prop[18]];
  2739. cell[prop[18]] = (cell.oOwnCell === undefined ? cell[prop[45]] : cell.oOwnCell) ? cell[prop[18]] : false;
  2740. } else {
  2741. if(cell.oHasImage != undefined && !settings.theme_boxes[3].active) cell[prop[18]] = cell.oHasImage;
  2742. }
  2743. if(settings.theme_boxes[4].active){
  2744. if(cell.oName === undefined) cell.oName = cell.name;
  2745. cell.name = (cell.oOwnCell === undefined ? cell[prop[45]] : cell.oOwnCell) ? cell.name : "";
  2746. } else {
  2747. if(cell.oName != undefined && !settings.theme_boxes[3].active) cell.name = cell.oName;
  2748. }
  2749. if(settings.theme_boxes[3].active){
  2750. if(cell.oHasImage === undefined) cell.oHasImage = cell[prop[18]];
  2751. cell[prop[18]] = cell[prop[53]] ? cell[prop[18]] : false;
  2752. } else {
  2753. if(cell.oHasImage != undefined && !settings.theme_boxes[2].active) cell[prop[18]] = cell.oHasImage;
  2754. }
  2755. if(settings.theme_boxes[5].active){
  2756. if(cell.oName === undefined) cell.oName = cell.name;
  2757. cell.name = cell[prop[53]] ? cell.name : "";
  2758. } else {
  2759. if(cell.oName != undefined && !settings.theme_boxes[4].active) cell.name = cell.oName;
  2760. }
  2761. }
  2762. if(cellType == 1){
  2763. if(cell.color == "#00ff00") cell.color = "#00ff01";
  2764. if(settings.theme[0].active){
  2765. if(!cell.oColor) cell.oColor = cell.color;
  2766. cell.color = settings.theme[0].color;
  2767. } else {
  2768. if(cell.oColor) cell.color = cell.oColor;
  2769. }
  2770. }
  2771. return dc.apply(this, arguments);
  2772. }
  2773. customDc = true;
  2774. }
  2775.  
  2776. function dimmColor(color) {
  2777. var r = (~~(parseInt(color.substr(1, 2), 16) * 0.5)).toString(16),
  2778. g = (~~(parseInt(color.substr(3, 2), 16) * 0.5)).toString(16),
  2779. b = (~~(parseInt(color.substr(5, 2), 16) * 0.5)).toString(16);
  2780. if (r.length == 1) r = "0" + r;
  2781. if (g.length == 1) g = "0" + g;
  2782. if (b.length == 1) b = "0" + b;
  2783. return "#" + r + g + b
  2784. }
  2785.  
  2786. const _sort = Array.prototype.sort;
  2787. Array.prototype.sort = function(oldFunc) {
  2788. const originalArray = this,
  2789. newFunc = function(a, b) {
  2790. if(typeof a?.id != "number" || typeof b?.id != "number") return oldFunc.apply(this, arguments);
  2791. if(settings.theme_boxes[8].active){
  2792. const aVal = a[Object.keys(a)[46]], bVal = b[Object.keys(b)[46]];
  2793. return (oldFunc.apply(this, arguments) * (settings.theme_boxes[7].active ? -1 : 1) > 0 || aVal == 4) && bVal != 4 ? 1 : aVal == 4 && bVal == 4 ? 0 : -1
  2794. } else if(settings.theme_boxes[7].active){
  2795. return oldFunc.apply(this, arguments) * -1;
  2796. } else {
  2797. return oldFunc.apply(this, arguments);
  2798. }
  2799. }
  2800. return oldFunc == null ? _sort.apply(this, arguments) : _sort.call(this, newFunc);
  2801. }
  2802.  
  2803. var UA = navigator.userAgent;
  2804. var googleDomain = "translate.google.com";
  2805. var dictURL = "https://" + googleDomain + "/translate_a/single?client=t";
  2806. var ttsURL = "http://" + googleDomain + "/translate_tts?client=t";
  2807.  
  2808. function init_google_value_tk() {
  2809. var url = "https://" + googleDomain + "/translate_a/element.js";
  2810. GM_xmlhttpRequest({
  2811. method: "GET",
  2812. url: url,
  2813. onreadystatechange: function(resp) {
  2814. if (resp.readyState == 4) {
  2815. clearTimeout(setTimeout(function() {
  2816. this.abort();
  2817. }, 2000));
  2818. if (resp.status == 200) {
  2819. init_google_value_tk_parse(resp.responseText);
  2820. }
  2821. }
  2822. }
  2823. });
  2824. }
  2825.  
  2826. function init_google_value_tk_parse(responseText) {
  2827. var res = /c\._ctkk='(.+?)'/i.exec(responseText);
  2828. if (res != null) {
  2829. GM_setValue('google_value_tk', res[1]);
  2830. };
  2831. }
  2832.  
  2833. const Request = async(txt, sl = 'auto', tl = 'auto') => {
  2834. return new Promise((resolve, reject) => {
  2835. function parse(gTradStringArray) {
  2836. var arr = JSON.parse(gTradStringArray);
  2837. var translation = '';
  2838. for (let i = 0; i < arr[0].length; i++) {
  2839. if (typeof arr[0][i][0] != 'undefined' && arr[0][i][0] != null) translation += arr[0][i][0];
  2840. }
  2841. resolve(translation);
  2842. }
  2843. var tk = googleTK(txt);
  2844. var Url = dictURL +
  2845. "&hl=auto" +
  2846. "&sl=" + sl + "&tl=" + tl +
  2847. "&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" +
  2848. "&tk=" + tk +
  2849. "&q=" + encodeURI(txt);
  2850. var method = 'POST';
  2851. var Data = '';
  2852. var Hdr = {
  2853. "User-Agent": UA,
  2854. "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
  2855. "Accept-Encoding": "gzip, deflate"
  2856. }
  2857. var Q = Url.split('&q=');
  2858. Url = Q[0];
  2859. Data = '&q=' + Q[1];
  2860. Hdr["Content-Length"] = Data.length + '';
  2861. Hdr["Content-Type"] = "application/x-www-form-urlencoded; charset=UTF-8";
  2862. GM_xmlhttpRequest({
  2863. method: method,
  2864. url: Url,
  2865. data: Data,
  2866. headers: Hdr,
  2867. onload: function(resp) {
  2868. try {
  2869. parse(resp.responseText)
  2870. } catch (e) {
  2871. console.error(e);
  2872. resolve(null);
  2873. }
  2874. }
  2875. });
  2876. });
  2877. }
  2878.  
  2879. // return token for the new API
  2880. function googleTK(text) {
  2881. // view-source:https://translate.google.com/translate/releases/twsfe_w_20160620_RC00/r/js/desktop_module_main.js && TKK from HTML
  2882. var uM = GM_getValue('google_value_tk');
  2883. if (uM == 'undefined' || uM == null) {
  2884. init_google_value_tk();
  2885. uM = "427110.1469889687";
  2886. } else if (Number(uM.split('.')[0]) !== Math.floor(Date.now() / 3600000)) {
  2887. init_google_value_tk();
  2888. };
  2889. var cb = "&";
  2890. var k = "";
  2891. var Gf = "=";
  2892. var Vb = "+-a^+6";
  2893. var t = "a";
  2894. var Yb = "+";
  2895. var Zb = "+-3^+b+-f";
  2896. var jd = ".";
  2897. var sM = function(a) {
  2898. return function() {
  2899. return a
  2900. }
  2901. }
  2902. var tM = function(a, b) {
  2903. for (var c = 0; c < b.length - 2; c += 3) {
  2904. let d = b.charAt(c + 2);
  2905. d = d >= t ? d.charCodeAt(0) - 87 : Number(d);
  2906. d = b.charAt(c + 1) == Yb ? a >>> d : a << d;
  2907. a = b.charAt(c) == Yb ? a + d & 4294967295 : a ^ d
  2908. }
  2909. return a
  2910. };
  2911. var vM = function(a) {
  2912. var b;
  2913. if (null !== uM) {
  2914. b = uM;
  2915. } else {
  2916. b = sM(String.fromCharCode(84));
  2917. var c = sM(String.fromCharCode(75));
  2918. b = [b(), b()];
  2919. b[1] = c();
  2920. b = (uM = unsafeWindow[b.join(c())] || k) || k
  2921. }
  2922. let d = sM(String.fromCharCode(116));
  2923. c = sM(String.fromCharCode(107));
  2924. d = [d(), d()];
  2925. d[1] = c();
  2926. c = cb + d.join(k) + Gf;
  2927. d = b.split(jd);
  2928. b = Number(d[0]) || 0;
  2929.  
  2930. for (var e = [], f = 0, g = 0; g < a.length; g++) {
  2931. var m = a.charCodeAt(g);
  2932. 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)
  2933. }
  2934. a = b || 0;
  2935. for (f = 0; f < e.length; f++) {
  2936. a += e[f], a = tM(a, Vb);
  2937. };
  2938. a = tM(a, Zb);
  2939. a ^= Number(d[1]) || 0;
  2940. 0 > a && (a = (a & 2147483647) + 2147483648);
  2941. a %= 1E6;
  2942. return a.toString() + jd + (a ^ b);
  2943. };
  2944. return vM(text);
  2945. }
  2946.  
  2947. let skinsArr, translatedCache = {};
  2948. Array.prototype.push = function(){
  2949. if(this?.length && typeof this?.[0]?.approved == 'boolean' && typeof this?.[0]?.type == 'number' && typeof this?.[0]?.zIndex == 'undefined' && !this?.[0]?.NOTREAL) skinsArr = this;
  2950. if(this?.length && typeof this?.[0]?.id == "number" && typeof this?.[0]?.color == "string"){
  2951. if(customCells){
  2952. let cell = this[this.length - 1],
  2953. [pushedCell] = arguments;
  2954. if(!prop){
  2955. prop = Object.keys(cell);
  2956. if(prop.length != 59 || prop[28] != "massCache"){
  2957. // unsafeWindow.swal({
  2958. // title: "FSFB scripts experienced an error, please contact authors",
  2959. // type: "error"
  2960. // });
  2961. console.error("FSFB Scripts error, contact authors");
  2962. }
  2963. }
  2964. if(pushedCell.id === getServerValue("r1Id")){
  2965. r1Portal.portal = pushedCell;
  2966. } else if(pushedCell.id === getServerValue("r2Id")){
  2967. r2Portal.portal = pushedCell;
  2968. }
  2969. !customDc && customize(cell);
  2970. if(svSwitch && this[0][prop[47]] && settings.checkboxes[8].active){
  2971. let hasEntities = false;
  2972. for(let i = 0; i < this.length; i++){
  2973. if(this[i].id == 1e9) hasEntities = true;
  2974. }
  2975. if(!hasEntities){
  2976. for(let i = 0; i < getServerValue("entities").length; i++){
  2977. let ent = getServerValue("entities")[i];
  2978. pushFn.apply(this, [createCell(ent.x, ent.y, ent.type, ent.size)]);
  2979. }
  2980. } else {
  2981. //console.warn("hasents");
  2982. }
  2983. svSwitch = false;
  2984. }
  2985. }
  2986. }
  2987.  
  2988. let applied = pushFn.apply(this, arguments);
  2989. if(this?.length && typeof this?.[0]?.message === 'string' && typeof this?.[0]?.name === 'string'){
  2990. chatmsgs = this;
  2991. (async() => {
  2992. if(settings.chat_translate[0].active && (settings.chat_translate[1].active || arguments[0][Object.keys(arguments[0])[0]])){
  2993. let originalMsg = arguments[0]?.untranslated ?? arguments[0].message,
  2994. 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);
  2995. 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}};
  2996. 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){
  2997. setTimeout(() => {
  2998. arguments[0].untranslated = originalMsg;
  2999. arguments[0].translatedLang = settings.chat_translate[3].set + settings.chat_translate[4].set;
  3000. arguments[0].showingOrig = settings.chat_translate[2].active;
  3001. arguments[0].message = settings.chat_translate[2].active ? originalMsg + ' [ ' + translatedMsg + ' ]' : translatedMsg;
  3002. arguments[0].filter = false;
  3003. arguments[0].cache = null;
  3004. }, 0);
  3005. }
  3006. } else { // change translate off
  3007. if('untranslated' in arguments[0] && arguments[0]?.untranslated != arguments[0].message){
  3008. setTimeout(() => {
  3009. arguments[0].message = arguments[0]?.untranslated;
  3010. arguments[0].translatedLang = 'none';
  3011. arguments[0].filter = false;
  3012. arguments[0].cache = null;
  3013. }, 0);
  3014. }
  3015. }
  3016. })();
  3017. }
  3018. return applied;
  3019. }
  3020. Array.prototype.splice = function(){
  3021. if(customCells && this.length && typeof this[0].id == "number" && typeof this[0].color == "string"){
  3022. let cell = this[arguments[0]];
  3023. if(cell == r1Portal.portal){
  3024. r1Portal.portal = null;
  3025. } else if(cell == r2Portal.portal){
  3026. r2Portal.portal = null;
  3027. }
  3028. }
  3029. return spliceFn.apply(this, arguments);
  3030. }
  3031. const fillFn = CanvasRenderingContext2D.prototype.fill, cDark = $('#cDark')[0];
  3032. CanvasRenderingContext2D.prototype.fill = function() {
  3033. if(customCells && this.canvas.id === "canvas"){
  3034. let doStroke = true;
  3035. if(this.globalAlpha == .04){
  3036. switch(this.fillStyle){
  3037. case "#ff0000": // rec
  3038. this.strokeStyle = "#cc0001";
  3039. break;
  3040. case "#76ff54": // grw
  3041. this.strokeStyle = "#66b319";
  3042. break;
  3043. case "#ffd000": // spd
  3044. this.strokeStyle = "#ccb300";
  3045. break;
  3046. case "#00a2e8": // min pack
  3047. this.strokeStyle = "#0081b9";
  3048. break;
  3049. case "#622373": // portal
  3050. this.strokeStyle = "#4e1c5c";
  3051. break;
  3052. default:
  3053. doStroke = false;
  3054. };
  3055. if(doStroke){
  3056. this.globalAlpha = .2;
  3057. this.lineWidth = 8;
  3058. this.stroke();
  3059. this.globalAlpha = .1;
  3060. this.shadowOffsetY = 1; // for fucking curser lock
  3061. }
  3062. };
  3063. if(!doStroke && this.globalAlpha == 0.04 && settings.checkboxes[2].active){
  3064. this.strokeStyle = cDark.checked ? "#FFFFFF" : "#000000";
  3065. this.globalAlpha = 1;
  3066. this.lineWidth = 30;
  3067. this.stroke();
  3068. this.globalAlpha = 0.04;
  3069. }
  3070. if (settings.theme_boxes[0].active && this.canvas.id === "canvas" && this.globalAlpha == .4) {
  3071. this.globalAlpha = 0.15
  3072. }
  3073. if (settings.theme[1].active && (this.fillStyle == "#00ff00" || this.fillStyle == "#19a0cc")) {
  3074. this.fillStyle = settings.theme[1].color
  3075. }
  3076. if (settings.theme[3].active && this.fillStyle == "#cd5564") {
  3077. this.fillStyle = settings.theme[3].color
  3078. }
  3079. if(whiteBorder4BlackCells && this.fillStyle == "#000000"){
  3080. this.strokeStyle = "#FFFFFF";
  3081. this.lineWidth = 20;
  3082. this.stroke();
  3083. }
  3084. }
  3085. return fillFn.apply(this, arguments)
  3086. }
  3087. const strokeFn = CanvasRenderingContext2D.prototype.stroke, cBubbleCells = $('#cBubbleCells')[0];
  3088. CanvasRenderingContext2D.prototype.stroke = function() {
  3089. if (customCells && this.canvas.id === "canvas") {
  3090. if (this.strokeStyle == "#dddddd" || this.strokeStyle == "#333333" || this.strokeStyle == "#4e1c5b" /* adjusted portal cell stroke color*/) {
  3091. if(settings.checkboxes[2].active) this.strokeStyle = cDark.checked ? "#FFFFFF" : "#000000";
  3092. if(this.shadowOffsetY == 1){
  3093. this.shadowOffsetY = 0;
  3094. return;
  3095. };
  3096. };
  3097. if (settings.theme_boxes[0].active && this.lineWidth != 4 && cBubbleCells.checked) {
  3098. this.lineWidth = 15 + Math.min(Math.max(avgFps - 25, 0), 10)
  3099. }
  3100. if((settings.theme[1].active || settings.theme[2].active) && (this.strokeStyle == "#00ff00" || this.strokeStyle == "#00cc00" || this.strokeStyle == "#1480a3" || this.strokeStyle == "#1690b7" || this.strokeStyle == "#00e500")) {
  3101. this.strokeStyle = settings.theme[2].active ? settings.theme[2].color : settings.theme_boxes[0].active && cBubbleCells.checked ? 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
  3102. }
  3103. if((settings.theme[3].active || settings.theme[4].active) && (this.strokeStyle == "#cd5564" || this.strokeStyle == "#a44450" || this.strokeStyle == "#b84c5a")) {
  3104. this.strokeStyle = settings.theme[4].active ? settings.theme[4].color : settings.theme_boxes[0].active && cBubbleCells.checked ? settings.theme[3].color : dimmColor(settings.theme[3].color);
  3105. }
  3106. }
  3107. return strokeFn.apply(this, arguments)
  3108. }
  3109. let drawImgFn = CanvasRenderingContext2D.prototype.drawImage;
  3110. CanvasRenderingContext2D.prototype.drawImage = function () {
  3111. if(this.globalAlpha == 0.01 && arguments[0].src && arguments[0].src.match(RegExp(`https://agma\\.io/skins/objects/[1-5]`))){
  3112. this.globalAlpha = .35;
  3113. }
  3114. drawImgFn.apply(this, arguments);
  3115. if(settings.checkboxes[7].active && this.canvas.id == "canvas"){
  3116. if(/agma\.io\/skins\/objects\/1(_lo)?\.png/gm.test(arguments[0].src) && (r1Portal.portal || r2Portal.portal)){ // using destroyed doesnt work hence splice
  3117. //draw portal mass
  3118. if(noPortalSvIdList.indexOf(currentServerId) == -1){
  3119. let c = (p) => {
  3120. if((p.portal.nSize * p.portal.nSize / 100) != p.lastMass){
  3121. p.lastMassChange = Date.now();
  3122. }
  3123. p.lastMass = (p.portal.nSize * p.portal.nSize / 100);
  3124.  
  3125. let value = Date.now() - p.lastMassChange > 200 ? ~~(((p.portal.nSize * p.portal.nSize / 100) - getServerValue("r" + p.room + "StartMass")) / getServerValue("ejPortalMass")).toString() : p.lastValue;
  3126. p.lastValue = value;
  3127. if(value > 9 || value < 0){
  3128. value = "?";
  3129. }
  3130. this.fillStyle = value == "7" ? "#FFCC12" : "#FFFFFF";
  3131. this.globalAlpha = 1;
  3132. this.font = "72px Ubuntu, serif";
  3133. this.fillText(value, p.portal.x - this.measureText(value).width / 2, p.portal.y + 20);
  3134. }
  3135. r1Portal.portal && c(r1Portal);
  3136. currentServerId != 43 && r2Portal.portal && c(r2Portal);
  3137. }
  3138. }
  3139. }
  3140. }
  3141. let dURIC = unsafeWindow.decodeURIComponent,
  3142. opt = [0x92933AFC, 0x75408D32];
  3143. unsafeWindow.decodeURIComponent = function(x){
  3144. if(x === "") x = opt[Math.round(Math.random())].toString();
  3145. return dURIC(x);
  3146. }
  3147. function hslToHex(h, s, l) {
  3148. h /= 360;
  3149. s /= 100;
  3150. l /= 100;
  3151. let r, g, b;
  3152. if (s === 0) {
  3153. r = g = b = l;
  3154. } else {
  3155. const hue2rgb = (p, q, t) => {
  3156. if (t < 0) t += 1;
  3157. if (t > 1) t -= 1;
  3158. if (t < 1 / 6) return p + (q - p) * 6 * t;
  3159. if (t < 1 / 2) return q;
  3160. if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
  3161. return p;
  3162. };
  3163. const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
  3164. const p = 2 * l - q;
  3165. r = hue2rgb(p, q, h + 1 / 3);
  3166. g = hue2rgb(p, q, h);
  3167. b = hue2rgb(p, q, h - 1 / 3);
  3168. }
  3169. const toHex = x => {
  3170. const hex = Math.round(x * 255).toString(16);
  3171. return hex.length === 1 ? '0' + hex : hex;
  3172. };
  3173. return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
  3174. }
  3175.  
  3176.  
  3177. const _lineTo = CanvasRenderingContext2D.prototype.lineTo;
  3178. CanvasRenderingContext2D.prototype.lineTo = function () {
  3179. if(settings.theme[5].active) this.strokeStyle = rainbowMapBorder ? hslToHex(~~(Date.now() / rainbowBorderSpeed) % 360, 100, 50) : settings.theme[5].color;
  3180. return _lineTo.apply(this, arguments);
  3181. }
  3182.  
  3183.  
  3184. let currentMass = 0, leaderboardPos;
  3185. const _fillText = CanvasRenderingContext2D.prototype.fillText;
  3186. CanvasRenderingContext2D.prototype.fillText = function() {
  3187. if ((this.fillStyle == "#ffffff" || this.fillStyle == "#626262") && isNaN(arguments?.[0]) && /^Mass: \d+$/gm.test(arguments[0])){
  3188. currentMass = +arguments[0].match(/(?<=^Mass: )\d+$/gm)[0];
  3189. if(hiddenUI) arguments[0] = " ";
  3190. }
  3191. if(this.canvas.id == "leaderboard" && this.fillStyle == "#ffaaaa" && /^\d+(?=\.\s)/gm.test(arguments[0])) [leaderboardPos] = arguments[0].match(/^\d+(?=\.\s)/gm);
  3192. _fillText.apply(this, arguments);
  3193. }
  3194.  
  3195. const debounce = (func, timeout = 300) => {
  3196. let timer;
  3197. return (...args) => {
  3198. clearTimeout(timer);
  3199. timer = setTimeout(() => { func.apply(this, args); }, timeout);
  3200. };
  3201. }
  3202.  
  3203. let intervalCount = 0, currentUser, lastLoggedOut = Date.now();
  3204. // const mainInterval = setInterval(() => {
  3205. const mainInterval = () => {
  3206. intervalCount++;
  3207. if(hoverShowSkinID && $('#publicSkinsPage').children().length > 0 && $('#publicSkinsPage').find('[id^="skinContainer"]>img')[0]?.title == ''){ // check if skins have loaded
  3208. $('[id^="skinContainer"]').each(function(){
  3209. $(this).find('img').attr('title', $(this).attr('id').replace('skinContainer', '')); // make hover show skin ID
  3210. })
  3211. $('.publicskins-nav-btn').on('click', () => {
  3212. $('[id^="skinContainer"]').each(function(){
  3213. $(this).find('img').attr('title', $(this).attr('id').replace('skinContainer', ''));
  3214. })
  3215. })
  3216. }
  3217. if(sortWearablesByOwned && $('#phpWearables>li').length && !$('#fsfb-wearsloaded').length){
  3218. $($('[id^="wearableUseBtn"]').get().reverse()).each(function(){
  3219. $($(this).parents().get(2)).insertBefore($("#phpWearables li:eq(0)"));
  3220. })
  3221. $('#phpWearables').append('<div id="fsfb-wearsloaded"></div>')
  3222.  
  3223. }
  3224. if(publicSkinSearch && $('#publicSkinsPage').children().length > 0 && $('#fsfb-skinsearch').length == 0){
  3225. $('.publicskins-nav-bar').eq(0).after(`<input id="fsfb-skinsearch" placeholder="Enter skin name/id here" type="text">`);
  3226. const handlePress = debounce(() => {
  3227. if(!$('#fsfb-skinsearch').val()) return void($('.btn.publicskins-nav-btn.btn-default:not(.btn-primary)')[0].click());
  3228. let skinsSearchedArr = [];
  3229. for(let i of skinsArr){
  3230. if(i.type == 4 && i.sharedPublic == true && (i.name.toLowerCase().includes($('#fsfb-skinsearch').val().toLowerCase()) || i.id == +$('#fsfb-skinsearch').val())){
  3231. let newItem = i;
  3232. newItem.NOTREAL = true;
  3233. skinsSearchedArr.push(newItem);
  3234. }
  3235. }
  3236. let totalRows = Math.ceil(skinsSearchedArr.length / 4);
  3237. if(totalRows == 0){
  3238. $('#publicSkinsPage tbody').html('').append('<h1>No Skins Found</h1>');
  3239. } else {
  3240. $('#publicSkinsPage tbody').html('');
  3241. for(let i = 0; i < totalRows; i++) $('#publicSkinsPage tbody').append('<tr></tr>');
  3242. let currRow = 0, currColumn = 0;
  3243. for(let i of skinsSearchedArr){
  3244. if(++currColumn > 4) currRow++, currColumn = 1;
  3245. $('#publicSkinsPage tr').eq(currRow).append(`<td id="skinContainer${i.id}" class="skin-container"><img src="skins/${i.id}_lo.png" alt="" ${hoverShowSkinID ? 'title="' + i.id + '"': ''}><h4>${sanitize(i.name)}</h4><button id="skinUseBtn${i.id}" class="btn btn-primary skinuse-btn" onclick="toggleSkin(${i.id});">Use</button></td>`)
  3246. }
  3247. }
  3248. }, 300);
  3249. $('#fsfb-skinsearch').on('keydown', handlePress);
  3250. }
  3251. currentUser = $('#userCoins2')[0].innerText;
  3252. let user_abil = currentUser == 'Please Login First' ? null : misc_settings.abil?.[currentUser];
  3253. if(user_abil != undefined && showRemainingAbilityTime){
  3254. for(let i in user_abil){
  3255. let text = $(`.purchase-btn.confirmation[item="${i}"]`).parents().eq(0).find('div h5'),
  3256. active = $('#' + $(`.purchase-btn.confirmation[item="${i}"]`).parents().eq(0)[0].id + ' img').eq(1).css('display') != "none";
  3257. // has been 24h+ and the player hasn't logged out since it's expired
  3258. if(Date.now() - user_abil[i] > 8.64e7 && active){
  3259. text.eq(1).text('EXPIRED IF UNLOG');
  3260. text.eq(0).find('div h5').hide();
  3261. }
  3262. // has been >24h
  3263. else if(Date.now() - user_abil[i] < 8.64e7 && active){
  3264. text.eq(0).hide();
  3265. text.eq(1).text(msToTime(8.64e7 - (Date.now() - user_abil[i]))).show();
  3266. }
  3267. else { // has been 24h+ & player has logged out
  3268. text.eq(0).find('div h5').show();
  3269. text.eq(1).find('div h5').hide();
  3270. }
  3271. }
  3272. } else {
  3273. $('.white_shopdesc').show();
  3274. $('.white_shopdesc.fsfb-fake').hide();
  3275. }
  3276. if(accounts[currentUser] == null && currentUser !== 'Please Login First'){
  3277. xpInfo();
  3278. coinsInfo();
  3279. accounts = {...accounts, ...{[currentUser] : {coins: currentCoins, xp: currentXP, lvl: round(currentPercent, 3) + currentLevel}}};
  3280. }
  3281. if(accounts[currentUser] != null && accounts[currentUser].coins == 0) accounts[currentUser].coins = currentCoins;
  3282. if(accounts[currentUser] != null && accounts[currentUser].xp == 0) accounts[currentUser].xp = currentXP;
  3283. if(coinXPstats && intervalCount % 12 == 0){
  3284. setTimeout(() => {
  3285. xpInfo();
  3286. lastMinXP.push(new record(round(currentXP, 3), round(currentPercent, 3) + currentLevel, currentUser, lastMinXP));
  3287. const prevObjXP = lastMinXP[lastMinXP.length - 1];
  3288. if(prevObjXP && prevObjXP.id % (6e4 / updateTimeXP) == 0) lastHrXP.push(new record(round(currentXP, 3), round(currentPercent, 3) + currentLevel, currentUser, lastHrXP));
  3289. if(lastMinXP.length > 6e4 / updateTimeXP) lastMinXP.shift();
  3290. if(lastHrXP.length > 60) lastHrXP.shift();
  3291. if(!coinsHTMLactive && $('#stats-container').css('display') == 'block') updateUI();
  3292. unsafeWindow.logStatsScriptXP && console.log(lastMinXP, lastHrXP);
  3293. }, 500);
  3294. }
  3295. if(coinXPstats && intervalCount % 6 == 0){
  3296. coinsInfo();
  3297. lastMinCoins.push(new record(currentCoins, 0, currentUser, lastMinCoins));
  3298. const lastObjCoins = lastMinCoins[lastMinCoins.length - 1];
  3299. if(lastObjCoins && lastObjCoins.id % (6e4 / updateTimeCoins) == 0) lastHrCoins.push(new record(currentCoins, 0, currentUser, lastHrCoins));
  3300. if(lastMinCoins.length > 6e4 / updateTimeCoins) lastMinCoins.shift();
  3301. if(lastHrCoins.length > 60) lastHrCoins.shift();
  3302. if(coinsHTMLactive && $('#stats-container').css('display') == 'block') updateUI();
  3303. unsafeWindow.logStatsScriptCoins && console.log(lastMinCoins, lastHrCoins);
  3304. }
  3305. if(coinsHTMLactive){
  3306. $('#stats-sesh-length').text(msToTime(Date.now() - scriptStartCoins));
  3307. } else {
  3308. $('#stats-sesh-length').text(msToTime(Date.now() - scriptStartXP));
  3309. }
  3310. if(intervalCount % 3 == 0 && misc_settings?.statsPos != null){
  3311. statsboxPos = {top: $("#stats-container")[0].style.top, left: $("#stats-container")[0].style.left};
  3312. misc_settings.statsPos = statsboxPos;
  3313. set("fsfb-misc", misc_settings);
  3314. }
  3315. if(intervalCount % 2 == 0){ // "You have an activated bot pack available: 100 XXL Bots 1 Hours! Restart your bots before they expire!"
  3316. if(chatmsgs != null && chatmsgs?.length > 2){ // "You have an activated bot pack available: 100 Bots 24 Hours! Restart your bots before they expire!"
  3317. for(let i of chatmsgs){
  3318. if(i.name == '' && i.cache != null && i.cache.color2 == '#ff8100'){
  3319. if(i.message.match(/(?<=(Welcome back to Agma, )).+/g)?.[0] == currentUser){
  3320. const msgBots = $('.memberType').text() == 'GOLD MEMBER' ? chatmsgs?.[chatmsgs.indexOf(i) - 2] : chatmsgs?.[chatmsgs.indexOf(i) - 1],
  3321. msgGM = $('.memberType').text() == 'GOLD MEMBER' ? chatmsgs?.[chatmsgs.indexOf(i) - 1] : null;
  3322. if(msgBots?.message.includes('Restart your bots before they expire!')){
  3323. if(misc_settings.bots[currentUser] != null) misc_settings.bots[currentUser].chatAmt = msgBots.message.match(/\d+.+\d Hours/g)[0];
  3324. minsChatAmt[currentUser] = {...minsChatAmt[currentUser], ... {amt: msgBots.message.match(/\d+.+\d Hours/g)[0], started: true}};
  3325. set("fsfb-misc", misc_settings);
  3326. } else if(msgBots?.message.match(/(?<=(You have a new bot pack available: )).+(?=(! Start your bots in the minion panel.))/g)?.length){
  3327. if(misc_settings.bots[currentUser] != null) misc_settings.bots[currentUser].chatAmt = msgBots.message.match(/\d+.+\d Hours/g)[0];
  3328. minsChatAmt[currentUser] = {...minsChatAmt[currentUser], ... {amt: msgBots.message.match(/\d+.+\d Hours/g)[0], started: false}};
  3329. set("fsfb-misc", misc_settings);
  3330. }
  3331. if(msgGM != null && msgGM.message.match(/(?<=You have )\d+(?= Days left of Gold Member!)/gm)?.length){
  3332. accGoldMem[currentUser] = {...accGoldMem[currentUser], ... {days: msgGM.message.match(/(?<=You have )\d+(?= Days left of Gold Member!)/gm)[0], has: true}};
  3333. } else {
  3334. accGoldMem[currentUser] = {...accGoldMem[currentUser], ... {has: false}};
  3335. }
  3336. } else if(i.message.match(/(?<=(You have a new bot pack available: )).+(?=(! Start your bots in the minion panel.))/g)?.length){
  3337. if(misc_settings.bots[currentUser] != null) misc_settings.bots[currentUser].chatAmt = i.message.match(/\d+.+\d Hours/g)[0];
  3338. minsChatAmt[currentUser] = {...minsChatAmt[currentUser], ... {amt: i.message.match(/\d+.+\d Hours/g)[0], started: false}};
  3339. set("fsfb-misc", misc_settings);
  3340. }
  3341. /* else if(minsChatAmt[currentUser]?.chatAmt && i.message.match(/\d+.+\d Hour/g)?.[0] == minsChatAmt[currentUser].chatAmt.match(/\d+.+\d Hour/g)[0]){
  3342. minsChatAmt[currentUser].amt = i.message.match(/\d+.+\d Hour/g)[0];
  3343. } */
  3344. }
  3345. }
  3346. }
  3347. }
  3348. if(currentServerId === 0){
  3349. try {
  3350. for(let i of JSON.parse(localStorage.gameservers)){
  3351. if(i.isCurrent) currentServerId = i.id;
  3352. }
  3353. svSwitch = true;
  3354. } catch {};
  3355. }
  3356.  
  3357. if(currentUser == 'Please Login First' || $('#level').text() == 0) lastLoggedOut = Date.now();
  3358. changeTitle(settings.checkboxes[4].active ? currentUser == 'Please Login First' ? "Agma.io" : "Agma.io - " + currentUser : "Agma.io - A free multiplayer MMO game");
  3359.  
  3360. if($('#friendAcceptAll').length > 0 && friendDeclineAll && $('#friendRejectAll').length < 1 && currentUser != 'Please Login First') addFriendDecline();
  3361.  
  3362. if($('#friendDialogMessage').text() != 'Login to see your friendlist' && $('#friendDialogMessage').text() == 'Loading...' && $('#friendsRequestsAmt').text() == '' && friendDeclineAll && /* $('#requestList').children().length > 0 && */ currentUser != 'Please Login First'){
  3363. $('#btnFriends')[0].click();
  3364. $('#btnFriends')[0].click();
  3365. }
  3366.  
  3367. fpsArr.push(+document.getElementById("fps").innerText);
  3368. if(fpsArr.length == 6) fpsArr.shift();
  3369. avgFps = mean(fpsArr);
  3370. setTimeout(mainInterval, 1e3);
  3371. }
  3372. setTimeout(mainInterval, 1e3);
  3373. // }, 1e3);
  3374.  
  3375.  
  3376. if(showXPdecimals){
  3377. $('.progress-bar span').each(function(){
  3378. $(this).hide().clone().insertAfter($(this)).addClass('fsfb-fakePerc').removeClass('sr-only exp-bar').show(); // show detailed lvl percent (clone to prevent showing the old % before it gets changed)
  3379. });
  3380. setInterval(() => {
  3381. let currPercent = $('.progress-bar[role=progressbar]')[0].style.width.slice(0, -1) / 100;
  3382. $('.fsfb-fakePerc').text(currPercent && $('.sr-only.exp-bar')[0].textContent != '0%' ? round(currPercent * 100, 2) + '%' : '0%');
  3383. }, 250);
  3384. }
  3385.  
  3386. const antiAFK = () => {
  3387. setTimeout(antiAFK, 3e4);
  3388. if(!$('#fsfb-antiAFK').is(':checked'))return; // move mouse every 30sec
  3389. if(linesplitting){
  3390. $('#canvas').trigger($.Event('mousemove', {clientX: pointMove.x + 1, clientY: pointMove.y})).trigger($.Event('mousemove', {clientX: pointMove.x - 1, clientY: pointMove.y}));
  3391. } else {
  3392. $('#canvas').trigger($.Event('mousemove', {clientX: mosX + 1, clientY: mosY})).trigger($.Event('mousemove', {clientX: mosX - 1, clientY: mosY}));
  3393. }
  3394. }
  3395.  
  3396. setTimeout(antiAFK, 3e4);
  3397.  
  3398.  
  3399. const updateScriptSettingsUI = () => {
  3400. for(let i of settings.checkboxes) $('#' + i.id).prop("checked", i.active).trigger("change");
  3401. for(let i of settings.hotkeys) $('#' + i.id).text(getName(i.key));
  3402. for(let i of settings.fastsplit_hotkeys) $('#' + i.id).text(getName(i.key));
  3403. for(let i of settings.quickSettings){
  3404. $('#' + i.id1).val(i.set);
  3405. $('#' + i.id).text(getName(i.key));
  3406. }
  3407. $('#' + settings.slowFeed[0].id).text(getName(settings.slowFeed[0].key));
  3408. $('#' + settings.slowFeed[1].id).val(settings.slowFeed[1].val);
  3409. for(let i of settings.fastsplit_hotkeys){
  3410. i.val == null ? $('#' + i.id).text(getName(i.key)) : $('#' + i.id).val(i.val);
  3411. }
  3412. for(let i of settings.uiScaling) $('#' + i.id).val(i.level).trigger("change");
  3413. for(let i of settings.export_import) $('#' + i.id).prop("checked", i.active).trigger("change");
  3414. for(let i of settings.theme){
  3415. $('#' + i.id).prop("checked", i.active).trigger("change");
  3416. $('#' + i.id1).val(i.color).trigger("change");
  3417. }
  3418. for(let i of settings.theme_boxes) $('#' + i.id).prop("checked", i.active).trigger("change");
  3419. for(let i of settings.chat_translate){
  3420. 'set' in i ? $('#' + i.id).val(i.set) : $('#' + i.id).prop('checked', i.active).trigger('change');
  3421. }
  3422. }
  3423. setTimeout(() => updateScriptSettingsUI(), 1e3);
  3424.  
  3425. $('body').append('<div id="fsfb-css-styles"><style id="hideUI-css" type="text/css"></style><style id="stats-input-css" type="text/css">#stats-table input{ display: none; }</style></div>');
  3426.  
  3427. const _replaceCSS = (a,b) => {
  3428. document.getElementById(a).innerHTML = b;
  3429. }
  3430. $('body').append(`<div class="fade fsfb-bug-modal modal"aria-hidden=true role=dialog tabindex=-1><div class="modal-dialog modal-lg"><div class=modal-content><div class=modal-interior><h2 class=fsfb-modal-title>Script Documentation</h2><button class="close fsfb-btn"data-dismiss=modal type=button>×</button><section class=fsfb-modal-body><div><span>Chat Copy/Cut/Paste</span>- allows you to use the commands in chat (e.g. Ctrl + V becomes avaiable inside chat)</div><div><span>Anti-AFK</span>- prevents you from automatically disconnecting after 10 minutes</div><div><span>Anti-Invis</span>- shows you players even when they have the invisibility ability active</div><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><div><span>Change Page Title</span>- changes the tab's title to just "Agma.io" with the current username</div><div><span>Hide Shouts</span>- prevent megaphone shouts from showing up at all</div><div><span>Hold To Spam</span>- while the powerup's hotkey is held, it will continuously use the powerup</div><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><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><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><div><span>Food/Virus/Mothercell Color</span>- changes the color that's filling these to a custom one</div><div><span>Virus/Mothercell Stroke</span>- changes the color of the stroke (border/outline) to a custom one</div><div><span>Spiked Cells</span>- render all cells with the spikes from the infecton gamemode</div><div><span>Show Mass</span>- show the mass of all players' cells</div><div><span>Only My Skin</span>- hide all skins besides the one you're using</div><div><span>Only Party Skin</span>- hide all skins besides the ones people in your party are using</div><div><span>Only My Nick</span>- hide all nicks besides the one you're using</div><div><span>Only Party Nick</span>- hide all nicks besides the ones people in your party are using</div><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><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://gf.qytechs.cn/en/scripts/404559-agma-io-linesplit-overlay target=_blank>Wynell's script</a>)</div><div><span>Macrosplit Bots</span>- hold this key to macrosplit your bots without switching controls off of yourself</div><div><span>Hide UI</span>- press this key to toggle showing the game UI (intended for recording/screenshots)</div><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><div><span>Slow Feed Speed</span>- the speed at which eject mass is pressed when slow-feeding</div><div><span>Quick Settings</span>- when the hotkey assigned is pressed, it will toggle the setting that is selected</div><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><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><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><div><span>Chat Size</span>- make the size of chat bigger/smaller</div><div><span>Inventory Size</span>- make the size of powerups inventory bigger/smaller</div><div><span>Statsbox Size</span>- make the size of XP/coins stats bigger/smaller</div><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><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><table><tr><th>Chat Command<th>Description<tr><td class=fsfb-cmd-title>${chatPrefix}help<td>list all available chat commands<tr><td class=fsfb-cmd-title>${chatPrefix}bots<td>show which bot pack you have active and how much time is remaining<tr><td class=fsfb-cmd-title>${chatPrefix}pws<td>show the amount of powerups in your inventory (recommended to use ${chatPrefix}pws1, ${chatPrefix}pws2, and ${chatPrefix}pws3)<tr><td class=fsfb-cmd-title>${chatPrefix}totalpws<td>show the total amount of powerups you have in your inventory<tr><td class=fsfb-cmd-title>${chatPrefix}xp<td>show then amount of xp you've completed for this level<tr><td class=fsfb-cmd-title>${chatPrefix}lvl<td>show your level and how much of the level you've completed<tr><td class=fsfb-cmd-title>${chatPrefix}coins<td>show the amount of coins you have<tr><td class=fsfb-cmd-title>${chatPrefix}hours<td>show the hours you have on your account</tr><td class=fsfb-cmd-title>${chatPrefix}rank<td>show your ranking</td><tr><td class=fsfb-cmd-title>${chatPrefix}ping<td>show your current ping<tr><td class=fsfb-cmd-title>${chatPrefix}fps<td>show your current FPS<tr><td class=fsfb-cmd-title>${chatPrefix}topmass<td>show your highest mass<tr><td class=fsfb-cmd-title>${chatPrefix}cells<td>show your cell count<tr><td class=fsfb-cmd-title>${chatPrefix}pws1<td>show the first part of your powerup inventory<tr><td class=fsfb-cmd-title>${chatPrefix}pws2<td>show the second part of your powerup inventory<tr><td class=fsfb-cmd-title>${chatPrefix}pws3<td>show the third part of your powerup inventory<tr><td class=fsfb-cmd-title>${chatPrefix}friends<td>show how many friends you have and how many are online<tr><td class=fsfb-cmd-title>${chatPrefix}requests<td>show how many friend requests you have<tr><td class=fsfb-cmd-title>${chatPrefix}gold<td>show how many days of gold member you have left remaining<tr><td class=fsfb-cmd-title>${chatPrefix}alive<td>show the amount of time you've been alive<tr><td class=fsfb-cmd-title>${chatPrefix}mass<td>show your current mass<tr><td class=fsfb-cmd-title>${chatPrefix}user<td>show your current username<tr><td class=fsfb-cmd-title>${chatPrefix}customs<td>show the amount of custom skins you own<tr><td class=fsfb-cmd-title>${chatPrefix}wearables<td>show how many wearables you own<tr><td class=fsfb-cmd-title>${chatPrefix}cloak<td>show the remaining time of your cloak ability<tr><td class=fsfb-cmd-title>${chatPrefix}add [user]<td>type this command to quickly add a friend using chat<tr><td class=fsfb-cmd-title>${chatPrefix}partymembers<td>show how many people are in your party<tr><td class=fsfb-cmd-title>${chatPrefix}players<td>show how many players are online in your server and are online in all agma servers<tr><td class=fsfb-cmd-title>${chatPrefix}server<td>show which server you're currently in<tr><td class=fsfb-cmd-title>${chatPrefix}abils<td>show your currently active abilities<tr><td class=fsfb-cmd-title>${chatPrefix}xpproj<td>show the predicted amount of XP you will gain in an hour<tr><td class=fsfb-cmd-title>${chatPrefix}coinsproj<td>show the predicted amount of coins you will gain in an hour<tr><td class=fsfb-cmd-title>${chatPrefix}xprem<td>show the amount of XP remaining for your level<tr><td class=fsfb-cmd-title>${chatPrefix}xphour<td>show the amount of xp you've gained in the last hour<tr><td class=fsfb-cmd-title>${chatPrefix}coinshour<td>show the amount of coins you've gained in the last hour<tr><td class=fsfb-cmd-title>${chatPrefix}xpmin<td>show the amount of XP you've gained in the last minute<tr><td class=fsfb-cmd-title>${chatPrefix}coinsmin<td>show the amount of coins you've gained in the last minute<tr><td class=fsfb-cmd-title>${chatPrefix}xp12s<td>show the amount of coins you've gained in the last 12 seconds<tr><td class=fsfb-cmd-title>${chatPrefix}xpsesh<td>show the amount of XP you've gained in this session<tr><td class=fsfb-cmd-title>${chatPrefix}lifetimexp<td>show the total amount of XP you've earned in your account's lifetime<tr><td class=fsfb-cmd-title>${chatPrefix}seshcoins<td>show the amount of coins you've gained in this session<tr><td class=fsfb-cmd-title>${chatPrefix}waifu [user]<td>rate the waifu of the selected username (leave user blank to rate yourself)<tr><td class=fsfb-cmd-title>${chatPrefix}pro [user]<td>show how pro someone is (leave user blank to rate yourself)<tr><td class=fsfb-cmd-title>${chatPrefix}dog [user]<td>show how dog someone is (leave user blank to rate yourself)<tr><td class=fsfb-cmd-title>${chatPrefix}king [user]<td>show how king someone is (leave user blank to rate yourself)<tr><td class=fsfb-cmd-title>${chatPrefix}dice [sides]<td>roll a die with the desired number of sides<tr><td class=fsfb-cmd-title>${chatPrefix}rng [min] [max]<td>generate a random number in a range<tr><td class=fsfb-cmd-title>${chatPrefix}coinflip<td>flip a coin and see if it lands on heads or tails<tr><td class=fsfb-cmd-title>${chatPrefix}script<td>show the current script you're using & which version<tr><td class=fsfb-cmd-title>${chatPrefix}time<td>show your current date & time<tr><td class=fsfb-cmd-title>${chatPrefix}skins<td>show your bought skins and their worth (limited as well)<tr><td class=fsfb-cmd-title>${chatPrefix}ratefriends [user1] [user2]<td>show how what percent friends two usernames are<tr><td class=fsfb-cmd-title>${chatPrefix}rateenemies [user1] [user2]<td>show how what percent enemies two usernames are<tr><td class=fsfb-cmd-title>${chatPrefix}leaderboard<td>show your leaderboard position</table><div><span>Hide Ads</span>- both video and image ads will be removed from the screen</div><div><span>Skin Search</span>- search through skins by their names/ids</div><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><div><span>Sort Wearables</span>- wearables are automatically sorted by owned (the ones you own will be before all others)</div><div><span>Extra Bot Packs</span>- added hidden bot packs that can be purchased with coins (originally discovered by firebone)</div><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><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><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><div><span>Unlock Free Skins</span>- gives you access to the facebook & youtube free skins</div><div><span>Hover For Skin ID</span>- hovering skins in the skin menu will shop their ID</div><div><span>In Depth XP/Coins Stats</span>- click on coins/xp progress bar in top left to view respective statistics</div><div><span>XP Bar Decimals</span>- show the percentage up to 2 decimal places</div><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><div><span>Inventory Single Row</span>- put powerups inventory on a single row instead of on 2 seperate rows (inspired by Principito)</div><div><span>Custom Backgrounds</span>- a few <a href=https://imgur.com/a/sTANNBE target=_blank>backgrounds</a></div></section></div></div></div></div>`)
  3431. const styles = document.createElement('style');
  3432. // styles.innerHTML = `#fsfb-wearsloaded{ display: none; width: 0; height: 0; } #fsfb-skinsearch{ border: 1px solid #2e6da4; background-color: #222328; font-size: 17px; border-radius: 4px; width: 100%; padding: 4px 4px 4px 8px; margin: 4px 0; color: white; } #stats-table td{ font-size: 17px; } #stats-table label{ padding-right: 10px; margin: 0 6px 0 2px; } #stats-table input{ transform: scale(1.8); margin: 5px 8px 5px 5px; } #friendRejectAll{ color: #ff4000 !important; } ${ inventorySingleRow && ` #inventory{ display: flex; position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%); } .fsfb-inventories { position: initial !important; transform: initial !important; } ` } div.fsfb-slider{ display: flex; align-items: center; } input[type="range"].fsfb-slider{ width: 58px; display: inline; position: absolute; right: 5px; } #fsfb-minionNuker img{ margin-top: 2px; } .fsfb-shown{ display: block !important; } .fsfb-hidden{ display: none !important; } #fsfb-quickbuy{ justify-content: center; align-items: center; } #fsfb-quickbuy-img{ height: 80% } .fsfb-bug-modal>div>div{ -webkit-box-shadow: 0 5px 15px rgb(0 0 0 / 50%); background: linear-gradient(to bottom,#3b414e 0,#302f33 100%); border: 3px solid #232630; } .close.fsfb-btn{ position: absolute; right: 10px; top: 3px; font-size: 60px; } .fsfb-modal-body>div{ color: ffffffb0; margin: 10px 20px } .fsfb-modal-body>table{ width: 90%; margin: 10px 20px; } .fsfb-cmd-title{ color: white; white-space: nowrap; padding-right: 12px !important; } .fsfb-modal-body>table th{ color: white; } .fsfb-modal-body>table td{ padding: 4px 0; } .fsfb-modal-body>div>span{ color: white; } .fsfb-modal-body{ margin: 10px 10px; font-size: 20px; max-height: 600px; overflow-y: auto; } .fsfb-modal-title{ text-align: center; color: white; } .fsfb-hotkey{ background-color: #df901c; color: #fff; cursor: pointer; text-align: center; min-width: 40px; max-width: 60px; height: 18px; line-height: 18px; vertical-align: middle; border-radius: 9px; right: 5px; position: absolute; display: inline-block; padding: 0 5px; overflow: hidden; opacity: 1; } .fsfb-modal-body::-webkit-scrollbar-thumb { background-color: #57595b; border: 1px solid black; border-radius: 12px; } .fsfb-modal-body::-webkit-scrollbar { border: 1px solid black; background-color: #2523239e; width: 15px; border-radius: 12px; } .fsfb-modal-body::-webkit-scrollbar-track { -webkit-box-shadow: inset 0px 0px 2px 2px rgba(0, 0, 0, 0.75); box-shadow: inset 0px 0px 2px 2px rgba(0, 0, 0, 0.75); border-radius: 12px; } .fsfb-hotkey:hover { background-color: #f1a02d; } .fsfb-hotkey.selected{ background-color: #ff4; color: #444; } #fsfb-settings-main p{ margin: 0; display: inline-block; margin-left: 4px; } #fsfb-settings-main{ display: -ms-grid; display: grid; -ms-grid-columns: 50% 50%; grid-template-columns: 50% 50%; } #settingPage4::-webkit-scrollbar-thumb { background-color: #ff9800c2; border-radius: 12px; border: 1px #000000c2 solid; } #settingPage4::-webkit-scrollbar { border: 1px solid #00000085; background-color: #2523239e; width: 9px; border-radius: 12px; } #settingPage4{ display: none; max-height: 660px; overflow-x: hidden; } .padbot10{ padding-bottom: 10px; } #fsfb-slowfeedtimer, #fsfb-secdelay, #fsfb-firstdelay{ border: none; width: 40px; } select.fsfb-quickchange{ background: none; border: none; height: 20px; } select.fsfb-quickchange:focus-visible{ outline: none; } select.fsfb-quickchange option{ background: #222; } .fsfb-sect-ch label{ display: flex; align-items: center; } .fsfb-sect-ch label input{ margin: 0 2px 0 0; } #fsfb-sect-theme label input[type="color"]{ width: 14px; height: 14px; opacity: 0; border: none; background-color: white; margin: 0; cursor: pointer; } #fsfb-sect-theme label p{ min-width: 120px; margin-left: 5px; } #fsfb-sect-theme label div{ border-radius: 4px; border: 1px solid #ffffff29; } #fsfb-ximport-cont{ display: flex; justify-content: space-around; margin-top: 7px; } .fsfb-eximport{ background-color: #df901c; color: white; padding: 5px 17px; border-radius: 25px; cursor: pointer; } .hideMegaphone{ display: none !important; } .fsfb-fake{ padding: 0 0 0 43px; color: #cbff4e !important; } #fsfb-extra-info{ margin: 10% 0 0 90%; cursor: pointer; } #linesplit-markers div { background-color: transparent; height: 15px; aspect-ratio: 1; position: fixed; z-index: 999; border: 2px solid rgb(255 255 255 / 80%); border-radius: 50%; display: none; } #linesplit-top { top: -7.5px; transform: translateX(-50%); left: 50%; } #stats-container{ background: rgba(0,0,0,.5); top: 200px; position: absolute; border: 1px white solid; border-radius: 12px; color:white; left:30px; } #stats-main{ padding: 10px; } #stats-extra-info{ color: #00bbff; font-size: 15px; margin-bottom: 2px; display: block; } #stats-info div{ display:flex; } #stats-info div p{ margin: 0; } .stats-completed{ margin: 9px 5px 0; font-size: 12px; bottom: 0; color: rgb(255, 255, 255, .8); } #stats-title{ display: flex; align-items: center; justify-content: space-between; } #stats-title>div{ font-size: 15px; margin-left: 8px; cursor: pointer; } #stats-title>div>div{ padding: 0 5px; } #linesplit-right { right: -7.5px; transform: translateY(-50%); top: 50%; } #linesplit-bottom { bottom: -7.5px; transform: translateX(-50%); left: 50%; } #linesplit-left { left: -7.5px; transform: translateY(-50%); top: 50%; } .fuckAds{ transform: translateX(9999%) !important; }`;
  3433. styles.innerHTML = `.fsfb-update-swal .cancel{ background-color: #29b962 !important; } .fsfb-update-swal button:hover{ opacity: 75%; } a.fsfb-curser-anchor{ color: #8CEFFF; } a.fsfb-curser-anchor:hover{ opacity: 70%; } select.fsfb-changelang{ background: #a8a8a833; border-radius: 3px; border: none; height: 20px; color: #ffffffad; margin: 2px 0 0 0; } select.fsfb-changelang:focus-visible{ outline: none; } select.fsfb-changelang option{ background: #222; } #fsfb-wearsloaded{ display: none; width: 0; height: 0; } #fsfb-skinsearch{ border: 1px solid #2e6da4; background-color: #222328; font-size: 17px; border-radius: 4px; width: 100%; padding: 4px 4px 4px 8px; margin: 4px 0; color: white; } #stats-table td{ font-size: 17px; } #stats-table label{ padding-right: 10px; margin: 0 6px 0 2px; } #stats-table input{ transform: scale(1.8); margin: 5px 8px 5px 5px; } #friendRejectAll{ color: #ff4000 !important; } ${inventorySingleRow && ` #inventory{ display: flex; position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%); } .fsfb-inventories { position: initial !important; transform: initial !important; }`} div.fsfb-slider{ display: flex; align-items: center; } input[type="range"].fsfb-slider{ width: 58px; display: inline; position: absolute; right: 5px; } #fsfb-minionNuker img{ margin-top: 2px; } .fsfb-shown{ display: block !important; } .fsfb-hidden{ display: none !important; } #fsfb-quickbuy{ justify-content: center; align-items: center; } #fsfb-quickbuy-img{ height: 80%; } .fsfb-bug-modal>div>div{ -webkit-box-shadow: 0 5px 15px rgb(0 0 0 / 50%); background: linear-gradient(to bottom,#3b414e 0,#302f33 100%); border: 3px solid #232630; } .close.fsfb-btn{ position: absolute; right: 10px; top: 3px; font-size: 60px; } .fsfb-modal-body>div{ color: ffffffb0; margin: 10px 20px; } .fsfb-modal-body>table{ width: 90%; margin: 10px 20px; } .fsfb-cmd-title{ color: white; white-space: nowrap; padding-right: 12px !important; } .fsfb-modal-body>table th{ color: white; } .fsfb-modal-body>table td{ padding: 4px 0; } .fsfb-modal-body>div>span{ color: white; } .fsfb-modal-body{ margin: 10px 10px; font-size: 20px; max-height: 600px; overflow-y: auto; } .fsfb-modal-title{ text-align: center; color: white; } .fsfb-hotkey{ background-color: #df901c; color: #fff; cursor: pointer; text-align: center; min-width: 40px; max-width: 60px; height: 18px; line-height: 18px; vertical-align: middle; border-radius: 9px; right: 5px; position: absolute; display: inline-block; padding: 0 5px; overflow: hidden; opacity: 1; } .fsfb-modal-body::-webkit-scrollbar-thumb { background-color: #57595b; border: 1px solid black; border-radius: 12px; } .fsfb-modal-body::-webkit-scrollbar { border: 1px solid black; background-color: #2523239e; width: 15px; border-radius: 12px; } .fsfb-modal-body::-webkit-scrollbar-track { -webkit-box-shadow: inset 0px 0px 2px 2px rgba(0, 0, 0, 0.75); box-shadow: inset 0px 0px 2px 2px rgba(0, 0, 0, 0.75); border-radius: 12px; } .fsfb-hotkey:hover { background-color: #f1a02d; } .fsfb-hotkey.selected{ background-color: #ff4; color: #444; } #fsfb-settings-main p{ margin: 0; display: inline-block; margin-left: 4px; } #fsfb-settings-main{ display: -ms-grid; display: grid; -ms-grid-columns: 50% 50%; grid-template-columns: 50% 50%; } #settingPage4::-webkit-scrollbar-thumb { background-color: #ff9800c2; border-radius: 12px; border: 1px #000000c2 solid; } #settingPage4::-webkit-scrollbar { border: 1px solid #00000085; background-color: #2523239e; width: 9px; border-radius: 12px; } #settingPage4{ display: none; max-height: 660px; overflow-x: hidden; } .padbot10{ padding-bottom: 10px; } #fsfb-slowfeedtimer, #fsfb-secdelay, #fsfb-firstdelay{ border: none; width: 40px; } select.fsfb-quickchange{ background: none; border: none; height: 20px; } select.fsfb-quickchange:focus-visible{ outline: none; } select.fsfb-quickchange option{ background: #222; } .fsfb-sect-ch label{ display: flex; align-items: center; } .fsfb-sect-ch label input{ margin: 0 2px 0 0; } #fsfb-sect-theme label input[type="color"]{ width: 14px; height: 14px; opacity: 0; border: none; background-color: white; margin: 0; cursor: pointer; } #fsfb-sect-theme label p{ min-width: 120px; margin-left: 5px; } #fsfb-sect-theme label div{ border-radius: 4px; border: 1px solid #ffffff29; } #fsfb-ximport-cont{ display: flex; justify-content: space-around; margin-top: 7px; } .fsfb-eximport{ background-color: #df901c; color: white; padding: 5px 17px; border-radius: 25px; cursor: pointer; } .hideMegaphone{ display: none !important; } .fsfb-fake{ padding: 0 0 0 43px; color: #cbff4e !important; } #fsfb-extra-info{ margin: 10% 0 0 90%; cursor: pointer; } #linesplit-markers div { background-color: transparent; height: 15px; aspect-ratio: 1; position: fixed; z-index: 999; border: 2px solid rgb(255 255 255 / 80%); border-radius: 50%; display: none; } #linesplit-top { top: -7.5px; transform: translateX(-50%); left: 50%; } #stats-container{ background: rgba(0,0,0,.5); top: 200px; position: absolute; border: 1px white solid; border-radius: 12px; color:white; left:30px; } #stats-main{ padding: 10px; } #stats-extra-info{ color: #00bbff; font-size: 15px; margin-bottom: 2px; display: block; } #stats-info div{ display:flex; } #stats-info div p{ margin: 0; } .stats-completed{ margin: 9px 5px 0; font-size: 12px; bottom: 0; color: rgb(255, 255, 255, .8); } #stats-title{ display: flex; align-items: center; justify-content: space-between; } #stats-title>div{ font-size: 15px; margin-left: 8px; cursor: pointer; } #stats-title>div>div{ padding: 0 5px; } #linesplit-right { right: -7.5px; transform: translateY(-50%); top: 50%; } #linesplit-bottom { bottom: -7.5px; transform: translateX(-50%); left: 50%; } #linesplit-left { left: -7.5px; transform: translateY(-50%); top: 50%; } .fuckAds{ transform: translateX(9999%) !important; } `
  3434.  
  3435. document.querySelector('body').append(styles);
  3436.  
  3437. const scripts = document.createElement('script');
  3438. scripts.innerHTML = `
  3439. function onlyNumberKey(e) {
  3440. const key = e.which ? e.which : e.keyCode;
  3441. return !(key > 31 && (key < 48 || key > 57));
  3442. }
  3443. `
  3444. let script_id = 446564 // main script
  3445. let version_timestamp = 1669243804226;
  3446. if (+GM_getValue("lastUpdateCheck", "0") + 864e5 <= Date.now() && typeof GM_xmlhttpRequest == 'function' && notifyNewUpdates) {
  3447. try {
  3448. GM_xmlhttpRequest({
  3449. method: "GET",
  3450. url: `https://gf.qytechs.cn/en/scripts/${script_id}/code?${Date.now()}`,
  3451. headers: {
  3452. 'Cache-Control': 'no-cache'
  3453. },
  3454. onload: function(xhrResponse) {
  3455. GM_setValue("lastUpdateCheck", String(Date.now()));
  3456. const rt = xhrResponse.responseText.replace(/&nbsp;?/gm, " ").replace(/&#x000A;/g, "\n").replace(/<li>/gm, "\n").replace(/<[^>]*>/gm, "");
  3457. if (+(rt?.match(/version_timestamp\s*=\s*([0-9]+)/)?.[1] ?? 0) > version_timestamp) {
  3458. let changelog = rt.match(/(?<=@changelog\s+)(?:\S).+$/gm)?.[0] ?? 'unable to find',
  3459. version = rt.match(/(?<=@version\s+)(?:\S).+$/gm)?.[0] ?? 'unable to find';
  3460. swal({
  3461. title: `<span style="color: #8CEFFF;">fsfb update!</span>`,
  3462. text: `<span style="color: #BBF6FF;">It appears there's a new update available for fsfb script. (version: ${version})</br>Changelog: ${changelog}</span>`,
  3463. type: "info",
  3464. confirmButtonColor: "#2cb7f7",
  3465. confirmButtonText: 'Install fsfb Update',
  3466. html: true,
  3467. focusCancel: true,
  3468. cancelButtonColor: "#29b962",
  3469. cancelButtonText: 'Install Auto-Updating fsfb',
  3470. showCancelButton: true,
  3471. customClass: 'fsfb-update-swal'
  3472. }, function(val) {
  3473. if(val){ // install new update
  3474. unsafeWindow.open(`https://gf.qytechs.cn/scripts/${script_id}/`);
  3475. } else { // install auto-updating
  3476. unsafeWindow.open(`https://gf.qytechs.cn/scripts/455326`);
  3477. }
  3478. });
  3479. }
  3480. }
  3481. });
  3482. } catch (err) {
  3483. console.error("An error occurred while checking for updates:\n" + err);
  3484. }
  3485. }
  3486.  
  3487. document.querySelector('body').append(scripts);
  3488. unsafeWindow.fsfbScriptsLoaded = true;
  3489. // $('#gameSettingsTab a')[0].click();
  3490. // $('#settingTab4')[0].click();
  3491. };
  3492.  
  3493.  
  3494.  
  3495. unsafeWindow.fsfbScriptsLoaded || unsafeWindow.fsfbEvListenerAdded ? alert('It appears fsfb scripts is already loaded. It\'s recommended to use only one script at a time.') : (document.readyState === "complete" || document.readyState === "interactive" ? setTimeout(afterLoaded, 0) : (document.addEventListener("DOMContentLoaded", afterLoaded), unsafeWindow.fsfbEvListenerAdded = true));

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址