fsfb scripts

a script for agma.io with features such as Copy Chat, Linesplit Toggle, Anti-AFK, Show Portal Mass, Change Food/Virus Color, and more!

目前为 2022-06-16 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name fsfb scripts
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.1
  5. // @description a script for agma.io with features such as Copy Chat, Linesplit Toggle, Anti-AFK, Show Portal Mass, Change Food/Virus Color, and more!
  6. // @author fishy & firebone
  7. // @match *://agma.io/*
  8. // @run-at document-start
  9. // @icon https://i.imgur.com/8AASK55.png
  10. // @license GPL-3.0-or-later
  11. // @grant unsafeWindow
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // ==/UserScript==
  15.  
  16. /* settings that you can't change in UI but might interfere with other scripts */
  17. let hideAds = true,
  18. improvedShop = true,
  19. extraBotPacks = true,
  20. rightClickCopyChat = true,
  21. rightClickCopyInfo = true,
  22. showRemainingAbilityTime = true,
  23. unlockFreeSkins = true,
  24. hoverShowSkinID = true,
  25. coinXPstats = true,
  26. quickBuy = false,
  27. whiteBorder4BlackCells = true;
  28.  
  29.  
  30. let settings = {
  31. hotkeys: [
  32. {title: "Shoot 7 Ejected", id: "fsfb-key7Feed", key: 0, active: false},
  33. {title: "Linesplit Lock", id: "fsfb-linesplit", key: 0, active: false},
  34. {title: "Macro Split Bots", id: "fsfb-MacroSplitBots", key: 0, active: false},
  35. {title: "Hide UI", id: "fsfb-hideUI", key: 0, active: false}
  36. ],
  37. checkboxes: [
  38. {title: "Chat Copy/Cut/Paste", id: "fsfb-copycutpaste", active: false},
  39. {title: "Anti-AFK", id: "fsfb-antiAFK", active: false},
  40. {title: "Anti-Invis", id: "fsfb-anticloak", active: false},
  41. {title: "Linesplit Toggle", id: "fsfb-linetoggle", active: false},
  42. {title: "Change Page Title", id: "fsfb-changetitle", active: false},
  43. {title: "Hide Shouts", id: "fsfb-hideshouts", active: false},
  44. {title: "Hold To Spam Rec", id: "fsfb-reco", active: false},
  45. {title: "Hold To Spam Speed", id: "fsfb-speed", active: false},
  46. {title: "Show Portal Mass", id: "fsfb-portalmass", active: false}
  47. ],
  48. slowFeed: [
  49. {title: "Toggle Feed", id: "fsfb-slowFeed", key: 0, active: false},
  50. {title: "Feed Speed", id: "fsfb-slowfeedtimer", val: 100, active: false}
  51. ],
  52. quickSettings: [
  53. {id: "fsfb-quick-hotkey1", id1: "fsfb-quick-select1", set: "cSkins", key: 0, active: false},
  54. {id: "fsfb-quick-hotkey2", id1: "fsfb-quick-select2", set: "cWearables", key: 0, active: false},
  55. {id: "fsfb-quick-hotkey3", id1: "fsfb-quick-select3", set: "cFood", key: 0, active: false},
  56. {id: "fsfb-quick-hotkey4", id1: "fsfb-quick-select4", set: "cBubbleCells", key: 0, active: false}
  57. ],
  58. theme: [
  59. {title: "Food Color", id: "fsfb-check-foodcolor", id1: "fsfb-color-foodcolor", color: "#FFFFFF", active: false},
  60. {title: "Virus Color", id: "fsfb-check-viruscolor", id1: "fsfb-color-viruscolor", color: "#00ff00", active: false},
  61. {title: "Virus Stroke", id: "fsfb-check-virusstroke", id1: "fsfb-color-virusstroke", color: "#00ff00", active: false},
  62. {title: "Mothercell Color", id: "fsfb-check-msColor", id1: "fsfb-color-msColor", color: "#cd5564", active: false},
  63. {title: "Mothercell Stroke", id: "fsfb-check-msStroke", id1: "fsfb-color-msStroke", color: "#cd5564", active: false}
  64. ],
  65. theme_boxes: [
  66. {title: "Fancy Bubble Cells", id: "fsfb-bublecell", active: false},
  67. {title: "Show Player Mass", id: "fsfb-showmass", active: false},
  68. {title: "Spiked Cells", id: "fsfb-spikedcells", active: false}
  69. ],
  70. export_import: [
  71. {title: "Game Settings", id: "fsfb-game-settings", active: false},
  72. {title: "Game Controls", id: "fsfb-game-controls", active: false},
  73. {title: "Script Settings", id: "fsfb-script-settings", active: false},
  74. {title: "Script Theme", id: "fsfb-theme-settings", active: false}
  75. ]
  76. }
  77.  
  78. 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 }
  79. 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: "'"};
  80.  
  81. const set = (name, obj) => typeof GM_setValue != "function" ? localStorage.setItem(name, JSON.stringify(obj)) : GM_setValue(name, obj),
  82. 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);
  83.  
  84. if(typeof unsafeWindow === 'undefined') unsafeWindow = window;
  85. const saveSettings = () => set("fsfb-scripts", settings);
  86. const getSettings = () => {
  87. let settingsPrev = get("fsfb-scripts", settings);
  88. for(let i in settingsPrev) {
  89. for(let j in settingsPrev[i]) {
  90. for(let x in settings){
  91. for(let y in settings[x]){
  92. if(settingsPrev[i][j].id == settings[x][y].id) settings[x][y] = settingsPrev[i][j];
  93. }
  94. }
  95. }
  96. }
  97. }
  98. getSettings();
  99.  
  100. const getKey = id => {
  101. const name = $("#" + id).text();
  102. return txtMappings[name] == null ? name.charCodeAt(0) : txtMappings[name];
  103. }
  104.  
  105. const getName = key => keyCodeMappings[key] == null ? String.fromCharCode(key) : keyCodeMappings[key];
  106.  
  107. const levelSum = lvl => lvl * (lvl - 1) / 2,
  108. range = arr => Math.max(...arr) - Math.min(...arr),
  109. sigma = arr => arr.reduce((a, b) => a + b),
  110. mean = arr => sigma(arr) / arr.length,
  111. variance = arr => arr.reduce((a, b) => a + (b - mean(arr)) ** 2, 0) / arr.length,
  112. standardDeviation = arr => Math.sqrt(variance(arr)),
  113. ascending = arr => arr.sort((a, b) => a - b),
  114. getIQR = arr => quartile(arr, .75) - quartile(arr, .25),
  115. round = (num, places) => Math.round(num * +("1e" + places)) / +("1e" + places);
  116. const median = arr => {
  117. const mid = ~~(arr.length / 2),
  118. asc = ascending(arr);
  119. return arr.length % 2 !== 0 ? asc[mid] : (asc[mid - 1] + asc[mid]) / 2;
  120. }
  121. const getProperty = (arr, property) => {
  122. let newArr = [];
  123. for (let i of arr) newArr.push(i[property]);
  124. return newArr;
  125. }
  126. const quartile = (arr, q) => {
  127. const sorted = ascending(arr),
  128. pos = (sorted.length - 1) * q,
  129. base = ~~pos,
  130. rest = pos - base;
  131. return sorted[base + 1] !== null ? sorted[base] + rest * (sorted[base + 1] - sorted[base]) : sorted[base]
  132. }
  133. const checkOutliers = (arr, returnOutliers) => {
  134. let nonOutliers = [], outliers = [];
  135. const IQR = getIQR(arr),
  136. Q1 = quartile(arr, .25),
  137. Q3 = quartile(arr, .75);
  138. for (let i of arr) i < Q1 - 1.5 * IQR || i > Q3 + 1.5 * IQR ? outliers.push(i) : nonOutliers.push(i);
  139. return returnOutliers ? outliers : nonOutliers;
  140. }
  141. const msToTime = ms => {
  142. let dur = ~~(ms / 1e3),
  143. sec = ~~(dur % 60),
  144. min = ~~((dur / 60) % 60),
  145. hr = ~~(dur / 3600);
  146. return (hr < 10 ? "0" + hr : hr) + ":" + (min < 10 ? "0" + min : min) + ":" + (sec < 10 ? "0" + sec : sec);
  147. }
  148. const changeTitle = title => {
  149. if(document.title != title) document.title = title;
  150. };
  151.  
  152. if(!settings.checkboxes[5].active) changeTitle('Agma.io');
  153.  
  154. if(unlockFreeSkins){
  155. const ytSkins = ["", 56, 1657, 2281, 2282, 2297, 2331, 2529, 2626, 2683, 2816, 2832];
  156. for (let i of ytSkins) localStorage.setItem('ytSkin' + i, '1');
  157. localStorage.setItem('fbSkin', '1');
  158. }
  159.  
  160. ['paste', 'copy', 'cut'].forEach(a => {
  161. unsafeWindow.addEventListener(a, e => {
  162. if(!$('#fsfb-copycutpaste').is(':checked')) return;
  163. e.stopImmediatePropagation();
  164. }, true)
  165. });
  166.  
  167. const afterLoaded = () => {
  168. // attempt to prevent the script from being active on subpages of agma.io
  169. if($('#friendResizer').length < 1 || $('#megaholder').length < 1 || $('#preroll').length < 1) return;
  170.  
  171. $('.setting-tablink').css({'width' : '30%', "font-size" : "font-size: calc(0.3vw + 7.5px);"});
  172. $('#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>`);
  173. $('#settingPage3').after(`<div id="settingPage4" class="setting-tabcontent"><div class="row"><div class="col-md-10 col-md-offset-1 stng" id="fsfb-settings-main" style="padding:0"><div id="fsfb-settings-left"><section id="fsfb-sect-checkbox" class="padbot10 fsfb-sect-ch"></section><section id="fsfb-sect-theme" class="fsfb-sect-ch"></section></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-quickSettings" class="padbot10"></section><section id="fsfb-sect-imexport" class="fsfb-sect-ch"></section></div></div></div></div>`);
  174. $('.container').eq(0).css("max-width", "1250px");
  175. $('#fsfb-sect-checkbox').append(`<p class="hotkey-paragraph">Script Features</p>`);
  176.  
  177. // add checkbox HTML
  178. for(let i of settings.checkboxes){
  179. $('#fsfb-sect-checkbox').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  180. $( "#" + i.id).change(function() {
  181. changeSettings(this.id, $(this).is(':checked'));
  182. });
  183. }
  184.  
  185. // add import/export HTML
  186. $('#fsfb-sect-imexport').append(`<p class="hotkey-paragraph">Import/Export</p>`);
  187. for(let i of settings.export_import){
  188. $('#fsfb-sect-imexport').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  189. $( "#" + i.id).change(function() {
  190. changeSettings(this.id, $(this).is(':checked'));
  191. });
  192. }
  193. $('#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>`);
  194.  
  195. $('#fsfb-sect-theme').append(`<p class="hotkey-paragraph">Game Theme</p`)
  196. for(let i of settings.theme){
  197. $('#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>`);
  198. $( "#" + i.id).change(function() {
  199. changeSettings(this.id, $(this).is(':checked'));
  200. });
  201. $( "#" + i.id1).change(function() {
  202. changeSettings(this.id, this.value);
  203. $(this).parent().css('background-color', this.value); // bcs the regular [input="color"] looks rly shit, change color of overlayed div instead
  204. });
  205. }
  206. for(let i of settings.theme_boxes){
  207. $('#fsfb-sect-theme').append(`<label><input id="${i.id}" type="checkbox"><p> ${i.title} </p></label>`);
  208. $( "#" + i.id).change(function() {
  209. changeSettings(this.id, $(this).is(':checked'));
  210. });
  211. }
  212.  
  213. $('#fsfb-sect-hotkeys').append(`<p class="hotkey-paragraph">Script Hotkeys</p>`);
  214.  
  215. const checkHotkeyClicked = (e, thing) => {
  216. if (e.target.id == thing.id && !thing.active){
  217. $('#' + thing.id).addClass('selected');
  218. thing.active = true;
  219. keysChanging = true; // prevent features from triggering when setting hotkey
  220. } else {
  221. thing.active = false;
  222. $('#' + thing.id).removeClass('selected');
  223. }
  224. }
  225. const checkNewHotkey = (e, thing) => {
  226. if (!thing.active) return;
  227. thing.key = e.keyCode;
  228. $('#' + thing.id).text(getName(e.keyCode));
  229. $('#' + thing.id).removeClass('selected');
  230. thing.active = false;
  231. saveSettings();
  232. e.preventDefault();
  233. }
  234.  
  235. const checkPowerupClicked = e => {
  236. if(!quickBuying || $(e.target).attr('class') == 'purchase-btn confirmation') return;
  237. let pwID;
  238. switch (e.target.id) {
  239. case 'invWall':
  240. pwID = 33;
  241. break;
  242. case 'invAntiFreeze':
  243. pwID = 35;
  244. break;
  245. case 'invAntiRecombine':
  246. pwID = 34;
  247. break;
  248. case 'invShield':
  249. pwID = 38;
  250. break;
  251. case 'invFrozenVirus':
  252. pwID = 36;
  253. break;
  254. case 'invRecombine':
  255. pwID = 1;
  256. break;
  257. case 'invSpeed':
  258. pwID = 2;
  259. break;
  260. case 'invGrowth':
  261. pwID = 6;
  262. break;
  263. case 'invSpawnVirus':
  264. pwID = 7;
  265. break;
  266. case 'invSpawnMothercell':
  267. pwID = 8;
  268. break;
  269. case 'invSpawnPortal':
  270. pwID = 9;
  271. break;
  272. case 'invSpawnGoldOre':
  273. pwID = 10;
  274. break;
  275. case 'invFreeze':
  276. pwID = 5;
  277. break;
  278. case 'inv360Shot':
  279. pwID = 30;
  280. break;
  281. case 'inv360Shot':
  282. pwID = 30;
  283. break;
  284. case 'fsfb-quickbuy-img':
  285. quickBuying = true;
  286. return;
  287. default:
  288. quickBuying = false;
  289. $('.inventory-box').removeClass('fsfb-shown').find('p').css('display', 'block');
  290. $('#fsfb-quickbuy').removeClass('activatedInv')
  291. curserMsg('Quick buy deactivated.', 'red');
  292. return;
  293. }
  294. $('.purchase-btn.confirmation[item="' + pwID + '"]')[0].click();
  295. $('.inventory-box').removeClass('fsfb-shown').find('p').css('display', 'block');
  296. $('#fsfb-quickbuy').removeClass('activatedInv')
  297. quickBuying = false;
  298. }
  299.  
  300. const slowfeedhotkey = settings.slowFeed[0];
  301. for(let i of settings.hotkeys){
  302. $('#fsfb-sect-hotkeys').append(`<br><p>${i.title}</p><div id="${i.id}" class="fsfb-hotkey"></div>`);
  303. $('#' + i.id).on('contextmenu', e => {
  304. $('#' + i.id).text('');
  305. i.key = 0;
  306. saveSettings();
  307. e.preventDefault();
  308. });
  309. }
  310.  
  311. const typing = () => $('input').is(':focus') || $('textarea').is(':focus');
  312. const press = key => {
  313. unsafeWindow.onkeydown({ keyCode: key });
  314. unsafeWindow.onkeyup({ keyCode: key });
  315. }
  316. let slowfeeding = !1, linesplitting = !1, hiddenUI = !1, splittingbots = !1, spamRec = !1, spamSpeed = !1;
  317.  
  318. const pressed = e => {
  319. const key = e.which ? e.which : e.keyCode;
  320. if(typing() || keysChanging) return;
  321. if(key == settings.hotkeys[0].key){ // 7 feed
  322. let i = 1;
  323. let interval = setInterval(() => {
  324. press(getKey("keyMacroFeed"));
  325. if(++i > 7) clearInterval(interval);
  326. }, 70);
  327. e.preventDefault();
  328. }
  329. if(key == settings.hotkeys[1].key && !$('#fsfb-linetoggle').is(':checked')){ // linesplit lock
  330. linesplitting = true;
  331. linesplit();
  332. $("#linesplit-markers div").css('display', 'block');
  333. e.preventDefault();
  334. }
  335. if(key == settings.hotkeys[1].key && $('#fsfb-linetoggle').is(':checked')){ // linesplit lock
  336. linesplitting = !linesplitting;
  337. if(linesplitting){
  338. $("#linesplit-markers div").css('display', 'block');
  339. linesplit();
  340. } else $("#linesplit-markers div").css('display', 'none');
  341. e.preventDefault();
  342. }
  343. if(key == settings.hotkeys[2].key){ // macro split bots
  344. splittingbots = true;
  345. const splittingBots = () => {
  346. if(!splittingbots) return;
  347. press(getKey("keySplitBots"));
  348. setTimeout(splittingBots, 40);
  349. }
  350. splittingBots();
  351. e.preventDefault();
  352. }
  353.  
  354. if(key == settings.hotkeys[3].key){ // hide ui
  355. hiddenUI = !hiddenUI;
  356. hiddenUI ? _replaceCSS('hideUI-css', '.hideUI{ display: none !important;}') : _replaceCSS('hideUI-css', ''); // replacing CSS bcs class is less likely to be overriden
  357. e.preventDefault();
  358. }
  359. if(key == settings.slowFeed[0].key){ // toggle feed
  360. slowfeeding = !slowfeeding;
  361. const feeding = () => {
  362. if(!slowfeeding) return;
  363. press(getKey("keyMacroFeed"));
  364. setTimeout(feeding, settings.slowFeed[1].val);
  365. }
  366. feeding();
  367. e.preventDefault();
  368. }
  369. if(key == settings.quickSettings[0].key){ // quick settings 1
  370. $('#' + settings.quickSettings[0].set).unbind().click();
  371. e.preventDefault();
  372. }
  373. if(key == settings.quickSettings[1].key){ // quick settings 2
  374. $('#' + settings.quickSettings[1].set).unbind().click();
  375. e.preventDefault();
  376. }
  377. if(key == settings.quickSettings[2].key){ // quick settings 3
  378. $('#' + settings.quickSettings[2].set).unbind().click();
  379. e.preventDefault();
  380. }
  381. if(key == settings.quickSettings[3].key){ // quick settings 4
  382. $('#' + settings.quickSettings[3].set).unbind().click();
  383. e.preventDefault();
  384. }
  385. if($('#fsfb-reco').is(':checked') && key == getKey("keyRecombine")){
  386. spamRec = true;
  387. const spammingRec = () => {
  388. if(!spamRec) return;
  389. press(getKey("keyRecombine"));
  390. setTimeout(spammingRec, 10);
  391. }
  392. spammingRec();
  393. e.preventDefault();
  394. }
  395. if($('#fsfb-speed').is(':checked') && key == getKey("keySpeed")){
  396. spamSpeed = true;
  397. const spammingSpeed = () => {
  398. if(!spamSpeed) return;
  399. press(getKey("keySpeed"));
  400. setTimeout(spammingSpeed, 10);
  401. }
  402. spammingSpeed();
  403. e.preventDefault();
  404. }
  405. }
  406.  
  407. const released = key => {
  408. if(typing() || keysChanging) return;
  409. if(key == settings.hotkeys[2].key) splittingbots = false; // macro split bots
  410. if(key == settings.hotkeys[1].key && !$('#fsfb-linetoggle').is(':checked')){ // linesplit lock
  411. linesplitting = false;
  412. $("#linesplit-markers div").css('display', 'none');
  413. }
  414. if(key == getKey("keyRecombine")) spamRec = false;
  415. if(key == getKey("keySpeed")) spamSpeed = false;
  416. }
  417.  
  418. const changeSettings = (ID, a) => {
  419. for(let i of settings.checkboxes) if(i.id == ID) i.active = a;
  420. for(let i of settings.export_import) if(i.id == ID) i.active = a;
  421. for(let i of settings.theme_boxes) if(i.id == ID) i.active = a;
  422. for(let i of settings.theme){
  423. if(i.id == ID) i.active = a
  424. if(i.id1 == ID) i.color = a;
  425. }
  426. customCells = $("#fsfb-sect-theme>label>input").is(":checked") || $("#fsfb-portalmass").is(":checked") || $("#fsfb-anticloak").is(":checked");
  427. if(ID == "fsfb-hideshouts") a ? $('#megaholder').addClass('hideMegaphone') : $('#megaholder').removeClass('hideMegaphone');
  428. saveSettings();
  429. }
  430. // add slowfeed HTML
  431. $('#fsfb-sect-slowfeed').append(`<p class="hotkey-paragraph">Slow-Feed</p>`);
  432. $('#fsfb-sect-slowfeed').append(`<br><p>${slowfeedhotkey.title}</p><div id="${slowfeedhotkey.id}" class="fsfb-hotkey"></div>`);
  433. $('#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>`);
  434.  
  435. $('#' + slowfeedhotkey.id).on('contextmenu', e => {
  436. $('#' + slowfeedhotkey.id).text('');
  437. slowfeedhotkey.key = 0;
  438. saveSettings();
  439. e.preventDefault();
  440. });
  441.  
  442. document.getElementById(settings.slowFeed[1].id).addEventListener("keypress", function(e){
  443. setTimeout(() => { // goes too fast or smth
  444. settings.slowFeed[1].val = +$('#' + settings.slowFeed[1].id).val();
  445. if($('#' + settings.slowFeed[1].id).val() == "") settings.slowFeed[1].val = 0;
  446. saveSettings();
  447. }, 5);
  448. });
  449.  
  450. // add quick settings HTML
  451. $('#fsfb-sect-quickSettings').append(`<p class="hotkey-paragraph">Quick Settings</p>`);
  452.  
  453. for(let i of settings.quickSettings){
  454. $('#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></select><div id="${i.id}" class="fsfb-hotkey"></div>`);
  455. $('#' + i.id).on('contextmenu', e =>{
  456. $('#' + i.id).text('');
  457. i.key = 0;
  458. saveSettings();
  459. e.preventDefault();
  460. });
  461. };
  462.  
  463. $('.fsfb-quickchange').on("change", function(){
  464. settings.quickSettings[+this.id.replace('fsfb-quick-select', '') - 1].set = this.value;
  465. saveSettings();
  466. });
  467.  
  468. // add event listeners
  469. let mosX, mosY;
  470. let keys = {};
  471. let keysChanging = false;
  472.  
  473. $(document).on('click', e => {
  474. for(let i = 0; i < settings.hotkeys.length; i++) checkHotkeyClicked(e, settings.hotkeys[i]);
  475. for(let i = 0; i < settings.quickSettings.length; i++) checkHotkeyClicked(e, settings.quickSettings[i]);
  476. checkHotkeyClicked(e, settings.slowFeed[0]);
  477. checkPowerupClicked(e);
  478. })
  479. .on('mousemove', e => {
  480. if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
  481. ({clientX: mosX, clientY: mosY} = e);
  482. if (linesplitting) linesplit();
  483. })
  484. .on("keydown", e => {
  485. if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
  486. for(let i = 0; i < settings.hotkeys.length; i++) checkNewHotkey(e, settings.hotkeys[i]);
  487. for(let i = 0; i < settings.quickSettings.length; i++) checkNewHotkey(e, settings.quickSettings[i]);
  488. checkNewHotkey(e, settings.slowFeed[0]);
  489. if (!(e.keyCode in keys)){
  490. keys[e.keyCode] = !0;
  491. pressed(e);
  492. }
  493. keysChanging = false;
  494. })
  495. .on("keyup", e => {
  496. if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
  497. delete keys[e.keyCode];
  498. released(e.keyCode);
  499. });
  500.  
  501. $('#fsfb-export-btn').on('click', () => {
  502. let script_settings = {},
  503. script_themes = {};
  504. for (let i in settings) { // put the settings & themes into different objects
  505. if (i == "theme" || i == "theme_boxes") script_themes[i] = settings[i];
  506. else if(i == "export_import") {}
  507. else script_settings[i] = settings[i];
  508. }
  509. let obj = {
  510. selected: {
  511. game_settings: $('#fsfb-game-settings').is(':checked'),
  512. game_controls: $('#fsfb-game-controls').is(':checked'),
  513. script_settings: $('#fsfb-script-settings').is(':checked'),
  514. script_theme: $('#fsfb-theme-settings').is(':checked')
  515. },
  516. game_settings: localStorage.settings,
  517. game_controls: localStorage.hotkeys,
  518. script_settings: script_settings,
  519. script_theme: script_themes
  520. }
  521. // if the setting is turned off, then dont need to export it - set it to null
  522. if(!obj.selected.game_settings) obj.game_settings = null;
  523. if(!obj.selected.game_controls) obj.game_controls = null;
  524. if(!obj.selected.script_settings) obj.script_settings = null;
  525. if(!obj.selected.script_theme) obj.script_theme = null;
  526.  
  527. const a = document.createElement('a'),
  528. file = new Blob([JSON.stringify(obj)], {type: "text/plain"});
  529. a.href = URL.createObjectURL(file), a.download = "fishy & firebone script settings", a.click(), URL.revokeObjectURL(a.href); // download a txt file of exported settings
  530. });
  531.  
  532. $('#fsfb-import-btn').on('click', () => {
  533. swal({
  534. title: "Import Settings",
  535. text: "Add your exported settings below and press OK to continue.",
  536. type: "input",
  537. showCancelButton: true,
  538. closeOnConfirm: false,
  539. animation: "slide-from-top",
  540. inputPlaceholder: "Paste exported settings here"
  541. },
  542. function(inputVal){
  543. if (inputVal == null) return false;
  544. if (inputVal == "") {
  545. swal.showInputError("Please don't leave the input empty!");
  546. return false
  547. }
  548. try {
  549. let val = JSON.parse(inputVal);
  550. if(val.selected.script_settings && $('#fsfb-script-settings').is(':checked')){
  551. for (let i in val.script_settings) {
  552. for (let j in val.script_settings[i]) {
  553. for(let x in settings){
  554. for(let y in settings[x]){
  555. if(val.script_settings[i][j].id == settings[x][y].id) settings[x][y] = val.script_settings[i][j];
  556. }
  557. }
  558. }
  559. }
  560. }
  561. if(val.selected.script_theme && $('#fsfb-theme-settings').is(':checked')){
  562. for (let i in val.script_theme) {
  563. for (let j in val.script_theme[i]) {
  564. for(let x in settings){
  565. for(let y in settings[x]){
  566. if(val.script_theme[i][j].id == settings[x][y].id) settings[x][y] = val.script_theme[i][j];
  567. }
  568. }
  569. }
  570. }
  571. }
  572. if(val.selected.game_settings && $('#fsfb-game-settings').is(':checked')) localStorage.setItem('settings', val.game_settings);
  573. if(val.selected.game_controls && $('#fsfb-game-controls').is(':checked')) localStorage.setItem('hotkeys', val.game_controls);
  574. // if any agma controls were imported, need to refresh bc it's set using localstorage
  575. if((val.selected.game_settings && $('#fsfb-game-settings').is(':checked')) || (val.selected.game_controls && $('#fsfb-game-controls').is(':checked'))){
  576. swal({
  577. title: "Please Refresh!",
  578. text: "Refreshing the page is required for your imported agma settings to take effect",
  579. type: "warning",
  580. });
  581. } else swal({ title: "Settings Successfully Imported!", type: "success" });
  582. saveSettings();
  583. updateScriptSettingsUI();
  584.  
  585. } catch (error){
  586. swal({
  587. title: "Something went wrong!",
  588. text: "Please make sure you've entered in valid settings",
  589. type: "error"
  590. });
  591. }
  592. });
  593.  
  594. });
  595.  
  596. $('#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>`)
  597. // PUT MODAL HERE
  598.  
  599.  
  600. localStorage.ad_l_time = "9e99"; // smth like time since last ad
  601. $('[id^="agma-io_"], [id^="adWrapper"], #preroll').addClass("fuckAds"); // move ads way off the screen
  602.  
  603. let width = unsafeWindow.innerWidth, height = unsafeWindow.innerHeight,
  604. points = [{n: "#linesplit-top", x: width / 2, y: 0, nx: width / 2, ny: -10e6}, {n: "#linesplit-bottom", x: width / 2, y: height, nx: width / 2, ny: 10e6}, {n: "#linesplit-left", x: 0, y: height / 2, nx: -10e6, ny: height / 2}, {n: "#linesplit-right", x: width, y: height / 2, nx: 10e6, ny: height / 2}];
  605.  
  606. $(unsafeWindow).on('resize', function(){
  607. width = unsafeWindow.innerWidth, height = unsafeWindow.innerHeight;
  608. points = [{n: "#linesplit-top", x: width / 2, y: 0, nx: width / 2, ny: -10e6}, {n: "#linesplit-bottom", x: width / 2, y: height, nx: width / 2, ny: 10e6}, {n: "#linesplit-left", x: 0, y: height / 2, nx: -10e6, ny: height / 2}, {n: "#linesplit-right", x: width, y: height / 2, nx: 10e6, ny: height / 2}];
  609. });
  610.  
  611. let pointMove;
  612. const linesplit = () => {
  613. if(!linesplitting) return;
  614. const distance = p => Math.sqrt(Math.pow(mosX - p.x, 2) + Math.pow(mosY - p.y, 2));
  615. let closest = points.reduce((a, b) => distance(a) < distance(b) ? a : b);
  616. for (let i = 0; i < points.length; i++) {
  617. if (closest.x == points[i].x && closest.y == points[i].y) {
  618. pointMove = {nx: points[i].nx, ny: points[i].ny}; // set var to the closest point so antiafk can access it and won't mess the linesplit up
  619. $('canvas').trigger($.Event('mousemove', {
  620. clientX: points[i].nx,
  621. clientY: points[i].ny
  622. }));
  623. }
  624. }
  625. $("#linesplit-markers div").css('background-color', 'transparent');
  626. $(closest.n).css('background-color', '#e25615');
  627. }
  628.  
  629. let confBtns = document.getElementsByClassName('purchase-btn confirmation');
  630. const btnsArr = Array.from(document.getElementsByClassName('purchase-btn confirmation'));
  631.  
  632. const changeHTML = (index, price, id, name) => {
  633. setTimeout(() => {
  634. const amtDropdown = document.getElementById('shopAmountDropdown')
  635. 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.';
  636. const dropdownChange = () => {
  637. if (amtDropdown.value == "custom") return buyCstmAmt(price, id, name);
  638. let priceSum = (amtDropdown.value * price).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
  639. 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.';
  640. };
  641. try { // try to remove old event listener before adding new one so they don't stack (no idea if this does anything bcs the event listener might be deleted anyway)
  642. amtDropdown.removeEventListener('change', dropdownChange);
  643. amtDropdown.addEventListener('change', dropdownChange);
  644. }
  645. catch (error){
  646. amtDropdown.addEventListener('change', dropdownChange);
  647. };
  648. }, 30);
  649. };
  650. const buyCstmAmt = (price, id, name) => {
  651. swal({
  652. title: "Enter Purchase Amount",
  653. text: "<span>How many <b>" + name + "</b> would you like to buy?</span>",
  654. html: true,
  655. type: "input",
  656. showCancelButton: true,
  657. closeOnConfirm: false,
  658. inputPlaceholder: "Input amount here"
  659. },
  660. function(input){
  661. let pwAmt = +input,
  662. priceTotal = pwAmt * price;
  663. if (input == null) return false;
  664. if (input == "") {
  665. swal.showInputError("Please don't leave the input empty!");
  666. return false
  667. }
  668. if(!/^[0-9]+$/.test(input) || !(+input >>> 0 === parseFloat(+input))){
  669. swal.showInputError("Please only enter positive integers!");
  670. return false
  671. }
  672. swal({
  673. title: 'Confirm',
  674. 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>',
  675. html: true,
  676. type: 'warning',
  677. showCancelButton: true,
  678. closeOnConfirm: false,
  679. confirmButtonColor: '#4CAF50',
  680. confirmButtonText: 'Yes, confirm purchase',
  681. cancelButtonText: 'No, cancel purchase'
  682. }, function(confirmed){
  683. if(confirmed) buyPw(id, pwAmt);
  684. })
  685.  
  686. });
  687. };
  688. // buy a certain amount of powers including > 255
  689. const buyPw = (shopID, pwAmt) => {
  690. if (pwAmt < 1) return;
  691. if (pwAmt > 255) {
  692. for (let i = 0; i < ~~(pwAmt / 255); i++) setTimeout(() => purchaseItem(shopID, 255), 250 * i + 250);
  693. purchaseItem(shopID, pwAmt % 255);
  694. } else purchaseItem(shopID, pwAmt);
  695. };
  696.  
  697.  
  698. if(improvedShop){
  699. // add event listeners to dropdown buttons
  700. for (let i = 0; i < confBtns.length; i++) {
  701. confBtns[i].onclick = function () {
  702. setTimeout(() => {
  703. 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>';
  704. }, 5);
  705. }
  706. }
  707. // add event listeners to shop buttons
  708. for (let i = 0; i < 16; i++) {
  709. if (i != 10 && i != 11){
  710. btnsArr[i].addEventListener('click', () => {
  711. let index = i;
  712. changeHTML(index, btnsArr[index].getAttribute('price'), btnsArr[index].getAttribute('item'), $('.purchase-btn.confirmation[item="' + btnsArr[index].getAttribute('item') + '"]').prev().find('h3').eq(0).text());
  713. });
  714. }
  715. }
  716. }
  717.  
  718.  
  719. // add extra bot packs
  720. const newBots = [
  721. {title: "100 Bots", time: "24 HOURS", price: "700,000", id: 9},
  722. {title: "125 Bots", time: "48 HOURS", price: "800,000", id: 10},
  723. {title: "300 Bots", time: "24 HOURS", price: "900,000", id: 7},
  724. {title: "100 MASS Bots", time: "1 HOURS", price: "800,000", id: 8},
  725. {title: "300 Bots", time: "72 HOURS", price: "2,000,000", id: 11},
  726. {title: "100 MASS Bots", time: "24 HOURS", price: "2,500,000", id: 12}
  727. ]
  728.  
  729. const minul = document.getElementsByClassName('tab-container-section minion scroll')[0].children[0].children[0]
  730. $('.confirm-minion[item=7], .confirm-minion[item=8]').parent().hide();
  731. document.getElementById('extraTab').childNodes[0].setAttribute('onclick', '');
  732.  
  733. function createEl(i) {
  734. const botEl = document.createElement('li');
  735. botEl.setAttribute('class', 'masterTooltip extra-min');
  736. 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.');
  737. minul.appendChild(botEl);
  738. botEl.innerHTML = `
  739. <div class="title_prch">
  740. <img src="img/store/minions/minions_tab.png" width="70px" height="60px">
  741. <div class="minionDescription">
  742. <h4 style="font-size: 18px;">` + i.title + `</h4>
  743. <h3 style="margin-top:10px;color:white;"> ` + i.time + `</h3>
  744. </div> <span class="win-price">` + i.price + `</span>
  745. </div>
  746. <a href="#" price="` + i.price + `" item="` + i.id + `" class="purchase-btn2 confirm-minion extra-min">Buy</a>
  747. `;
  748. }
  749. if(extraBotPacks){
  750. for(let i of newBots) createEl(i);
  751. }
  752.  
  753. // $('.confirm-minion.extra-min').click(function(e) {
  754. // e.preventDefault(); // Prevent the href from redirecting directly
  755. // var linkURL = $(this).attr("href");
  756. // var priceK = $(this).attr("price");
  757. // var itemId = $(this).attr("item");
  758. // setMinionUi(true);
  759. // warnBeforeMinion(linkURL,priceK,itemId);
  760. // });
  761.  
  762. function warnBeforeMinion(linkURL, priceK,itemId) {
  763. swal({
  764. title: "Confirm",
  765. text: 'If you click "Buy", you will purchase these minions. They cost ' + priceK,
  766. type: "warning",
  767. showCancelButton: true,
  768. confirmButtonColor: "#4CAF50",
  769. confirmButtonText: "Yes, confirm purchase",
  770. cancelButtonText: "No, cancel purchase"
  771. }, function() {
  772. purchaseMinion(itemId);
  773. });
  774. }
  775.  
  776.  
  777. // context menu: click on skin -> skin ID to clipboard, click on name -> name to clipboard
  778. $('#contextPlayer').on('click', e => {
  779. if(!rightClickCopyInfo) return;
  780. if($('#contextPlayerSkin').width() + $('#contextPlayerSkin').offset().left + 5 > e.pageX){ // bcs #contextMenu event :dog:
  781. // cell was clicked
  782. if($('#contextPlayerSkin').css('background-image') != "none"){
  783. let skinID = $('#contextPlayerSkin').css('background-image').match(/\/skins\/[0-9]+/g)[0].substring(7); // get the skin image, then match only the skin's ID
  784. navigator.clipboard.writeText(skinID).then(function() {
  785. curserMsg('Skin ID of ' + skinID + ' was copied to your clipboard.', 'green');
  786. }, function() {
  787. curserMsg('Something went wrong. Nothing was added to your clipboard.', 'red');
  788. });
  789. } else if($('#contextPlayerSkin').css('background-color') == 'rgb(51, 51, 51)') curserMsg('No player selected. Nothing was added to your clipboard.', 'red');
  790. else curserMsg('No skin equipped. Nothing was added to your clipboard.', 'red');
  791. } else { // name was clicked
  792. if($('#contextPlayerSkin').css('background-color') == 'rgb(51, 51, 51)') curserMsg('No player selected. Nothing was added to your clipboard.', 'red');
  793. else {
  794. navigator.clipboard.writeText($('#contextPlayerName').text()).then(function() {
  795. curserMsg('Nickname: "' + $('#contextPlayerName').text() + '" was copied to your clipboard', 'green');
  796. }, function() {
  797. curserMsg("Something went wrong. Nothing was added to your clipboard.", "red");
  798. });
  799. }
  800. }
  801. });
  802.  
  803.  
  804. // little bar at the top of the screen that tells u stuff
  805. const curserMsg = (msg, color) => {
  806. if(color == "green") color = "rgb(0, 192, 0)";
  807. if(color == "red") color = "rgb(255, 0, 0)";
  808. if(color == "gray") color = "rgb(153, 153, 153)";
  809. $('#curser').text(msg).show().css('color', color)
  810. setTimeout(() => $('#curser').fadeOut(400), 4e3);
  811. }
  812.  
  813.  
  814. // ability time remaining
  815. let acc_abil = get("fsfb-abil", {});
  816. let lastClickedPrice, currentPrice, lastID;
  817.  
  818. const confirmClicked = () => {
  819. setTimeout(() => {
  820. if($('.sweet-alert.showSweetAlert.visible h2').text() != "Success!" || lastClickedPrice != currentPrice) return;
  821. acc_abil[currentUser] = {...acc_abil[currentUser], ...{[lastID] : Date.now()}};
  822. set("fsfb-abil", acc_abil);
  823. }, 1200);
  824. };
  825.  
  826. [18, 20, 22, 23].forEach(el => {
  827. if(!showRemainingAbilityTime) return;
  828. let h5 = $(`.purchase-btn.confirmation[item="${el}"]`).parents().eq(0).find('div h5');
  829. h5.clone().insertAfter(h5).addClass('fsfb-fake').hide();
  830. $(`.purchase-btn[item="${el}"]`).on('click', function () {
  831. lastClickedPrice = this.getAttribute('price');
  832. lastID = this.getAttribute('item');
  833. $('.confirm').attr('disabled', 'true'); // disable so user doesn't buy early - swal is slow to load text
  834. setTimeout(() => {
  835. $('.confirm').removeAttr('disabled');
  836. currentPrice = $('.sweet-alert.showSweetAlert.visible p').eq(0).text().replaceAll(/\D+/g, '');
  837. }, 750);
  838. setTimeout(() => $('.confirm')[0].addEventListener('click', confirmClicked), 500);
  839. })
  840. });
  841.  
  842. /* xp/coins statistics */
  843.  
  844. const updateTimeXP = 12e3; // xp bar updates every 12 seconds, don't change
  845. let lastMinXP = [];
  846. let lastHrXP = [];
  847. let currentPercent, currentLevel, currentXP, currentCoins;
  848.  
  849. unsafeWindow.logStatsScriptXP = !1;
  850. unsafeWindow.logStatsScriptCoins = !1;
  851. let scriptStart = Date.now();
  852. let accounts = {};
  853. const guiDisplay = "none";
  854. let coinsHTMLactive = false;
  855.  
  856. const updateTimeCoins = 6e3;
  857. let lastMinCoins = [];
  858. let lastHrCoins = [];
  859.  
  860. // add stats box html
  861. const statsBody = document.createElement('div');
  862. statsBody.setAttribute('id', 'stats-container');
  863. statsBody.style.display = guiDisplay;
  864. statsBody.innerHTML = `<div id="stats-main"><div id="stats-title"><title id="stats-extra-info">XP Stats - Updating Every 12s</title><div title="Reset Stats" class="fa fa-refresh" id="stats-reset-btn"></div></div><div><div><section id="stats-info"><div><p class="stats-label">Lvl Completed:</p><span>0/0</span></div><div><p class="stats-label">Remaining:</p><span>0</span></div><div><p class="stats-label">Projected (hr):</p><span>0</span></div><div><p class="stats-label">Last Hour:</p><span>0</span><span class="stats-completed">(0/60)</span></div><div><p class="stats-label">Last Minute:</p><span>0</span><span class="stats-completed">(0/5)</span></div><div><p class="stats-label">Last 12s:</p><span>0</span></div><div><p class="stats-label">Minute Mean:</p><span>0</span></div><div><p class="stats-label">Minute Median:</p><span>0</span></div><div><p class="stats-label">Minute Sd:</p><span>0</span></div><div style="display:none"><p class="stats-label">Latest Outliers:</p><span style="display:none">0, 200, 500</span></div><div><p class="stats-label">Session XP:</p><span>0</span></div><div><p class="stats-label">Session Length:</p><span>00:00:00</span></div><div><p class="stats-label" id="stats-sesh-length">Lifetime XP:</p><span>0</span></div><p style="font-size:0" id="last-updated">0</p></section></div></div>`;
  865. document.querySelector('body').append(statsBody)
  866.  
  867. $('#stats-reset-btn').on('click', () => {
  868. lastMinCoins = [];
  869. lastHrCoins = [];
  870. lastMinXP = [];
  871. lastHrXP = [];
  872. scriptStart = Date.now();
  873. accounts = {};
  874. updateUI();
  875. });
  876.  
  877.  
  878. class record {
  879. constructor(val, user, arr) {
  880. // this.type = this.findWhich(arr); // mostly for debugging
  881. this.id = this.getID(arr);
  882. this.user = user;
  883. this.amount = val;
  884. this.gained = this.calcGain(val, arr, this.id);
  885. // this.timestamp = Date.now(); // mostly for debugging
  886. }
  887. findWhich(arr) {
  888. return ((arr == lastMinXP || arr == lastHrXP ? "xp " : "coins " ) + (arr == lastHrXP || arr == lastHrCoins ? "hour" : "minute"));
  889. }
  890. calcGain(val, arr, id){
  891. const prevObj = arr[arr.length - 1];
  892. if(arr == lastHrXP && id == 1) return round(sigma(getProperty(lastMinXP, "gained")), 3);
  893. if(arr == lastHrCoins && id == 1) return round(sigma(getProperty(lastMinCoins, "gained")), 3);
  894. return prevObj && val - prevObj.amount >= 0 && prevObj.user == this.user && prevObj.amount != 0 ? round(val - prevObj.amount, 3) : 0;
  895. }
  896. getID(arr) {
  897. return arr[arr.length - 1] ? arr[arr.length - 1].id + 1 : 1;
  898. }
  899.  
  900. }
  901.  
  902. const xpInfo = () => {
  903. currentPercent = $('.progress-bar[role=progressbar]')[0].style.width.slice(0, -1) / 100;
  904. currentLevel = $('#level.user-level')[0].textContent;
  905. currentXP = (levelSum(currentLevel) + currentLevel * currentPercent) * 1e3;
  906. }
  907. const coinsInfo = () => {
  908. currentCoins = +($('#coinsDash')[0].textContent.replaceAll(' ', ''));
  909. }
  910.  
  911. const updateUI = () => {
  912. if(!coinsHTMLactive){
  913. const xpHr = lastHrXP.length,
  914. xpMin = lastMinXP.length,
  915. lvlCompleted = currentPercent ? String(Math.round(currentPercent * currentLevel * 1e3, 1)).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "/" + (currentLevel * 1e3).toLocaleString('en-US') : "0/0",
  916. xpRemaining = currentLevel ? Math.round(currentLevel * 1e3 - currentPercent * currentLevel * 1e3) : 0,
  917. projectedHr = xpHr > 0 ? Math.round(sigma(getProperty(lastHrXP.slice(-5), "gained")) * 12) : 0,
  918. lastHr = xpHr > 0 ? Math.round(sigma(getProperty(lastHrXP, "gained"))) : 0,
  919. lastHrCompleted = xpHr > 0 ? xpHr : 0,
  920. lastMin = xpMin > 0 ? Math.round(sigma(getProperty(lastMinXP, "gained"))) : 0,
  921. lastMinCompleted = xpMin > 0 ? xpMin : 0,
  922. lastMinTotal = 6e4 / updateTimeXP,
  923. last12sec = xpMin > 0 ? Math.round(lastMinXP[lastMinXP.length - 1].gained) : 0,
  924. xBar = xpHr > 0 ? Math.round(mean(getProperty(lastHrXP, "gained"))) : 0,
  925. xTilde = xpHr > 0 ? Math.round(median(getProperty(lastHrXP, "gained"))) : 0,
  926. standardDev = xpHr > 0 ? Math.round(standardDeviation(getProperty(lastHrXP, "gained"))) : 0,
  927. outliers = xpHr > 0 ? checkOutliers(getProperty(lastHrXP, "gained")) : 0,
  928. sessionXP = currentXP && accounts[currentUser] ? Math.round(currentXP - accounts[currentUser].xp): 0,
  929. sessionLength = msToTime(Date.now() - scriptStart),
  930. lifetimeXP = currentXP ? Math.round(currentXP) : 0,
  931. updateTime = updateTimeXP;
  932. 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>`;
  933. $('#stats-extra-info').text(`XP Stats - Updating Every ${updateTime / 1e3}s`).css('color', '#00bbff');
  934. } else {
  935. const coinsHr = lastHrCoins.length,
  936. coinsMin = lastMinCoins.length,
  937. coinGoalCompleted = currentCoins ? currentCoins.toLocaleString('en-US') + "/" + Math.ceil(currentCoins / 25e4).toLocaleString('en-US') * 25e4 : 0,
  938. coinsRemaining = currentCoins ? 25e4 - currentCoins % 25e4 : 0,
  939. projectedHr = coinsHr > 5 ? Math.round(sigma(getProperty(lastHrCoins.slice(-5), "gained")) * 12) : 0,
  940. lastHr = coinsHr > 0 ? Math.round(sigma(getProperty(lastHrCoins, "gained"))) : 0,
  941. lastHrCompleted = coinsHr > 0 ? coinsHr : 0,
  942. lastMin = coinsMin > 0 ? Math.round(sigma(getProperty(lastMinCoins, "gained"))) : 0,
  943. lastMinCompleted = coinsMin > 0 ? coinsMin : 0,
  944. lastMinTotal = 6e4 / updateTimeCoins,
  945. last12sec = coinsMin > 0 ? getProperty(lastHrCoins.slice(-5), "amount")[0] : 0,
  946. xBar = coinsHr > 0 ? Math.round(mean(getProperty(lastHrCoins, "gained"))) : 0,
  947. xTilde = coinsHr > 0 ? Math.round(median(getProperty(lastHrCoins, "gained"))) : 0,
  948. standardDev = coinsHr > 0 ? Math.round(standardDeviation(getProperty(lastHrCoins, "gained"))) : 0,
  949. outliers = coinsHr > 0 ? checkOutliers(getProperty(lastHrCoins, "gained")) : 0,
  950. sessionXP = currentCoins && accounts[currentUser] ? Math.round(currentCoins - accounts[currentUser].coins): 0,
  951. sessionLength = msToTime(Date.now() - scriptStart),
  952. updateTime = updateTimeCoins;
  953. 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>`;
  954. $('#stats-extra-info').text(`Coin Stats - Updating Every ${updateTime / 1e3}s`).css('color', '#ffc800');
  955. }
  956. }
  957.  
  958. $('.progress-bar').eq(1).parent()[0].style.cursor = "pointer";
  959.  
  960. if(coinXPstats){
  961. $('.progress-bar').eq(1).parent().on("click", () => {
  962. const statsCont = $('#stats-container');
  963. if(statsCont[0].style.display == "none") statsCont.fadeIn(400);
  964. else if(!coinsHTMLactive) statsCont.fadeOut(400);
  965. coinsHTMLactive = false;
  966. updateUI();
  967. }), [$(".dash-coin.dcTopBar").eq(0), $("#coinsTopLeft"), $(".progress-bar-coins").eq(1)].forEach(el => {
  968. el.on('click', (e) => {
  969. const statsCont = $('#stats-container');
  970. if(statsCont[0].style.display == "none") statsCont.fadeIn(400);
  971. else if(coinsHTMLactive) statsCont.fadeOut(400);
  972. coinsHTMLactive = true;
  973. updateUI();
  974. e.stopImmediatePropagation();
  975. });
  976. });
  977. }
  978.  
  979. // copy chat msgs
  980. if(rightClickCopyChat){
  981. $('#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>`)
  982. $('#contextCopyChat').on('click', () => {
  983. let arr = chatmsgs, str = "";
  984. if(arr != null){
  985. for(let i of arr.reverse()) str += ((new Date(i.time).toLocaleTimeString()) + " " + i.name + i.cache.oe + "\n")
  986. navigator.clipboard.writeText(str).then(function() {
  987. curserMsg('Chat messages were successfully added to clipboard.', 'green');
  988. }, function() {
  989. curserMsg('Something went wrong. Nothing was added to your clipboard.', 'red');
  990. });
  991. }
  992. $('#contextMenu').fadeOut(100);
  993. });
  994. }
  995.  
  996. let quickBuying = false;
  997. if(quickBuy){
  998. $('#inv360Shot').after(`<div class="inventory-box" id="fsfb-quickbuy"><img id="fsfb-quickbuy-img" height="80%" src="https://i.imgur.com/tAjoEyU.png"></div>`);
  999. $('#fsfb-quickbuy').on('click', function(){
  1000. quickBuying = !quickBuying;
  1001. if (quickBuying){
  1002. $(this).addClass('activatedInv');
  1003. curserMsg('Quick buy activated, click the powerup you would like to buy.', 'green');
  1004. $('.inventory-box').addClass('fsfb-shown').find('p').css('display', 'none');
  1005. $('#invCloak').removeClass('fsfb-shown');
  1006. } else {
  1007. $(this).removeClass('activatedInv');
  1008. curserMsg('Quick buy deactivated.', 'red');
  1009. $('.inventory-box').removeClass('fsfb-shown').find('p').css('display', 'block');
  1010. }
  1011. });
  1012.  
  1013. }
  1014. // add linesplit bubbles
  1015. $('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
  1016. // add class to all elements that need to behidden
  1017. $('#inventory, #chat, #minionUi, #infection_remain_zombie, #party, #challengeInfoBox, #gamemodeBox, #infoBox, #brGameContainer, #infGameContainer, #curser, #leaderboard, #minimap, #btnFriends, .innerBoxDashboard2, #fpsBox, #settingsBtn, #megaholder').addClass("hideUI");
  1018.  
  1019.  
  1020. let pushFn = Array.prototype.push,
  1021. spliceFn = Array.prototype.splice,
  1022. prop = null,
  1023. customCells = !0,
  1024. specialCells = !0,
  1025. customDc = !1,
  1026. avgFps = 0,
  1027. fpsArr = [],
  1028. chatmsgs, r1Portal = {
  1029. portal: null,
  1030. lastMass: 0,
  1031. lastMassChange: 0,
  1032. lastValue: 0,
  1033. room: 1
  1034. },
  1035. r2Portal = {
  1036. portal: null,
  1037. lastMass: 0,
  1038. lastMassChange: 0,
  1039. lastValue: 0,
  1040. room: 2
  1041. },
  1042. svInfo = {
  1043. default: {
  1044. ejPortalMass: 12,
  1045. r1Id: 1,
  1046. r2Id: 7,
  1047. r3Id: null,
  1048. r1StartMass: 500,
  1049. r2StartMass: 500
  1050. },
  1051. 2: {
  1052. ejPortalMass: 20,
  1053. r1Id: 1,
  1054. r2Id: 14,
  1055. r3Id: 6,
  1056. r1StartMass: 500,
  1057. r2StartMass: 500
  1058. },
  1059. 4: {
  1060. ejPortalMass: 20,
  1061. r1Id: 1,
  1062. r2Id: 14,
  1063. r3Id: 6,
  1064. r1StartMass: 500,
  1065. r2StartMass: 500
  1066. },
  1067. 6: {
  1068. ejPortalMass: 12,
  1069. r1Id: 1,
  1070. r2Id: 6,
  1071. r1StartMass: 500,
  1072. r2StartMass: 500
  1073. },
  1074. 13: {
  1075. ejPortalMass: 13.5,
  1076. r1Id: 12,
  1077. r2Id: 11,
  1078. r1StartMass: 500,
  1079. r2StartMass: 500
  1080. },
  1081. 14: {
  1082. ejPortalMass: 13.5,
  1083. r1Id: 12,
  1084. r2Id: 11,
  1085. r1StartMass: 500,
  1086. r2StartMass: 500
  1087. },
  1088. 38: {
  1089. ejPortalMass: 12,
  1090. r1Id: 1,
  1091. r2Id: 6,
  1092. r1StartMass: 500,
  1093. r2StartMass: 400
  1094. },
  1095. 39: {
  1096. ejPortalMass: 11,
  1097. r1Id: 1,
  1098. r2Id: 13,
  1099. r3Id: 7,
  1100. r1StartMass: 1004,
  1101. r2StartMass: 250,
  1102. r3StartMass: 1004
  1103. }
  1104. },
  1105. noPortalSvIdList = [11, 19, 23, 24, 37, 36, 31, 29, 40, 41, 16, 15, 21, 35, 12, 25, 42, 28, 32, 22, 18, 26, 30],
  1106. currentServerId = 0,
  1107. currentServerName = "";
  1108. for (let i of JSON.parse(localStorage.gameservers)) i.isCurrent && (currentServerId = +i.address.substr(1, 2), currentServerName = i.name);
  1109. let ss = unsafeWindow.setserver;
  1110.  
  1111. function getServerValue(a) {
  1112. return svInfo[currentServerId] && svInfo[currentServerId][a] ? svInfo[currentServerId][a] : svInfo.default[a]
  1113. }
  1114.  
  1115. function customize(a) {
  1116. let b = a.__proto__.de;
  1117. a.__proto__.de = function () {
  1118. let a = this,
  1119. c = a[prop[46]];
  1120. return 0 == c && ((settings.theme_boxes[1].active || settings.checkboxes[2].active) && (a[prop[45]] = !0), settings.theme_boxes[2].active ? (void 0 === a.oSpiked && (a.oSpiked = a[prop[56]]), a[prop[56]] = settings.theme_boxes[2].active) : void 0 != a.oSpiked && (a[prop[56]] = a.oSpiked)), 1 == c && ("#00ff00" == a.color && (a.color = "#00ff01"), settings.theme[0].active ? (a.oColor || (a.oColor = a.color), a.color = settings.theme[0].color) : a.oColor && (a.color = a.oColor)), b.apply(this, arguments)
  1121. }, customDc = !0
  1122. }
  1123.  
  1124. function dimmColor(d) {
  1125. var a = (~~(.5 * parseInt(d.substr(1, 2), 16))).toString(16),
  1126. b = (~~(.5 * parseInt(d.substr(3, 2), 16))).toString(16),
  1127. c = (~~(.5 * parseInt(d.substr(5, 2), 16))).toString(16);
  1128. return 1 == a.length && (a = "0" + a), 1 == b.length && (b = "0" + b), 1 == c.length && (c = "0" + c), "#" + a + b + c
  1129. }
  1130. unsafeWindow.setserver = (a, b) => {
  1131. currentServerId = +a.substr(1, 2), currentServerName = b, r1Portal.portal = null, r2Portal.portal = null, ss(a, b)
  1132. }, Array.prototype.push = function () {
  1133. if (this.length > 0 && this[0].hasOwnProperty("goldMember") && (chatmsgs = this), customCells && this.length && "number" == typeof this[0].id && "string" == typeof this[0].color) {
  1134. let b = this[this.length - 1],
  1135. a = arguments[0];
  1136. prop || (prop = Object.keys(b)), a.id === getServerValue("r1Id") ? r1Portal.portal = a : a.id === getServerValue("r2Id") && (r2Portal.portal = a), customDc || customize(b)
  1137. }
  1138. return pushFn.apply(this, arguments)
  1139. }, Array.prototype.splice = function () {
  1140. if (customCells && this.length && "number" == typeof this[0].id && "string" == typeof this[0].color) {
  1141. let a = this[arguments[0]];
  1142. a == r1Portal.portal ? r1Portal.portal = null : a == r2Portal.portal && (r2Portal.portal = null)
  1143. }
  1144. return spliceFn.apply(this, arguments)
  1145. };
  1146. let fillFn = CanvasRenderingContext2D.prototype.fill;
  1147. CanvasRenderingContext2D.prototype.fill = function () {
  1148. return settings.theme_boxes[0].active && "canvas" === this.canvas.id && .4 == this.globalAlpha && (this.globalAlpha = .15), settings.theme[1].active && "#00ff00" == this.fillStyle && (this.fillStyle = settings.theme[1].color), settings.theme[3].active && "#cd5564" == this.fillStyle && (this.fillStyle = settings.theme[3].color), whiteBorder4BlackCells && "#000000" == this.fillStyle && (this.strokeStyle = "#FFFFFF", this.lineWidth = 20, this.stroke()), fillFn.apply(this, arguments)
  1149. };
  1150. let strokeFn = CanvasRenderingContext2D.prototype.stroke;
  1151. CanvasRenderingContext2D.prototype.stroke = function () {
  1152. return "canvas" === this.canvas.id && (settings.checkboxes[2].active && ("#dddddd" == this.strokeStyle || "#333333" == this.strokeStyle) && (this.strokeStyle = $("#cDark").prop("checked") ? "#FFFFFF" : "#000000"), settings.theme_boxes[0].active && 4 != this.lineWidth && $("#cBubbleCells").prop("checked") && (this.lineWidth = 15 + Math.min(Math.max(avgFps - 25, 0), 10)), (settings.theme[1].active || settings.theme[2].active) && ("#00ff00" == this.strokeStyle || "#00cc00" == this.strokeStyle) && (this.strokeStyle = settings.theme[2].active ? settings.theme[2].color : settings.theme_boxes[0].active && $("#cBubbleCells").prop("checked") ? settings.theme[1].color : dimmColor(settings.theme[1].color)), (settings.theme[3].active || settings.theme[4].active) && ("#cd5564" == this.strokeStyle || "#a44450" == this.strokeStyle) && (this.strokeStyle = settings.theme[4].active ? settings.theme[4].color : settings.theme_boxes[0].active && $("#cBubbleCells").prop("checked") ? settings.theme[3].color : dimmColor(settings.theme[3].color))), strokeFn.apply(this, arguments)
  1153. };
  1154. let drawImgFn = CanvasRenderingContext2D.prototype.drawImage;
  1155. CanvasRenderingContext2D.prototype.drawImage = function () {
  1156. if (drawImgFn.apply(this, arguments), -1 == noPortalSvIdList.indexOf(currentServerId) && "AS | MegaSplit" != currentServerName && "canvas" == this.canvas.id && settings.checkboxes[8].active && ("https://agma.io/skins/objects/1_lo.png?v=2" == arguments[0].src || "https://agma.io/skins/objects/1.png?v=2" == arguments[0].src) && (r1Portal.portal || r2Portal.portal)) {
  1157. let a = a => {
  1158. a.portal.nSize * a.portal.nSize / 100 != a.lastMass && (a.lastMassChange = Date.now()), a.lastMass = a.portal.nSize * a.portal.nSize / 100;
  1159. let b = Date.now() - a.lastMassChange > 200 ? ~~((a.portal.nSize * a.portal.nSize / 100 - getServerValue("r" + a.room + "StartMass")) / getServerValue("ejPortalMass")).toString() : a.lastValue;
  1160. a.lastValue = b, this.fillStyle = "7" == b ? "#FFCC12" : "#FFFFFF", this.globalAlpha = 1, this.font = "72px Ubuntu, serif", this.fillText(b, a.portal.x - this.measureText(b).width / 2, a.portal.y + 20)
  1161. };
  1162. r1Portal.portal && a(r1Portal), 39 != currentServerId && r2Portal.portal && a(r2Portal)
  1163. }
  1164. }
  1165.  
  1166.  
  1167. // const oldFillText = CanvasRenderingContext2D.prototype.fillText;
  1168. // CanvasRenderingContext2D.prototype.fillText = function() {
  1169. // if (hiddenUI && arguments[0].includes("Mass: ")) arguments[0] = "";
  1170. // oldFillText.apply(this, arguments);
  1171. // }
  1172.  
  1173. let intervalCount = 0, currentUser, lastLoggedOut = Date.now();
  1174. const mainInterval = setInterval(() => {
  1175. intervalCount++;
  1176. if(hoverShowSkinID && $('#publicSkinsPage').children().length > 0){ // check if skins have loaded
  1177. $('[id^="skinContainer"]').each(function(){
  1178. $(this).find('img').attr('title', $(this).attr('id').replace('skinContainer', '')); // make hover show skin ID
  1179. })
  1180. $('.publicskins-nav-btn').on('click', () => {
  1181. $('[id^="skinContainer"]').each(function(){
  1182. $(this).find('img').attr('title', $(this).attr('id').replace('skinContainer', ''));
  1183. })
  1184. })
  1185. }
  1186. currentUser = $('#userCoins2')[0].innerText;
  1187. let user_abil = currentUser == 'Please Login First' ? null : acc_abil[currentUser];
  1188. if(user_abil != undefined && showRemainingAbilityTime){
  1189. for(let i in user_abil){
  1190. let text = $(`.purchase-btn.confirmation[item="${i}"]`).parents().eq(0).find('div h5'),
  1191. active = $('#' + $(`.purchase-btn.confirmation[item="${i}"]`).parents().eq(0)[0].id + ' img').eq(1).css('display') != "none";
  1192. // has been 24h+ and the player hasn't logged out since it's expired
  1193. if(Date.now() - user_abil[i] > 8.64e7 && active){
  1194. text.eq(1).text('EXPIRED IF UNLOG');
  1195. text.eq(0).find('div h5').hide();
  1196. }
  1197. // has been >24h
  1198. else if(Date.now() - user_abil[i] < 8.64e7 && active){
  1199. text.eq(0).hide();
  1200. text.eq(1).text(msToTime(8.64e7 - (Date.now() - user_abil[i]))).show();
  1201. }
  1202. else { // has been 24h+ & player has logged out
  1203. text.eq(1).find('div h5').hide();
  1204. text.eq(0).find('div h5').show();
  1205. }
  1206.  
  1207. }
  1208. } else {
  1209. $('.white_shopdesc').show();
  1210. $('.white_shopdesc.fsfb-fake').hide();
  1211. }
  1212. if(accounts[currentUser] == null && currentUser !== 'Please Login First'){
  1213. xpInfo();
  1214. coinsInfo();
  1215. accounts = {...accounts, ...{[currentUser] : {coins: currentCoins, xp: currentXP}}};
  1216. }
  1217. if(coinXPstats && intervalCount % 12 == 0){
  1218. xpInfo();
  1219. lastMinXP.push(new record(round(currentXP, 3), currentUser, lastMinXP));
  1220. const prevObjXP = lastMinXP[lastMinXP.length - 1];
  1221. if (prevObjXP && prevObjXP.id % (6e4 / updateTimeXP) == 0) lastHrXP.push(new record(currentXP, currentUser, lastHrXP));
  1222. if (lastMinXP.length > 6e4 / updateTimeXP) lastMinXP.shift();
  1223. if (lastHrXP.length > 60) lastHrXP.shift();
  1224. if(!coinsHTMLactive && $('#stats-container').css('display') == 'block') updateUI();
  1225. }
  1226. if(coinXPstats && intervalCount % 6 == 0){
  1227. coinsInfo();
  1228. lastMinCoins.push(new record(currentCoins, currentUser, lastMinCoins));
  1229. const lastObjCoins = lastMinCoins[lastMinCoins.length - 1];
  1230. if (lastObjCoins && lastObjCoins.id % (6e4 / updateTimeCoins) == 0) lastHrCoins.push(new record(currentCoins, currentUser, lastHrCoins));
  1231. if (lastMinCoins.length > 6e4 / updateTimeCoins) lastMinCoins.shift();
  1232. if (lastHrCoins.length > 60) lastHrCoins.shift();
  1233. if(coinsHTMLactive && $('#stats-container').css('display') == 'block') updateUI();
  1234. }
  1235. $('#stats-sesh-length').text(msToTime(Date.now() - scriptStart));
  1236.  
  1237. if(currentUser == 'Please Login First' || $('#level').text() == 0) lastLoggedOut = Date.now();
  1238. changeTitle(settings.checkboxes[4].active ? currentUser == 'Please Login First' ? "Agma.io" : "Agma.io - " + currentUser : "Agma.io - A free multiplayer MMO game");
  1239.  
  1240. fpsArr.push(+document.getElementById("fps").innerText);
  1241. if(fpsArr.length == 6) fpsArr.shift();
  1242. avgFps = mean(fpsArr);
  1243. }, 1e3);
  1244.  
  1245. const antiAFK = () => {
  1246. setTimeout(antiAFK, 3e4);
  1247. if(!$('#fsfb-antiAFK').is(':checked'))return; // move mouse every 30sec
  1248. if(linesplitting){
  1249. $('#canvas').trigger($.Event('mousemove', {clientX: pointMove.nx + 1, clientY: pointMove.ny}));
  1250. $('#canvas').trigger($.Event('mousemove', {clientX: pointMove.nx - 1, clientY: pointMove.ny}));
  1251. } else {
  1252. $('#canvas').trigger($.Event('mousemove', {clientX: mosX + 1, clientY: mosY}));
  1253. $('#canvas').trigger($.Event('mousemove', {clientX: mosX - 1, clientY: mosY}));
  1254. }
  1255. }
  1256. setTimeout(antiAFK, 3e4);
  1257.  
  1258.  
  1259. const updateScriptSettingsUI = () => {
  1260. for(let i of settings.checkboxes){
  1261. $('#' + i.id).prop("checked", i.active);
  1262. $('#' + i.id).trigger("change");
  1263. }
  1264. for(let i of settings.hotkeys) $('#' + i.id).text(getName(i.key));
  1265. for(let i of settings.quickSettings){
  1266. $('#' + i.id1).val(i.set);
  1267. $('#' + i.id).text(getName(i.key));
  1268. }
  1269. $('#' + settings.slowFeed[0].id).text(getName(settings.slowFeed[0].key));
  1270. $('#' + settings.slowFeed[1].id).val(settings.slowFeed[1].val);
  1271. for(let i of settings.export_import){
  1272. $('#' + i.id).prop("checked", i.active);
  1273. $('#' + i.id).trigger("change");
  1274. }
  1275. for(let i of settings.theme){
  1276. $('#' + i.id).prop("checked", i.active);
  1277. $('#' + i.id).trigger("change");
  1278. $('#' + i.id1).val(i.color);
  1279. $('#' + i.id1).trigger("change");
  1280. }
  1281. for(let i of settings.theme_boxes){
  1282. $('#' + i.id).prop("checked", i.active);
  1283. $('#' + i.id).trigger("change");
  1284. }
  1285. }
  1286. setTimeout(() => updateScriptSettingsUI(), 1e3);
  1287.  
  1288. $('body').append('<div id="fsfb-css-styles"><style id="hideUI-css" type="text/css"></style></div>');
  1289.  
  1290. const _replaceCSS = (a,b) => {
  1291. document.getElementById(a).innerHTML = b;
  1292. }
  1293. $('body').append(`
  1294. <div class="modal fade fsfb-bug-modal" tabindex="-1" role="dialog" aria-hidden="true">
  1295. <div class="modal-dialog modal-lg">
  1296. <div class="modal-content">
  1297. <div class="modal-interior">
  1298. <h2 class="fsfb-modal-title">Script Documentation</h2>
  1299. <button type="button" class="close fsfb-btn" data-dismiss="modal">×</button>
  1300. <section class="fsfb-modal-body">
  1301. <!-- Checkboxes -->
  1302. <div><span>Chat Copy/Cut/Paste</span> - allows you to use the commands in chat (e.g. Ctrl + V becomes avaiable inside chat)</div>
  1303. <div><span>Anti-AFK</span> - prevents you from automatically disconnecting after 10 minutes</div>
  1304. <div><span>Anti-Invis</span> - shows you players even when they have the invisibility ability active</div>
  1305. <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>
  1306. <div><span>Change Page Title</span> - changes the tab's title to just "Agma.io" with the current username</div>
  1307. <div><span>Hide Shouts</span> - prevent megaphone shouts from showing up at all</div>
  1308. <div><span>Hold To Spam</span> - while the powerup's hotkey is held, it will continuously use the powerup</div>
  1309. <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>
  1310. <div><span>Copy Chat Messages</span> - when you right click, there is an extra option called "Copy Chat Messages", when clicked, it will add the currently displayed chat messages to your clipboard</div>
  1311. <!-- Theme -->
  1312. <div><span>Food/Virus/Mothercell Color</span> - changes the color that's filling these to a custom one</div>
  1313. <div><span>Virus/Mothercell Stroke</span> - changes the color of the stroke (border/outline) to a custom one</div>
  1314. <div><span>Spiked Cells</span> - render all cells with the spikes from the infecton gamemode</div>
  1315. <div><span>Show Mass</span> - show the mass of all players' cells</div>
  1316. <!-- Hotkeys -->
  1317. <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>
  1318. <div><span>Linesplit Lock</span> - finds which direction your mouse is the closest to & puts mouse way off the map (towards that direction) so you can perform perfect linesplits without zooming out or precisely placing your mouse (feature and design inspired by <a href="https://greasyfork.org/en/scripts/404559-agma-io-linesplit-overlay" target="_blank">Wynell's script</a></div>
  1319. <div><span>Macrosplit Bots</span> - hold this key to macrosplit your bots without switching controls off of yourself</div>
  1320. <div><span>Hide UI</span> - press this key to toggle showing the game UI (intended for recording/screenshots)</div>
  1321. <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>
  1322. <div><span>Slow Feed Speed</span> - the speed at which eject mass is pressed when slow-feeding</div>
  1323. <div><span>Quick Settings</span> - when the hotkey assigned is pressed, it will toggle the setting that is selected</div>
  1324. <!-- (Ex/Im)port -->
  1325. <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>
  1326. <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>
  1327. <!-- Passive features -->
  1328. <div><span>Hide Ads</span> - both video and image ads will be removed from the screen</div>
  1329. <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>
  1330. <div><span>Extra Bot Packs</span> - added hidden bot packs that can be purchased with coins (originally discovered by firebone)</div>
  1331. <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>
  1332. <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>
  1333. <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>
  1334. <div><span>Unlock Free Skins</span> - gives you access to the facebook & youtube free skins</div>
  1335. <div><span>Hover For Skin ID</span> - hovering skins in the skin menu will shop their ID</div>
  1336. <div><span>In Depth XP/Coins Stats</span> - click on coins/xp progress bar in top left to view respective statistics</div>
  1337. <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>
  1338. <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>
  1339. <div><span>Custom Backgrounds</span> - a few <a href="https://imgur.com/a/sTANNBE" target="_blank">backgrounds</a></div>
  1340. </section>
  1341. </div>
  1342. </div>`)
  1343.  
  1344. const styles = document.createElement('style');
  1345. styles.innerHTML = `
  1346. .fsfb-shown{
  1347. display: block !important;
  1348. }
  1349. #fsfb-quickbuy{
  1350. display: flex !important;
  1351. justify-content: center;
  1352. align-items: center;
  1353. }
  1354. #fsfb-quickbuy-img{
  1355. height: 80%
  1356. }
  1357. .fsfb-bug-modal>div>div{
  1358. -webkit-box-shadow: 0 5px 15px rgb(0 0 0 / 50%);
  1359. background: linear-gradient(to bottom,#3b414e 0,#302f33 100%);
  1360. border: 3px solid #232630;
  1361. }
  1362. .close.fsfb-btn{
  1363. position: absolute;
  1364. right: 10px;
  1365. top: 3px;
  1366. font-size: 60px;
  1367. }
  1368. .fsfb-modal-body>div{
  1369. color: ffffffb0;
  1370. margin: 10px 20px
  1371. }
  1372. .fsfb-modal-body>div>span{
  1373. color: white;
  1374. }
  1375. .fsfb-modal-body{
  1376. margin: 10px 10px;
  1377. font-size: 20px;
  1378. max-height: 600px;
  1379. overflow-y: auto;
  1380. }
  1381. .fsfb-modal-title{
  1382. text-align: center;
  1383. color: white;
  1384. }
  1385. .fsfb-hotkey{
  1386. background-color: #df901c;
  1387. color: #fff;
  1388. cursor: pointer;
  1389. text-align: center;
  1390. min-width: 40px;
  1391. max-width: 60px;
  1392. height: 18px;
  1393. line-height: 18px;
  1394. vertical-align: middle;
  1395. border-radius: 9px;
  1396. right: 5px;
  1397. position: absolute;
  1398. display: inline-block;
  1399. padding: 0 5px;
  1400. overflow: hidden;
  1401. opacity: 1;
  1402. }
  1403. .fsfb-modal-body::-webkit-scrollbar-thumb {
  1404. background-color: #57595b;
  1405. border: 1px solid black;
  1406. border-radius: 12px;
  1407. }
  1408. .fsfb-modal-body::-webkit-scrollbar {
  1409. border: 1px solid black;
  1410. background-color: #2523239e;
  1411. width: 15px;
  1412. border-radius: 12px;
  1413. }
  1414. .fsfb-modal-body::-webkit-scrollbar-track {
  1415. -webkit-box-shadow: inset 0px 0px 2px 2px rgba(0, 0, 0, 0.75);
  1416. box-shadow: inset 0px 0px 2px 2px rgba(0, 0, 0, 0.75);
  1417. border-radius: 12px;
  1418. }
  1419. .fsfb-hotkey:hover {
  1420. background-color: #f1a02d;
  1421. }
  1422. .fsfb-hotkey.selected{
  1423. background-color: #ff4;
  1424. color: #444;
  1425. }
  1426. #fsfb-settings-main p{
  1427. margin: 0;
  1428. display: inline-block;
  1429. margin-left: 4px;
  1430. }
  1431. #fsfb-settings-main{
  1432. display: -ms-grid;
  1433. display: grid;
  1434. -ms-grid-columns: 50% 50%;
  1435. grid-template-columns: 50% 50%;
  1436. }
  1437. #settingPage4{
  1438. display: none;
  1439. max-height: 600px;
  1440. overflow-x: hidden;
  1441. }
  1442. .padbot10{
  1443. padding-bottom: 10px;
  1444. }
  1445. #fsfb-slowfeedtimer{
  1446. border: none;
  1447. width: 40px;
  1448. }
  1449. select.fsfb-quickchange{
  1450. background: none;
  1451. border: none;
  1452. height: 20px;
  1453. }
  1454. select.fsfb-quickchange:focus-visible{
  1455. outline: none;
  1456. }
  1457. select.fsfb-quickchange option{
  1458. background: #222;
  1459. }
  1460. .fsfb-sect-ch label{
  1461. display: flex;
  1462. align-items: center;
  1463. }
  1464. .fsfb-sect-ch label input{
  1465. margin: 0 2px 0 0;
  1466. }
  1467. #fsfb-sect-theme label input[type="color"]{
  1468. width: 14px;
  1469. height: 14px;
  1470. opacity: 0;
  1471. border: none;
  1472. background-color: white;
  1473. margin: 0;
  1474. cursor: pointer;
  1475. }
  1476. #fsfb-sect-theme label p{
  1477. min-width: 120px;
  1478. margin-left: 5px;
  1479. }
  1480. #fsfb-sect-theme label div{
  1481. border-radius: 4px;
  1482. border: 1px solid #ffffff29;
  1483. }
  1484. #fsfb-ximport-cont{
  1485. display: flex;
  1486. justify-content: space-around;
  1487. margin-top: 7px;
  1488. }
  1489. .fsfb-eximport{
  1490. background-color: #df901c;
  1491. color: white;
  1492. padding: 5px 17px;
  1493. border-radius: 25px;
  1494. cursor: pointer;
  1495. }
  1496. .hideMegaphone{
  1497. display: none !important;
  1498. }
  1499. .fsfb-fake{
  1500. color: #cbff4e;
  1501. }
  1502. #fsfb-extra-info{
  1503. margin: 10% 0 0 90%;
  1504. cursor: pointer;
  1505. }
  1506. #linesplit-markers div {
  1507. background-color: transparent;
  1508. height: 15px;
  1509. aspect-ratio: 1;
  1510. position: fixed;
  1511. z-index: 999;
  1512. border: 2px solid rgb(255 255 255 / 80%);
  1513. border-radius: 50%;
  1514. display: none;
  1515. }#linesplit-top {
  1516. top: -7.5px;
  1517. transform: translateX(-50%);
  1518. left: 50%;
  1519. }
  1520. #stats-container{
  1521. background: rgba(0,0,0,.5);
  1522. top: 200px;
  1523. position: absolute;
  1524. border: 1px white solid;
  1525. border-radius: 12px;
  1526. color:white;
  1527. left:30px;
  1528. }
  1529. #stats-info{
  1530. display: flex;
  1531. flex-direction: column;
  1532. justify-content: center;
  1533. font-size: 1.5vh;
  1534. }
  1535. #stats-main{
  1536. padding: 10px;
  1537. }
  1538. #stats-extra-info{
  1539. color: #00bbff;
  1540. font-size: 1.1vh;
  1541. margin-bottom: 2px;
  1542. display: block;
  1543. }
  1544. #stats-info div{
  1545. display:flex;
  1546. }
  1547. #stats-info div p{
  1548. margin: 0;
  1549. }
  1550. .stats-label{
  1551. min-width: 12.5vh;
  1552. }
  1553. .stats-completed{
  1554. margin: 0.35vh 0.3vh 0;
  1555. font-size: 1vh;
  1556. bottom: 0;
  1557. color: rgb(255, 255, 255, .8);
  1558. }
  1559. #stats-title{
  1560. display: flex;
  1561. align-items: center;
  1562. }
  1563. #stats-title div{
  1564. display: flex;
  1565. margin-left: auto;
  1566. font-size: 1.1vh;
  1567. cursor: pointer;
  1568. }
  1569. #linesplit-right {
  1570. right: -7.5px;
  1571. transform: translateY(-50%);
  1572. top: 50%;
  1573. }
  1574. #linesplit-bottom {
  1575. bottom: -7.5px;
  1576. transform: translateX(-50%);
  1577. left: 50%;
  1578. }
  1579. #linesplit-left {
  1580. left: -7.5px;
  1581. transform: translateY(-50%);
  1582. top: 50%;
  1583. }
  1584. .fuckAds{
  1585. transform: translateX(9999%) !important;
  1586. }
  1587. `
  1588. document.querySelector('body').append(styles);
  1589.  
  1590. const scripts = document.createElement('script');
  1591. scripts.innerHTML = `
  1592. function onlyNumberKey(e) {
  1593. const key = e.which ? e.which : e.keyCode;
  1594. return !(key > 31 && (key < 48 || key > 57));
  1595. }
  1596. `
  1597. document.querySelector('body').append(scripts);
  1598. };
  1599.  
  1600. document.readyState === 'complete' ? afterLoaded() : document.addEventListener("DOMContentLoaded", afterLoaded);
  1601.