`);
$('#' + slowfeedhotkey.id).on('contextmenu', e => {
$('#' + slowfeedhotkey.id).text('').removeClass('selected');
slowfeedhotkey.active = false;
slowfeedhotkey.key = 0;
saveSettings();
styleActiveSettings();
e.preventDefault();
});
document.getElementById(settings.slowFeed[1].id).addEventListener("keypress", function(e){
setTimeout(() => { // goes too fast or smth
settings.slowFeed[1].val = +$('#' + settings.slowFeed[1].id).val();
saveSettings();
}, 5);
});
// add fastsplit HTML
$('#fsfb-sect-fastsplit').append(`
Fast-Split
`);
// for(let i of settings.fastsplit_hotkeys){
for(let j = 0; j < settings.fastsplit_hotkeys.length; j++){
let i = settings.fastsplit_hotkeys[j];
$('#fsfb-sect-fastsplit').append('val' in i ? `
`);
for(let i of settings.uiScaling){
$('#fsfb-sect-uiScale').append(`
${i.title}
`);
// $('#fsfb-sect-uiScale').append(`
${i.title}
`);
$( "#" + i.id).on('input', function() {
changeSettings(this.id, $(this).val());
});
};
let quickBuying = false;
['megaphone_shout', 'minion_nuker', 'freeze_yourself', 'ripple-virus'].forEach(elId => $('#invCloak').after(``));
$(settings.checkboxes[10] ? '#fsfb-megaphone_shout' : '#inv360Shot').after(``);
$('#fsfb-quickbuy').on('click', function(){
quickBuying = !quickBuying;
if (quickBuying){
$(this).addClass('activatedInv');
curserMsg('Quick buy activated, click the powerup you would like to buy.', 'green');
$('.inventory-box').addClass('fsfb-shown').find('p').hide();
$('#invCloak').removeClass('fsfb-shown');
$('#fsfb-quickbuy').removeClass('fsfb-shown');
} else {
$(this).removeClass('activatedInv');
curserMsg('Quick buy deactivated.', 'red');
$('.inventory-box').removeClass('fsfb-shown').find('p').show();
}
});
const styleActiveSettings = () => {
// css selector :has() isn't fully supported at the moment, so gotta use jquery for the time being :(
// #fsfb-settings-main p:has(+.fsfb-hotkey:not(:empty)) {
// color:#df901c;
// }
// #fsfb-settings-main .fsfb-hotkey:not(:empty) ~ p:has(+input.fsfb-hotkey):not(div.fsfb-hotkey:empty + * + p):not(div.fsfb-hotkey:empty + * + p + * + * + p) {
// color: #df901c;
// }
if(!userPreferences.stylizeActiveSettings) return;
$('#fsfb-settings-main p:has(+div.fsfb-hotkey), #fsfb-settings-main select:has(+div.fsfb-hotkey)').each(function(){
$(this)[$(this).is(':has(+.fsfb-hotkey:empty)') ? 'removeClass' : 'addClass']('fsfb-active-setting');
})
$('#fsfb-settings-main .fsfb-hotkey~ p:has(+input.fsfb-hotkey)').each(function(){
$(this)[$(this).is('div.fsfb-hotkey:empty + * + p') || $(this).is('div.fsfb-hotkey:empty + * + p + * + * + p') ? 'removeClass' : 'addClass']('fsfb-active-setting');
});
$('input[type="range"].fsfb-slider').each(function(){
$(this).prev()[this.value == this.getAttribute('value') ? 'removeClass': 'addClass']('fsfb-active-setting');
})
}
// add lag detection settings to things
let customCells = false;
$("#fsfb-sect-theme>label>input").addClass('fsfb-ncustom');
$('#fsfb-powsoverlay, #fsfb-mtchmass').addClass('fsfb-ncustom');
$('#fsfb-check-Hazard, #fsfb-check-border').removeClass('fsfb-ncustom');
$("#fsfb-revcell, #fsfb-portalstop, #fsfb-bublecell").removeClass('fsfb-ncustom');
$('#fsfb-foodSize').prev().addClass('fsfb-lag');
let antilagCells = false;
let fillCells = false;
$('#fsfb-bublecell, #fsfb-showmass, #fsfb-spikedcells, #fsfb-mtchmass, #fsfb-powsoverlay, #fsfb-anticloak, #fsfb-portalmass, #fsfb-nc-public').addClass('fsfb-lag');
$('#fsfb-powsoverlay, #fsfb-bublecell, #fsfb-anticloak').addClass('fsfb-nfill');
let onlyCells = false;
let strokeCells = false;
$('#fsfb-bublecell, #fsfb-powsoverlay').addClass('fsfb-nstroke');
let foodSizeOn = false;
// let drawCells = false;
// $('#fsfb-powsoverlay, #fsfb-portalmass').addClass('fsfb-ndraw');
let customCellsChanged = false;
let translateChanged = false, pwOverlayChanged = false;
const changeSettings = (ID, active, e) => { // a = active, e = event (optional)
for(let i of settings.uiScaling) if(i.id == ID) i.level = active;
for(let i of settings.checkboxes) if(i.id == ID) i.active = active;
for(let i of settings.export_import) if(i.id == ID) i.active = active;
for(let i of settings.theme_boxes) if(i.id == ID) i.active = active;
for(let i of settings.anti_lag) if(i.id == ID) i.active = active;
for(let i of settings.chat_translate){
if(i.id == ID){
if(ID == "fsfb-tranchat" && (typeof GM_xmlhttpRequest != 'function') && e?.originalEvent?.isTrusted){
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);
$('#' + ID).prop('checked', false);
return;
}
translateChanged = true;
i.active = active;
}
}
for(let i of settings.theme){
if(i.id == ID) i.active = active;
if(i.id1 == ID) i.color = active;
}
for(let i of settings.name_color){
if(i.id == ID) i.active = active;
if(i.id1 == ID) i.color = active;
}
if(ID == "fsfb-hideshouts") $('#megaholder')[`${active ? 'add' : 'remove'}Class`]('hideMegaphone');
if(ID == "fsfb-qBuy") $('#fsfb-quickbuy')[active ? 'show' : 'hide'](); //.css('display', a ? 'flex' : 'hide');
if(ID == "fsfb-pwsonerow"){
$('#fsfb-quickbuy').detach().appendTo(`#inventory${active ? 2 : 1}`);
[1, 2].forEach((i) => $("#inventory" + i).addClass("fsfb-inventories").css("order", active ? i : -i));
_replaceCSS("css-invsingleline", active ? `#inventory{ display: flex; position: absolute; left: 50%; bottom: 8px; transform: translateX(-50%); } .fsfb-inventories { position: initial !important; transform: initial !important; }` : '');
}
let maxlen = $('#fsfb-nc-public').is(':checked') ? ($('#fsfb-box-ncs').is(':checked') ? 16 : 19) : 24;
$('#nick').attr('maxlength', maxlen);
if(ID == "fsfb-nc-public"){
if(!active) pubNameSwitch = true;
setTimeout(() => (pubNameSwitch = false), 25); // def nonoptimal, 2 lazy 2 fix
}
// client side colored name
if(["fsfb-box-nc", "fsfb-box-ncs", "fsfb-nc-color", "fsfb-nc-stroke", "fsfb-nc-public"].includes(ID) && nameColors?.set){
if(settings.name_color[0].active || ID === "fsfb-nc-public"){
if(active && ID === "fsfb-nc-public" && !settings.name_color[1].active){
nameColors.set(defaultColorsAmnt, "#FFFFFF");
nameStrokes.set(defaultColorsAmnt, "#000000");
} else {
nameColors.set(defaultColorsAmnt, settings.name_color[1].color);
nameStrokes.set(defaultColorsAmnt, settings.name_color[2].active ? settings.name_color[2].color : contrastColor(settings.name_color[1].color));
}
// warnings
if(playerIsAlive()){
if(ID == "fsfb-nc-public" && !active){
curserMsg("Respawn to deactivate FSFB public custom name.", "red", 4e3, false);
} else {
curserMsg(`Respawn to update your name color!\nNote: FSFB Public Colored Name is ONLY visible to other FSFB script users!`, 'orange', 8e3, true);
}
} else if(maxlen < 24 && ID == "fsfb-nc-public") {
curserMsg(`FSFB script's public custom name feature encodes the color in your clan tag; therefore, clan tags won't work with this feature enabled. The max nick length with the current settings is ${maxlen}${settings.name_color[2].active ? `, deactivate Name Stroke to increase this to 19.` : '.'}\nPlease note: FSFB Public Colored Name is ONLY visible to other FSFB script users!`, "purple", 20e3, true);
}
} else {
if((ID === "fsfb-nc-color" && !active) || !settings.name_color[1].active){
nameColors.set(defaultColorsAmnt, "#FFFFFF");
nameStrokes.set(defaultColorsAmnt, "#000000"); // again won't revert bc lazy
} else {
nameColors.set(defaultColorsAmnt, settings.name_color[1].color);
nameStrokes.set(defaultColorsAmnt, settings.name_color[2].active ? settings.name_color[2].color : contrastColor(settings.name_color[1].color));
}
}
}
if(ID == "fsfb-bublecell" && $("#cBubbleCells").is(":checked") != active && e?.originalEvent?.isTrusted) $('#cBubbleCells').unbind().click();
if(ID == "fsfb-showmass" && $("#cMass").is(":checked") != active && e?.originalEvent?.isTrusted) $('#cMass').unbind().click();
if((ID == "fsfb-myskins" || ID == "fsfb-partyskins") && !$("#cSkins").is(":checked") && active) $('#cSkins').unbind().click();
if((ID == "fsfb-mynick" || ID == "fsfb-partynicks") && !$("#cNames").is(":checked") && active) $('#cNames').unbind().click();
if(ID == "fsfb-myskins" && active) $('#fsfb-partyskins').prop('checked', false).trigger('change');
if(ID == "fsfb-partyskins" && active) $('#fsfb-myskins').prop('checked', false).trigger('change');
if(ID == "fsfb-mynick" && active) $('#fsfb-partynicks').prop('checked', false).trigger('change');
if(ID == "fsfb-partynicks" && active) $('#fsfb-mynick').prop('checked', false).trigger('change');
if(ID == "fsfb-hideejected" && active) $('#fsfb-hidestaticej').prop('checked', false).trigger('change');
if(ID == "fsfb-hidestaticej" && active) $('#fsfb-hideejected').prop('checked', false).trigger('change');
if(ID == "fsfb-box-nc" && !active) $('#fsfb-box-ncs').prop('checked', false).trigger('change');
if(ID == "fsfb-powsoverlay") svSwitch = true;
let zoomLvl = "100%";
let map = {
1: 50,
2: 70,
3: 80,
4: 90,
5: 100,
6: 110,
7: 125,
8: 150,
9: 200
}
if(ID == "fsfb-invSize") $('#inventory').css('zoom', (map[+active] ?? 100)+ '%');
if(ID == "fsfb-statsSize") $('#stats-container').css('zoom', (map[+active] ?? 100) + '%');
let prevCustomCells = customCells;
// lag settings
customCells = $(".fsfb-ncustom, #fsfb-nc-public").is(":checked") || $('#fsfb-foodSize').val() != '1'; // || userPreferences.whiteBorder4BlackCells;
fillCells = $('.fsfb-nfill').is(':checked');
strokeCells = $('.fsfb-nstroke').is(':checked') || settings.checkboxes[2].active;;
onlyCells = $('#fsfb-myskins, #fsfb-partyskins, #fsfb-mynick, #fsfb-partynicks').is(':checked') || ($('#fsfb-box-nc').is(':checked') && !$('#fsfb-nc-public').is(':checked')) || userPreferences.rainbowNameColor;
antilagCells = $("#fsfb-sect-antilag>label>input").not('#fsfb-nodeathanim').is(':checked');
foodSizeOn = $('#fsfb-foodSize').val() != '1';
if(prevCustomCells != customCells && e?.originalEvent?.isTrusted) customCellsChanged = true;
// if(customCellsChanged && e?.originalEvent?.isTrusted) setTimeout(() => (customCellsChanged = false), 25);
if(customCellsChanged && e?.originalEvent?.isTrusted) setTimeout(() => (customCellsChanged = false), 25);
styleActiveSettings();
saveSettings();
}
// add event listeners
let mosX, mosY,
keys = {},
keysChanging = false,
mouseHoveringChat = false;
$(document).on('click', e => {
if(e.target.id.includes('fsfb')){
for(let i = 0; i < settings.hotkeys.length; i++) checkHotkeyClicked(e, settings.hotkeys[i]);
for(let i = 0; i < settings.fastsplit_hotkeys.length; i++) settings.fastsplit_hotkeys[i].val == null && checkHotkeyClicked(e, settings.fastsplit_hotkeys[i]);
for(let i = 0; i < settings.quickSettings.length; i++) checkHotkeyClicked(e, settings.quickSettings[i]);
checkHotkeyClicked(e, settings.slowFeed[0]); // slow feed
checkHotkeyClicked(e, settings.frozenvirus[0]);
}
checkPowerupClicked(e);
})
.on('mousemove', e => {
if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
mouseHoveringChat = e.target.id == 'chtCanvas';
({clientX: mosX, clientY: mosY} = e);
if (linesplitting) linesplit();
})
.on("keydown", e => {
// if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
if(keysChanging){
for(let i = 0; i < settings.hotkeys.length; i++) checkNewHotkey(e, settings.hotkeys[i]);
for(let i = 0; i < settings.fastsplit_hotkeys.length; i++) checkFsHotkey(e, settings.fastsplit_hotkeys[i]);
for(let i = 0; i < settings.quickSettings.length; i++) checkNewHotkey(e, settings.quickSettings[i]);
checkNewHotkey(e, settings.slowFeed[0]); // slow feed
checkNewHotkey(e, settings.frozenvirus[0]);
}
if (!(e.keyCode in keys)){
keys[e.keyCode] = !0;
pressed(e);
}
keysChanging = false;
})
.on("keyup", e => {
if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
delete keys[e.keyCode];
released(e.keyCode);
if(cursorLockActivated && e.keyCode == getKey("FixedMouse")){
e.stopImmediatePropagation();
e.preventDefault();
}
})
.on('mouseup', e => {
if(e.which !== 1 || !e?.originalEvent?.isTrusted || !userPreferences.clickToCursorLock || cursorLockActivated) return;
unsafeWindow.onkeyup({keyCode: getKey('FixedMouse')});
});
$('#canvas').on('mousedown', e => { // for mouse cursorlock
if(e.which !== 1 || !e?.originalEvent?.isTrusted || !userPreferences.clickToCursorLock) return;
if(cursorLockActivated){
cursorLockActivated = false;
unsafeWindow.onblur = _onblur;
}
unsafeWindow.onkeydown({keyCode: getKey('FixedMouse')})
});
let factsArr = typeof facts_list == 'undefined' ? null : facts_list.replace(/^\n|\n$/gm, '').split(/\n/gm);
const randomFact = index => factsArr && (factsArr[index] ?? factsArr[~~(Math.random() * factsArr.length)]);
const hashCode = (str, shift) => {
let hash = 0;
for (let i = 0; i < str.length; i++) hash = (hash << shift) - hash + str.charCodeAt(i), hash |= 0;
return str.length == 0 ? 0 : hash;
};
const RNG = (min, max) => Math.random() * (max - min) + min;
// chat cmds
if(userPreferences.extraChatCommands){
const $chtbox = $('#chtbox');
$chtbox.on('keydown', e => {
if(e.keyCode != 13) return;
if(userPreferences.messageBanWarning && /a\s*g\s*a\s*r\s*p\s*o\s*w\s*e\s*r/gmi.test($chtbox.val())){
curserMsg("FSFB WARNING: FSFB script has censored your message as it has detected that your message may've contained a word/phrase that agma.io gives a 24 hour IP ban for. This FSFB feature can be disabled in the tampermonkey script settings.", 'red', 1e4);
$chtbox.val($chtbox.val().replace(/a\s*g\s*a\s*r\s*p\s*o\s*w\s*e\s*r/gmi, match => '*'.repeat(match.length)));
}
let newMsg = '', cmdVerif, customCmd = false, firstMatch = $chtbox.val().match(new RegExp(`(?<=((^|(^\/party +)|(^\/pm +\\S+ ))${escapeRegExp(userPreferences.chatPrefix || "/f ")}))\\S+(\\s+\\S+)*(?=($|\\s+))`, 'gmi'));
if(firstMatch == null) return;
let[command, ...params] = firstMatch[0].split(/\s+/g);
const originalMsg = $chtbox.val().replace(new RegExp(`(?<=((^|(^\/party +)|(^\/pm +\\S+ ))))${escapeRegExp(userPreferences.chatPrefix || "/f ")}${escapeRegExp(firstMatch[0])}(?=($|\\s+))`, 'gmi'), ''),
isolatedMsg = firstMatch[0].replace(new RegExp(command + '\\s', 'gi'), '');
coinsInfo();
xpInfo();
/* ~~~ example of external chat cmds (can be used by other scripts) ~~~ */
/*
externalChatCmds = [
{
commands: ['test1', 'test2', 'test3'],
callback: (command, isolatedMsg, params) => {
let newMsg = '';
return newMsg;
}
},
{
commands: ['eg1', 'eg2'],
callback: (cmd, isoMsg, params) => {
console.log(cmd, isoMsg, params);
alert('bru')
return '';
}
}
]
*/
let { externalChatCmds } = unsafeWindow;
if(externalChatCmds?.length){
for(let i of externalChatCmds){
if(i.commands.includes(command.toLowerCase())){
customCmd = true;
newMsg = i.callback(command.toLowerCase(), isolatedMsg, params);
}
}
}
if(!customCmd && (cmdVerif ??= true)){
switch(command.toLowerCase()){
case 'help':
$('#fsfb-extra-info')[0].click();
setTimeout(() => $('.fsfb-modal-body').scrollTop(1850), 200);
newMsg = '';
break;
case 'bots': case 'bot': case 'min': case 'mins': case 'minion': case 'minions': {
let minInfo = misc_settings.bots?.[currentUser], minsAmt, minsTimeRem;
$('#infoContent').children().each(function(){
if($(this).text().includes('Minion Time:')) minsTimeRem = $(this).find('span').text();
if($(this).text().includes('Minions:')) minsAmt = $(this).find('span').text();
});
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) */
newMsg = `Minion Pack Unstarted: ${minsChatAmt[currentUser].amt}`;
} else if(minsChatAmt[currentUser] != null && minsChatAmt[currentUser]?.started && minsChatAmt[currentUser]?.amt && minInfo == null ){
newMsg = `Minion Pack Activated: ${minsChatAmt[currentUser].amt}`;
} else if(!(minsAmt == '0' && minsTimeRem == '00:00:00') && minsChatAmt[currentUser] != null && minsChatAmt[currentUser].amt && minInfo != null && (minInfo.active || minsChatAmt[currentUser].started) && minInfo.rem - (Date.now() - minInfo.currMs) > 0){
newMsg = `Minion Pack Activated: ${minsChatAmt[currentUser].amt}, with ${msToTime(minInfo.rem - (Date.now() - minInfo.currMs))} remaining`;
} else if(!(minsAmt == '0' && minsTimeRem == '00:00:00') && minsAmt != null && minsTimeRem != null){
let timeArr = minsTimeRem.split(':'), msBotsTime = (3.6e6 * +timeArr[0]) + (6e4 * +timeArr[1]) + (1e3 * +timeArr[2]);
newMsg = `Minion Pack Activated: ${minsAmt}, with ${msToTime(msBotsTime)} remaining`;
} else {
newMsg = `Minion Pack Activated: none`;
}
break;
}
case 'pws': case 'pw': case 'powers': case 'power': case 'inv': case 'inventory': case 'pows': case 'powerups': {
curserMsg(`It's recommended to use ${userPreferences.chatPrefix + command}1, ${userPreferences.chatPrefix + command}2, and ${userPreferences.chatPrefix + command}3 instead, so your chat messages aren't cut-off by the chat maxlength`, 'red', 8e3);
updatePwCount();
newMsg = 'Inv: ';
for (let i = 0; i < 6; i++) {
newMsg = 'Inv: ';
for (let pw in pws) {
const check = index => i < index ? ' ' : '';
if (pws[pw] != '') newMsg += `${pws[pw]}${check(1)}${(i > 2 ? pw.slice(0, 6 - i) : pw)},${check(2)}`;
}
newMsg = newMsg.replace(/,[^,]*$/g, '');
if (newMsg.length <= 100) i = 6;
}
if (newMsg == 'Inv: ') newMsg = 'Inv: no powers';
break;
}
case 'totalpws': case 'totalpw': case 'totalpowers': case 'totalpower': case 'total': case 'totpw': case 'totalpows': case 'totalpowerups':
newMsg = `Total Powerups: ${$('.inventory-box>p').toArray().map(x => +x.innerText).reduce((a, b) => a + b).toLocaleString('en-US')}`
break;
case 'xplevel': case 'levelxp': case 'lvlxp': case 'xpcompleted': case 'xp':
newMsg = `XP Completed: ${currentPercent ? String(Math.round(currentPercent * currentLevel * 1e3, 1)).replace(/\B(?=(\d{3})+(?!\d))/g, ",") + "/" + (currentLevel * 1e3).toLocaleString('en-US') : "0/0"}`
break;
case 'level': case 'levels': case 'lvls': case 'lvl': case 'lvlcompleted': case 'lvlscompleted':
newMsg = `Level ${currentLevel}, with ${round(currentPercent * 100, 3)}% completed`
break;
case 'coin': case 'coins':
newMsg = `Coins: ${String(currentCoins).replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`
break;
case 'hours': case 'hrs': case 'hour': case 'hr': case 'timeplayed':
newMsg = (isLogged() ? $('.timePlayed>span').text().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : 'Time Played: 0, not logged in');
break;
case 'rank': case 'lvlrank':
newMsg = `My Rank: ${isLogged() ? $('.ranking.text-left>span').text().match(/(?<=^Your rank: )\d+$/gm)?.[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",") : '∞, not logged in'}`;
break;
case 'ping': case 'delay': case 'ms':
newMsg = `My Ping: ${$('#ping').text().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`
break;
case 'fps': case 'frames':
newMsg = `My FPS: ${$('#fps').text().replace(/\B(?=(\d{3})+(?!\d))/g, ",")}`
break;
case 'topmass': case 'highscore': case 'highmass': case 'highestmass':
newMsg = `My Top Mass: ${$('#topMass').text()}`
break;
case 'cells': case 'cellcount': case 'cell':
newMsg = `My Cell Count: ${$('#cellsAmount').text()}`
break;
case 'pw1': case 'pws1': case 'power1': case 'powers1': case 'inv1': case 'inventory1':
newMsg = getPowerMessage(1);
break;
case 'pw2': case 'pws2': case 'power2': case 'powers2': case 'inv2': case 'inventory2':
newMsg = getPowerMessage(2);
break;
case 'pw3': case 'pws3': case 'power3': case 'powers3': case 'inv3': case 'inventory3':
newMsg = getPowerMessage(3);
break;
case 'friends': case 'friend': case 'friendsonline': case 'friendonline':
newMsg = `Friends Online: ${$('#friendsLoggedInAmt').text() + $('#friendsTotalAmt').text()}`;
break;
case 'request': case 'requests': case 'friendrequest': case 'friendrequests': case 'friendreq': case 'req':
newMsg = `Friend Requests: ${$('#friendsRequestsAmt').text() === '' ? 'none' : $('#friendsRequestsAmt').text()}`;
break;
case 'gold': case 'goldmem': case 'goldmember': case 'golddays': case 'gm': {
const obj = accGoldMem[currentUser];
if(obj?.has == null){
newMsg = `Days of Goldmember Remaining: none`;
break;
}
newMsg = `Days Of Goldmember Remaining: ${obj.has || obj.days != null ? (obj.days + (obj.days == 1 ? ' Day' : ' Days')) : 'none'}`;
break;
}
case 'alive': case 'alivetime': case 'timealive':
newMsg = `Time Alive: ${playerIsAlive() ? msToTime(Date.now() - timeAlive) : 'not alive'}`;
break;
case 'mass': case 'currentmass':
newMsg = `Current Mass: ${playerIsAlive() ? currentMass.toLocaleString('en-US') : 'none, not alive'}`;
break;
case 'user': case 'username': case 'usr': case 'account': case 'acc':
newMsg = `My Username: ${currentUser == "Please Login First" ? "logged out" : currentUser}`
break;
case 'custom': case 'customs': case 'customskins': case 'totalcustoms':
if($('#publicSkinsPage').children().length == 0){
curserMsg(`Please load your custom skins before using this command`, 'red', 6e3);
newMsg = '';
break;
}
newMsg = `My Total Custom Skins: ${$('[id^="skinCustomImg"').length}, worth ${($('[id^="skinCustomImg"').length * 1e6).toLocaleString('en-US')}`
break;
case 'skins': case 'boughtskins': case 'ownedskins': case 'totalskins': {
if($('#skinsBuy [id^="skinContainer"]').length == 0){
curserMsg(`Please load your owned skins from the agma.io skin store`, 'red', 6e3);
newMsg = '';
break;
}
let ownedSkinsArr = [], edSkins = 0, totalValue = 0;
$('#skinsBuy [id^="skinUseBtn"]').each(function(){
ownedSkinsArr.push(+$(this)[0].id.match(/\d+$/gm)[0]);
})
for(let i of skinsArr){
if(ownedSkinsArr.includes(i.id)){
i.price > 2e6 ? ++edSkins : totalValue += i.price;
}
}
newMsg = `My Total Bought Skins: ${$('#skinsBuy [id^="skinUseBtn"').length}, worth ${totalValue.toLocaleString('en-US')} coins (${edSkins} lim. ed.)`;
break;
}
case 'ownedwearables': case 'wearables': case 'wears': case 'totalwearables': case 'totalwears': {
if(!$('#fsfb-wearsloaded').length){
curserMsg(`Please load your wearables before using this command`, 'red', 6e3);
newMsg = ''; // aft
break;
}
let wearsPrice = [0], edSkins = 0;
$('[id^="wearableUseBtn"]').each(function(){
if ($(this).parents().eq(0).siblings('p').text() == '(Limited Edition)') edSkins++;
wearsPrice.push(+$(this).parents().eq(0).siblings('span.win-price').text().replace(/,/g, ''));
});
newMsg = `My Total Wearables: ${$('[id^="wearableUseBtn"]').length}, worth ${sigma(wearsPrice).toLocaleString('en-US')} coins (${edSkins} lim. ed.)`;
break;
}
case 'cloak': case 'invis': case 'invisibility': {
let status, time;
$('#infoContent>p').each(function(){
if($(this).text().includes('Cloaked: ')) status = $(this).text().match(/(?<=Cloaked: ).+/gm);
if($(this).text().includes('Cloak Time: ')) time = $(this).text().match(/(?<=Cloak Time: ).+/gm);
})
newMsg = status == null || time == null ? `Cloak: inactive` : `Cloak ${status}, with ${time} remaining`;
break;
}
case 'addfriend': case 'add':
if(params?.length == 0){
curserMsg(`Please add a username after the command, such as: ${userPreferences.chatPrefix}${command} Fishyyy`, 'red', 6e3);
newMsg = '';
break;
}
unsafeWindow.friendAdd(params[0]);
break;
case 'partymembers': case 'partymember': case 'party':
newMsg = $('#partyContent').children().length - 1 > 0 ? `In a party, with ${$('#partyContent').children().length - 1} members` : `Party members: not in a party`;
break;
case 'players': case 'ply': case 'plyrs': case 'serverplayers': case 'totalplayers':{
let plyr_max = [0, 0]; // players[0] | maxplayers[1]
$('[id^="serverPlayers"]').each(function(){
plyr_max = plyr_max.map((n, i) => n + +this.innerText.split('/')[i]);
});
let serverBots = getServerValue('bots') ?? 0, totalbots = 0;
for(let server in svInfo){
totalbots += (svInfo[server].bots ?? 0);
}
newMsg = `Server Players: ${$('[id^="serverRow"].active>[id^="serverPlayers"]').text()}${serverBots ? ` (${serverBots} bots)` : ''}, Total Players: ${plyr_max[0].toLocaleString('en-US') + '/' + plyr_max[1].toLocaleString('en-US')} ${totalbots ? `(${totalbots} bots)` : ''}`;
break;
}
case 'server':
newMsg = `Currently playing in ${$('[id^="serverRow"].active>*').first().text()}`;
break;
case 'abil': case 'abils': case 'ability': case 'abilities': { // not finished
let arr = [], newStr = '';
$('.checkmark').each(function(){
if($(this).css('display') == 'block') arr.push($(this).siblings().eq(1).attr('item'));
})
if(misc_settings.abil?.[currentUser] != null || arr.length){
const abil = misc_settings.abil?.[currentUser],
map = {
18: 'Freeze',
22: 'Invis Cloak',
20: '2x Spawn',
23: '2x Exp'
}
if(currentUser == "Please Login First"){
newMsg = `Active Abilities: none`;
break;
}
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, ') : ', ');
newMsg = `Active Abilities: ${newStr == '' ? 'none' : newStr}`;
newMsg = newMsg.replace(/,[^,]*$/g, '');
} else {
newMsg = `Active Abilities: none`;
}
break;
}
case 'xpproj': case 'xpprojected': case 'projectedxp': case 'projxp': case 'levelprojected': case 'lvlproj': case 'lvlprojected': {
const projectedHr = lastHrXP.length > 0 ? sigma(getProperty(lastHrXP.slice(-5), "gained")) * 12 : 0;
newMsg = `Next Hour Projected XP: ${xpStatsInPercentages ? round(convertToPerc(projectedHr, currentLevel) * 100, 2) + '%' : round(projectedHr).toLocaleString('en-US')}`;
break;
}
case 'projcoins': case 'projcoin': case 'projectedcoin': case 'projectedcoins': case 'coinsproj': case 'coinproj': case 'coinprojected': case 'coinsprojected': {
const projectedHr = lastHrCoins.length > 5 ? Math.round(sigma(getProperty(lastHrCoins.slice(-5), "gained")) * 12) : 0;
newMsg = `Next Hour Projected Coins: ${projectedHr.toLocaleString('en-US')}`;
break;
}
case 'xpremaining': case 'xprem': case 'remainingxp': case 'remxp': {
const xpRemaining = currentLevel ? currentLevel * 1e3 - currentPercent * currentLevel * 1e3 : 0,
newMsg = `XP Remanining: ${xpStatsInPercentages ? round(convertToPerc(xpRemaining, currentLevel) * 100, 2) + '%' : round(xpRemaining).toLocaleString('en-US')}`;
break;
}
case 'coinsremaining': case 'coinsrem': case 'remainingcoins': case 'remcoins': {
const coinsRemaining = currentCoins ? 25e4 - currentCoins % 25e4 : 0;
newMsg = `Coins Remanining: ${coinsRemaining.toLocaleString('en-US')}`;
break;
}
case 'xplasthour': case 'lasthourxp': case 'hrxp': case 'xphr': case 'hourxp': case 'xphour': {
const lastHr = lastHrXP.length > 0 ? sigma(getProperty(lastHrXP, "gained")) : 0;
newMsg = `XP Last Hour: ${xpStatsInPercentages ? round(convertToPerc(lastHr, currentLevel) * 100, 2) + '%' : round(lastHr).toLocaleString('en-US')}`;
break;
}
case 'coinslasthour': case 'lasthourcoins': case 'hrcoins': case 'coinshr': case 'hourcoins': case 'coinshour': {
const lastHr = lastHrCoins.length > 0 ? Math.round(sigma(getProperty(lastHrCoins, "gained"))) : 0
newMsg = `Coins Last Hour: ${lastHr.toLocaleString('en-US')}`;
break;
}
case 'xplastmin': case 'xplastminute': case 'lastminxp': case 'lastminutexp': case 'minxp': case 'minutexp': case 'xpmin': {
const lastMin = lastMinXP.length > 0 ? sigma(getProperty(lastMinXP, "gained")) : 0;
newMsg = `XP Last Minute: ${xpStatsInPercentages ? round(convertToPerc(lastMin, currentLevel) * 100, 2) + '%' : round(lastMin).toLocaleString('en-US')}`;
break;
}
case 'coinslastmin': case 'coinslastminute': case 'lastmincoins': case 'lastminutecoins': case 'coinsxp': case 'minutecoins': case 'coinsmin': {
const lastMin = lastMinCoins.length > 0 ? Math.round(sigma(getProperty(lastMinCoins, "gained"))) : 0;
newMsg = `Coins Last Minute: ${lastMin.toLocaleString('en-US')}`;
break;
}
case 'xplast12s': case 'xplast12sec': case 'xplast12seconds': case 'last12secondsxp': case 'lastminutexp': case 'xp12s': case '12sxp': case 'xp12sec': {
const last12sec = lastMinXP.length > 0 ? lastMinXP[lastMinXP.length - 1].gained : 0;
newMsg = `XP Last 12 Seconds: ${xpStatsInPercentages ? round(convertToPerc(last12sec, currentLevel) * 100, 2) + '%' : round(last12sec).toLocaleString('en-US')}`;
break;
}
case 'xpsession': case 'sessionxp': case 'xpsesh': case 'sesh': case 'seshxp': case 'online': {
const sessionXP = currentXP && accounts[currentUser] ? currentXP - accounts[currentUser].xp : 0;
newMsg = `Session XP: ${xpStatsInPercentages ? round(currentLevel && accounts[currentUser] ? ((round(currentPercent, 3) + currentLevel) - accounts[currentUser].lvl) * 100 : 0, 2) + '%' : round(sessionXP).toLocaleString('en-US')}, Session Length: ${msToTime(Date.now() - scriptStartXP)}`;
break;
}
case 'lifetimexp': case 'xplifetime':
newMsg = `Lifetime XP: ${round(currentXP ?? 0).toLocaleString('en-US')}`;
break;
case 'coinssession': case 'sessioncoins': case 'coinssesh': case 'seshcoins': {
const sessionCoins = currentCoins && accounts[currentUser] ? Math.round(currentCoins - accounts[currentUser].coins): 0;
newMsg = `Session Coins: ${sessionCoins.toLocaleString('en-US')}, Session Length: ${msToTime(Date.now() - scriptStartCoins)}`;
break;
}
case 'ratewaifu': case 'waifu': case 'waifurating': case 'waifurate': case 'howwaifu':
cmdVerif = false;
if(params?.length == 0){
if(currentUser == "Please Login First"){
newMsg = `My Waifu Rating: 0 / 10`;
break;
}
newMsg = `My Waifu Rating: ${Math.round(+String(Math.round(hashCode(currentUser, 5) * 102.2)).slice(-3, -1) / 10)} / 10`;
break;
}
newMsg = `${params[0]}'s Waifu Rating: ${Math.round(+String(Math.round(hashCode(params[0], 5) * 102.2)).slice(-3, -1) / 10)} / 10`;
break;
case 'ratepro': case 'pro': case 'prorating': case 'prorate': case 'howpro':
cmdVerif = false;
if(params?.length == 0){
if(currentUser == "Please Login First"){
newMsg = `I am 0% pro`;
break;
}
newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 5) * 194.3)).slice(-4, -1) / 10)}% pro`;
break;
}
newMsg = `${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 5) * 194.3)).slice(-4, -1) / 10)}% pro`;
break;
case 'ratedog': case 'dog': case 'dograting': case 'dograte': case 'howdog':
cmdVerif = false;
if(params?.length == 0){
if(currentUser == "Please Login First"){
newMsg = `I am 0% dog`;
break;
}
newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 6) * 189.3)).slice(-4, -1) / 10)}% dog`;
break;
}
newMsg = `${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 6) * 189.3)).slice(-4, -1) / 10)}% dog`;
break;
case 'rateking': case 'king': case 'kingrating': case 'kingrate': case 'howking':
cmdVerif = false;
if(params?.length == 0){
if(currentUser == "Please Login First"){
newMsg = `I am 0% king`;
break;
}
newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 5) * 389.3)).slice(-4, -1) / 10)}% king`;
break;
}
newMsg = `${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 5) * 389.3)).slice(-4, -1) / 10)}% king`;
break;
case 'dice': case 'die': case 'roll': case 'rolldice': {
const sides = params?.length == 0 || isNaN(+params[0]) ? 6 : +params[0];
newMsg = `Rolled a dice with ${sides.toLocaleString('en-US')} sides, landed on ${Math.round(RNG(1, sides)).toLocaleString('en-US')}`;
break;
}
case 'rng': case 'random': case 'randomnumber': case 'number': case 'num': {
const min = params?.length < 1 || isNaN(+params[0]) ? 0 : +params[0];
const max = params?.length < 2 || isNaN(+params[1]) ? min + 10 : +params[1];
if(min > max){
curserMsg(`Please make sure your minimum (${min}) is less than your maximum (${max})`, 'red', 5e3);
newMsg = '';
break;
}
newMsg = `Generated a random number between ${min.toLocaleString('en-US')} and ${max.toLocaleString('en-US')}, chose: ${Math.round(RNG(min, max)).toLocaleString('en-US')}`;
break;
}
case 'ratefriends': case 'friendsrating': case 'friendsrate': case 'howfriends':
cmdVerif = false;
if(params?.length == 0){
if(currentUser == "Please Login First"){
newMsg = `I am 0% friends with a frog`;
break;
}
newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 4) * 283.7)).slice(-4, -1) / 10)}% friends with a frog`;
break;
}
if(params?.length == 1){
if(currentUser == "Please Login First"){
newMsg = `I am 0% friends with a ${params[0]}`;
break;
}
newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 4) * 283.7) + Math.round((hashCode(params[0], 5) * 405.2))).slice(-4, -1) / 10)}% friends with a ${params[0]}`;
break;
} else {
newMsg = `${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 4) * 283.7) + Math.round((hashCode(params[0], 5) * 405.2))).slice(-4, -1) / 10)}% friends with ${params[1]}`;
break;
}
case 'rateenemies': case 'enemiesrating': case 'enemiesrate': case 'howenemies': case 'enemies':
cmdVerif = false;
if(params?.length == 0){
if(currentUser == "Please Login First"){
newMsg = `I am 0% enemies with a frog`;
break;
}
newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 4) * 164.45)).slice(-5, -2) / 10)}% enemies with a frog`;
break;
}
if(params?.length == 1){
if(currentUser == "Please Login First"){
newMsg = `I am 0% enemies with a ${params[0]}`;
break;
}
newMsg = `I am ${Math.round(+String(Math.round(hashCode(currentUser, 4) * 164.45) + Math.round((hashCode(params[0], 5) * 405.2))).slice(-5, -2) / 10)}% enemies with a ${params[0]}`;
break;
} else {
newMsg = `${params[0]} is ${Math.round(+String(Math.round(hashCode(params[0], 4) * 164.45) + Math.round((hashCode(params[0], 5) * 405.2))).slice(-5, -2) / 10)}% enemies with ${params[1]}`;
break;
}
case 'flip': case 'coinflip': case 'heads': case 'tails': case 'flipcoin': {
newMsg = `Coin flipped! Landed on ${Math.round(RNG(0, 1)) ? 'heads' : 'tails'}`;
break;
}
case 'script': case 'version': case 'fsfb': case 'v':
newMsg = `Using fsfb script! Version ${version}`
break;
case 'time': case 'localtime': case 'localetime': case 'date':
newMsg = `My Time: ${new Date().toLocaleString()}`
break;
case 'leaderboard': case 'leader': case 'lb':
newMsg = `My leaderboard position: ${!playerIsAlive() || leaderboardPos == null ? 'none, not alive' : leaderboardPos}`;
break;
case 'altcaps': case 'altcap': case 'altscaps': case 'altscap':
newMsg = isolatedMsg.toLowerCase().split('').map((v, i) => i % 2 == 0 ? v : v.toUpperCase()).join('');
cmdVerif = false;
break;
case 'sparkles': case 'sprk':
newMsg = '✨' + isolatedMsg + '✨';
cmdVerif = false;
break;
case 'cfix': case 'canvasfix': {
document.querySelectorAll('canvas').forEach(canvas => ({ innerWidth: canvas.width, innerHeight: canvas.height } = unsafeWindow));
// const canvas = document.querySelector('#canvas');
// ({ innerWidth: canvas.width, innerHeight: canvas.height } = unsafeWindow);
curserMsg('Canvas was reset.', 'green', 5e3);
newMsg = '';
break;
}
case 'solo': case 'soloserver': // message from Miracle Scripts (another great agma script) - https://greasyfork.org/scripts/391142
newMsg = ':warning: SOLO SERVER :warning: No teaming!! No hay equipo!! Pas d\'équipe!! Kein Teaming!! لا فريق';
cmdVerif = false;
break;
case 'facts': case 'fact': case 'funfact': {
const fact = randomFact();
newMsg = fact ? randomFact() : '';
fact ?? curserMsg(`Fsfb is unable to access the list of facts. Possible cause: not using tampermonkey or not using the latest version of tampermonkey.`, 'red', 6e3);
break;
}
case 'fastsplit': case 'fs': case 'fastsplits': case 'fastsplitdelay': case 'delay': {
const fs = settings.fastsplit_hotkeys;
newMsg = `My fast onesplit delays: ${fs[1].val}, ${fs[2].val}; fast double: ${fs[4].val}, ${fs[5].val}; fast triple: ${fs[7].val}, ${fs[8].val}`;
break;
}
case "dailyprogress": case "dailychallenge": case "progress": case "challengeprogress": case "challenge": case "challenges": {
let progress = +$("#challengeProgress").text();
let goal = +$("#challengeGoal").text();
let percent = ((progress / goal) * 100).toFixed(1);
newMsg = `Daily Challenge Progress: ${progress.toLocaleString('en-US')} / ${goal.toLocaleString('en-US')} (${percent}% completed)`;
break;
}
case "sp": case "sorapoint": case "sorapoints": case "sps": {
let soraPoints = +$('.sora-points-inv.chall-mythic').text();
newMsg = `Sora Points: ${soraPoints.toLocaleString('en-US')}`;
break;
}
case "keys": case "key": case "challengekeys": case "challengekey": {
let challengeKeys = +$('.mystery-keys.chall-mythic').text();
newMsg = `Challenge Keys: ${challengeKeys.toLocaleString('en-US')}`;
break;
}
case "stars": case "challengestars": case "challengestar": case "star": {
let challengeStars = +$('.challenge-stars.chall-mythic').text();
newMsg = `Challenge Stars: ${challengeStars.toLocaleString('en-US')}`;
break;
}
case "lastshout": case "lshout":
$chtbox.blur();
if(shouts.length == 0){
swal({
title: "No shouts detected.",
type: "error"
});
} else {
let shout = shouts[shouts.length-1];
let wasRemoved = false;
if(shout.isModerator == true && shouts.length >= 2){
shout = shout[shouts.length-2];
wasRemoved = true;
}
swal({
title: "Last shout info:",
text: `
Nickname: ${shout.name} \n
Message: ${shout.message} \n
Translated: ${shout.messageTranslated} \n
Time: ${(new Date(shout.time)).toString()} \n
Was Removed: ${wasRemoved ? "Yes" : "No"}
`,
type: "info"
});
newMsg = "";
}
$chtbox.blur();
break;
case "allshouts": case "dumpshouts": {
$chtbox.blur();
console.log("Collected shouts:");
for(let shout of shouts){
console.log(`
Nickname: ${shout.name}
Message: ${shout.message}
Translated: ${shout.messageTranslated}
Time: ${(new Date(shout.time)).toString()}
`);
newMsg = "";
}
console.log("Total: ", shouts.length , "shouts");
swal({
title: "Dumped " + shouts.length + " shouts into console.",
type: "info"
});
break;
}
default:
newMsg = $chtbox.val();
return;
}
}
// (unsafeWindow.kfjsdafl != null) ? $chtbox.val(newMsg) : $chtbox.val(''), console.log(newMsg);;
$chtbox.val(newMsg == '' ? '' : originalMsg + (newMsg?.[cmdVerif ? 'replace' : '']?.(/[youaie](?!.: )/gmi, m => cmap[m]) || newMsg));
})
}
// check if player is alive
let playerAlive = false, timeAlive;
const playerIsAlive = () => playerAlive && $('#advert').css('display') != 'none' ? false : playerAlive;
unsafeWindow.playerIsAlive = playerIsAlive;
const _setNick = unsafeWindow.setNick;
unsafeWindow.setNick = function(){
if(userPreferences.messageBanWarning && /a\s*g\s*a\s*r\s*p\s*o\s*w\s*e\s*r/gmi.test(arguments[0])){
curserMsg("FSFB WARNING: FSFB script has censored your nickname as it has detected that your nickname may've contained a word/phrase that agma.io gives a 24 hour IP ban for. This FSFB feature can be disabled in the tampermonkey script settings.", 'red', 1e4);
const newNick = (arguments[0].replace(/a\s*g\s*a\s*r\s*p\s*o\s*w\s*e\s*r/gmi, match => '*'.repeat(match.length)));
arguments[0] = newNick;
document.getElementById('nick').value = newNick;
}
if(settings.name_color[0].active && settings.name_color[1].active && arguments[0] != '' && (!/^\[(?:\W{3}|\W{6})\]/gmi.test(arguments[0]) || arguments[0] === document.querySelector('#nick')?.value)){ // check if name already has color tag
const tag = `[${encodeCustomNameColor(settings.name_color[1].color)}${settings.name_color[2].active ? encodeCustomNameColor(settings.name_color[2].color) : ''}]`;
arguments[0] = tag + arguments[0];
}
if(arguments[1] || !playerAlive) timeAlive = Date.now(); // respawned
playerAlive = true;
setTimeout(() => { svSwitch = true }, 1000);
return _setNick.apply(this, arguments);
}
const _closeAdvert = unsafeWindow.closeAdvert
unsafeWindow.closeAdvert = function(){
playerAlive = false;
return _closeAdvert.apply(this, arguments);
}
const getPowerMessage = line => {
let obj = {};
[1, 2, 3].forEach(n => (obj[`string${n}`] = `Inv (${n}/?): `));
updatePwCount();
const newPws = {
Recombine: pws.rec,
Speed: pws.spd,
Growth: pws.grw,
Virus: pws.vrs,
Mothercell: pws.mtcl,
Portal: pws.prtl,
'Gold Block': pws.gblk,
Freeze: pws.fz,
Push: pws.psh,
Wall: pws.wall,
'Anti-Freeze': pws.afz,
'Anti-Recombine': pws.arc,
Shield: pws.shld,
'Frozen Virus': pws.fvs
}
for(let i in newPws){
let add = newPws[i] == '' ? '' : `${newPws[i]} ${i}, `;
if(obj.string1.length + add.length <= 100) obj.string1 += add;
else if(obj.string2.length + add.length <= 100) obj.string2 += add;
else obj.string3 += add;
}
if(obj.string1 == `Inv (1/?): ` && line == 1) return 'Inv (1/1): no powers';
if(obj['string' + line] == `Inv (${line}/?): `){
curserMsg(`This inventory line doesn't exist! Try a smaller number`, 'red', 6e3);
return '';
}
let totalLines = 1;
if(obj.string3 == 'Inv (3/?): ' && obj.string2 != 'Inv (2/?): ') totalLines = 2;
else if(obj.string3 != 'Inv (3/?): ' && obj.string2 != 'Inv (2/?): ') totalLines = 3;
return obj['string' + line].replace(/,[^,]*$/g, '').replace(/\?/g, totalLines);
}
let pws = {rec: '', spd: '', grw: '', vrs: '', mtcl: '', prtl: '', gblk: '', fz: '', psh: '', wall: '', afz: '', arc: '', shld: '', fvs: ''};
const updatePwCount = () => {
$('.inventory-box').each(function(){
const map = {
Wall : 'wall',
AntiFreeze: 'afz',
AntiRecombine: 'arc',
Shield: 'shld',
FrozenVirus: 'fvs',
Recombine: 'rec',
Speed: 'spd',
Growth: 'grw',
SpawnVirus: 'vrs',
SpawnMothercell: 'mtcl',
SpawnPortal: 'prtl',
SpawnGoldOre: 'gblk',
Freeze: 'fz',
'360Shot': 'psh'
}
if(map[$(this)[0]?.id.slice(3)] != null) pws[map[$(this)[0].id.slice(3)]] = $(this).css('display') != 'none' ? $(this).children().eq(0).text() || '1' : '';
})
}
$('#fsfb-export-btn').on('click', () => {
if(!$('#fsfb-sect-imexport>label>input').is(':checked')) return void(curserMsg('You need to select at least one setting to export!', 'red'));
if($('#fsfb-custom-bg').is(':checked')) curserMsg('Exporting settings with a custom background is likely to increase the file size', 'red', 8e3);
let script_settings = {},
script_themes = {};
for (let i in settings) { // put the settings & themes into different objects
if (i == "theme" || i == "theme_boxes" || i == "name_color") script_themes[i] = settings[i];
else if (i != "export_import") script_settings[i] = settings[i];
}
let bgImg = null;
const downloadFile = () => {
let obj = {
selected: {
game_settings: $('#fsfb-game-settings').is(':checked'),
game_controls: $('#fsfb-game-controls').is(':checked'),
game_background: $('#fsfb-custom-bg').is(':checked'),
script_settings: $('#fsfb-script-settings').is(':checked'),
script_theme: $('#fsfb-theme-settings').is(':checked')
},
game_settings: localStorage.settings,
game_controls: localStorage.hotkeys,
game_background: bgImg,
script_settings: script_settings,
script_theme: script_themes
}
// if the setting is turned off, then dont need to export it - set it to null
for(let i in obj){
if(i != 'selected' && !obj.selected[i]) obj[i] = null;
}
const a = document.createElement('a'),
file = new Blob([JSON.stringify(obj)], {type: "text/plain"});
a.href = URL.createObjectURL(file);
a.download = "fsfb script settings";
a.click();
URL.revokeObjectURL(a.href); // download a txt file of exported settings
}
if($('#fsfb-custom-bg').is(':checked')){
let db, req = unsafeWindow.indexedDB.open("AgmaDB", 1);
req.onsuccess = function (x) {
db = req.result;
db.onclose = (c) => { db = null; }
db.onversionchange = (c) => { db.close(), db = null; }
let _ = db.transaction("general", "readonly").objectStore("general").get("cbgDataURL");
_.onsuccess = () => {
bgImg = _.result;
downloadFile();
}
}, req.onupgradeneeded = function (c) {
let req = c.target.result;
if (!req.objectStoreNames.contains("general")) {
req.createObjectStore("general");
downloadFile();
} else downloadFile();
};
} else {
downloadFile();
}
});
$('#fsfb-import-btn').on('click', () => {
if(!$('#fsfb-sect-imexport>label>input').is(':checked')) return void(curserMsg('You need to select at least one setting to import!', 'red'));
if($('#fsfb-custom-bg').is(':checked')) curserMsg('Pasting in settings that include a background image is likely to cause initial lag', 'red', 0);
swal({
title: "Import Settings",
text: "Add your exported settings below and press OK to continue.",
type: "input",
showCancelButton: true,
closeOnConfirm: false,
animation: "slide-from-top",
inputPlaceholder: "Paste exported settings here"
},
function(inputVal){
if (inputVal == null) return false;
if (inputVal == "") {
swal.showInputError("Please don't leave the input empty!");
return false
}
try {
let val = JSON.parse(inputVal);
if(val.selected.script_settings && $('#fsfb-script-settings').is(':checked')){
for (let i in val.script_settings) {
for (let j in val.script_settings[i]) {
for(let x in settings){
for(let y in settings[x]){
if(val.script_settings[i][j].id == settings[x][y].id) settings[x][y] = val.script_settings[i][j];
}
}
}
}
}
if(val.selected.script_theme && $('#fsfb-theme-settings').is(':checked')){
for (let i in val.script_theme) {
for (let j in val.script_theme[i]) {
for(let x in settings){
for(let y in settings[x]){
if(val.script_theme[i][j].id == settings[x][y].id) settings[x][y] = val.script_theme[i][j];
}
}
}
}
}
if(val.selected.game_settings && $('#fsfb-game-settings').is(':checked')) localStorage.setItem('settings', val.game_settings);
if(val.selected.game_controls && $('#fsfb-game-controls').is(':checked')) localStorage.setItem('hotkeys', val.game_controls);
if(val.selected.game_background && $('#fsfb-custom-bg').is(':checked')){
let db, req = unsafeWindow.indexedDB.open("AgmaDB", 1);
req.onsuccess = function (x) {
db = req.result;
db.onclose = (c) => { db = null; }
db.onversionchange = (c) => { db.close(), db = null; }
let _ = db.transaction("general", "readwrite").objectStore("general").put(val.game_background, "cbgDataURL");
}, req.onupgradeneeded = function (c) {
let req = c.target.result;
if (!req.objectStoreNames.contains("general")) {
req.createObjectStore("general");
}
};
}
// if any agma controls were imported, need to refresh bc it's set using localstorage
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'))){
swal({
title: "Please Refresh!",
text: "Refreshing the page is required for your imported agma settings to take effect",
type: "warning",
});
} else swal({ title: "Settings Successfully Imported!", type: "success" });
saveSettings();
styleActiveSettings();
updateScriptSettingsUI();
} catch (error){
swal({
title: "Something went wrong!",
text: "Please make sure you've entered in valid settings",
type: "error"
});
}
});
});
$('#fsfb-settings-right').append(``)
if(userPreferences.hideAds){
localStorage.ad_l_time = "9e99"; // smth like time since last ad
$('[id^="agma-io_"], [id^="adWrapper"], #preroll').addClass("fsfb-removeAds"); // move ads way off the screen
} else if(localStorage.ad_l_time == "9e99")localStorage.ad_l_time = Date.now();
let {innerWidth: width, innerHeight: height} = unsafeWindow;
$(unsafeWindow).on('resize', () => ({innerWidth: width, innerHeight: height} = unsafeWindow));
let pointMove;
const linesplit = () => {
if(!linesplitting) return;
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}];
if(userPreferences.linesplitClosestSide){
let closestSide = [mosY / height, (height - mosY) / height, mosX / width, (width - mosX) / width]; // top, bottom, left, right
closest = points[closestSide.indexOf(Math.min(...closestSide))];
} else {
let distance = p => Math.sqrt(Math.pow(mosX - p.x, 2) + Math.pow(mosY - p.y, 2)),
closestPoint = points.reduce((a, b) => distance(a) < distance(b) ? a : b);
for (let i = 0; i < points.length; i++) {
if (closestPoint.x == points[i].x && closestPoint.y == points[i].y) closest = points[i]
}
}
pointMove = {x: closest.nx, y: closest.ny};
$('#canvas').trigger($.Event('mousemove', { clientX: closest.nx, clientY: closest.ny }));
$("#linesplit-markers div").css('background-color', 'transparent');
$('#linesplit-' + closest.n).css('background-color', '#e25615');
}
let confBtns = document.getElementsByClassName('purchase-btn confirmation');
const btnsArr = Array.from(document.getElementsByClassName('purchase-btn confirmation'));
const changeHTML = (index, price, id, name) => {
setTimeout(() => {
const amtDropdown = document.getElementById('shopAmountDropdown')
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.';
const dropdownChange = () => {
if (amtDropdown.value == "custom") return buyCstmAmt(price, id, name);
let priceSum = (amtDropdown.value * price).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
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.';
};
amtDropdown.addEventListener('change', dropdownChange);
}, 50);
};
const buyCstmAmt = (price, id, name) => {
swal({
title: "Enter Purchase Amount",
text: "How many " + name + " would you like to buy?",
html: true,
type: "input",
showCancelButton: true,
closeOnConfirm: false,
inputPlaceholder: "Input amount here"
},
function(input){
let pwAmt = +input,
priceTotal = pwAmt * price;
if (input == null) return false;
if (input == "") {
swal.showInputError("Please don't leave the input empty!");
return false
}
if(!/^[0-9]+$/.test(input) || !(+input >>> 0 === parseFloat(+input))){
swal.showInputError("Please only enter positive integers!");
return false
}
swal({
title: ' Confirm ',
text: '
If you click "Buy", you will purchase this item. It will cost ' + priceTotal.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' in total. You chose to purchase ' + pwAmt.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") + ' ' + name + '
',
html: true,
type: 'warning',
showCancelButton: true,
closeOnConfirm: true,
confirmButtonColor: '#4caf51',
confirmButtonText: 'Yes, confirm purchase',
cancelButtonText: 'No, cancel purchase'
}, function(confirmed){
if(confirmed && $('.confirm').is(':visible')) buyPw(id, pwAmt);
})
});
};
// buy a certain amount of powers including > 255
const buyPw = (shopID, pwAmt) => {
if (pwAmt < 1) return;
if (pwAmt > 255) {
for (let i = 0; i < ~~(pwAmt / 255); i++) setTimeout(() => purchaseItem(shopID, 255), 500 * i + 250);
purchaseItem(shopID, pwAmt % 255);
} else purchaseItem(shopID, pwAmt);
};
if(userPreferences.improvedShop){
// add event listeners to dropdown buttons
for (let i = 0; i < confBtns.length; i++) {
confBtns[i].onclick = function () {
setTimeout(() => {
if (document.getElementById('shopAmountDropdown') != null) document.getElementById('shopAmountDropdown').innerHTML += ' ';
}, 5);
}
}
// add event listeners to shop buttons
for (let i = 0; i < 16; i++) {
if (i != 10 && i != 11){
btnsArr[i].addEventListener('click', function(e){
if(!e.isTrusted && !quickBuying) return;
let index = i;
changeHTML(index, btnsArr[index].getAttribute('price'), btnsArr[index].getAttribute('item'), $('.purchase-btn.confirmation[item="' + btnsArr[index].getAttribute('item') + '"]').prev().find('h3').eq(0).text());
});
}
}
}
// listen for when a min pack is bought
let minBoughtAmt = {};
if(userPreferences.extraChatCommands){
let lastClickedPrice, currentPrice, lastID;
const confirmClicked = () => {
setTimeout(() => {
if($('.sweet-alert h2').text() != "Success!" || lastClickedPrice != currentPrice) return;
const map = {
1: '10 Bots 1 Hour',
2: '40 Bots 1 Hour',
3: '50 Bots 2 Hours',
4: '80 Bots 1 Hour',
5: '100 Bots 4 Hours',
6: '125 Bots 8 Hours',
7: '300 Bots 24 Hours',
8: '100 MASS Bots 1 Hour',
9: '100 Bots 24 Hours',
10: '125 Bots 48 Hours',
11: '300 Bots 72 Hours',
12: '100 MASS Bots 24 Hours'
}
minBoughtAmt[currentUser] = {...minsChatAmt[currentUser], ... {chatAmt: map[lastID], started: false}};
}, 900);
};
$('.purchase-btn2.confirm-minion[item]').on('click', function () {
lastClickedPrice = this.getAttribute('price').replace(/,/g, '');
lastID = this.getAttribute('item');
$('.confirm').attr('disabled', 'true'); // disable so user doesn't buy early - swal is slow to load text
setTimeout(() => {
$('.confirm').removeAttr('disabled');
currentPrice = $('.sweet-alert.showSweetAlert.visible p').eq(0).text().replace(/\D+/g, '');
}, 750);
setTimeout(() => $('.confirm')[0].addEventListener('click', confirmClicked), 500);
})
}
// context menu: click on skin -> skin ID to clipboard, click on name -> name to clipboard
$('#contextPlayer').on('click', e => {
if(!userPreferences.rightClickCopyInfo) return;
if($('#contextPlayerSkin').width() + $('#contextPlayerSkin').offset().left + 5 > e.pageX){ // bcs #contextMenu event :dog:
// cell was clicked
if($('#contextPlayerSkin').css('background-image') != "none"){
let skinID = $('#contextPlayerSkin').css('background-image').match(/(?<=\/skins\/)[0-9]+/gm)[0]; // get the skin image, then match only the skin's ID
navigator.clipboard.writeText(skinID).then(function() {
curserMsg('Skin ID of ' + skinID + ' was copied to your clipboard.', 'green');
}, function() {
curserMsg('Something went wrong. Nothing was added to your clipboard.', 'red');
});
} else if($('#contextPlayerSkin').css('background-color') == 'rgb(51, 51, 51)') curserMsg('No player selected. Nothing was added to your clipboard.', 'red');
else curserMsg('No skin equipped. Nothing was added to your clipboard.', 'red');
} else { // name was clicked
if($('#contextPlayerSkin').css('background-color') == 'rgb(51, 51, 51)') curserMsg('No player selected. Nothing was added to your clipboard.', 'red');
else {
navigator.clipboard.writeText($('#contextPlayerName').text()).then(function() {
curserMsg('Nickname: "' + $('#contextPlayerName').text() + '" was copied to your clipboard', 'green');
}, function() {
curserMsg("Something went wrong. Nothing was added to your clipboard.", "red");
});
}
}
});
// little bar at the top of the screen that tells u stuff
let curserTimeout;
function curserMsg(msg, color, time, html){
let colorTable = {
"green": "rgb(0, 192, 0)",
"red": "rgb(255, 0, 0)",
"gray": "rgb(153, 153, 153)",
"yellow": "rgb(255, 170, 0)",
"orange": "rgb(255, 149, 0)",
"lightgray": "rgb(194, 194, 194)",
"white": "rgb(255, 255, 255)",
"purple": "rgb(190, 138, 255)"
}
color = colorTable[color];
clearTimeout(curserTimeout);
$('#curser')[(html ?? false) ? "html" : "text"](msg).show().css('color', color)
if(time != 0) curserTimeout = setTimeout(() => $('#curser').fadeOut(400), time ?? 4e3);
}
unsafeWindow.curserMsg = curserMsg;
let minTimeRemaining = 0;
const startMin = unsafeWindow.strMin;
unsafeWindow.strMin = function(){
minsStart();
return startMin.apply(this, arguments);
}
let minsChatAmt = {},
accGoldMem = {};
const minsStart = async() => {
let minAmt, minsStarted = false;
const waitUntil1 = (condition) => new Promise(resolve => {
let interval = setInterval(() => {
$('#infoContent').children().each(function(){
if($(this).text().includes('Minion Time:')) minsStarted = true;
})
condition() && (clearInterval(interval), resolve());
}, 100);
setTimeout(() => { (clearInterval(interval), resolve()); }, 1e4);
});
await waitUntil1(() => minsStarted == true);
if(!minsStarted) return;
$('#infoContent').children().each(function(){
if($(this).text().includes('Minion Time:')) minTimeRemaining = $(this).find('span').text();
if($(this).text().includes('Minions:')) minAmt = $(this).find('span').text();
})
let timeArr = minTimeRemaining.split(':'), msBotsTime = (3.6e6 * +timeArr[0]) + (6e4 * +timeArr[1]) + (1e3 * +timeArr[2]);
misc_settings.bots[currentUser] = {...misc_settings.bots[currentUser], ...{active: true, amt: minAmt, chatAmt: null, rem: msBotsTime, currMs: Date.now()}};
setStorage("fsfb-misc", misc_settings);
}
// ability time remaining
if(userPreferences.showRemainingAbilityTime){
let lastID;
const abilityIds = [18, 20, 22, 23];
abilityIds.forEach(id => {
const h5 = $(`.purchase-btn.confirmation[item="${id}"]`).parents().eq(0).find('div h5');
h5.clone().insertAfter(h5).addClass('fsfb-fake').hide();
const checkmarkImg = $(`.purchase-btn.confirmation[item="${id}"]`).parent().find(".checkmark:first");
$(`.purchase-btn[item="${id}"]`).on('click', function () {
lastID = this.getAttribute('item');
const hadAbilityBefore = checkmarkImg.is(":visible");
setTimeout(() => {
const isVisibleNow = $(`.purchase-btn.confirmation[item="${id}"]`).parent().find(".checkmark:first").is(":visible");
if(!hadAbilityBefore && isVisibleNow){
// purchase successful, set timer
misc_settings.abil[currentUser] = {...misc_settings.abil[currentUser], ...{[lastID] : Date.now()}};
setStorage("fsfb-misc", misc_settings);
}
}, 2500)
});
});
}
// sort wearables by owned
const waitUntil = condition => new Promise(resolve => {
let interval = setInterval(() => {
condition() && (clearInterval(interval), resolve());
}, 100);
setTimeout(() => { (clearInterval(interval), resolve()) }, 15e3);
});
if(userPreferences.sortWearablesByOwned){
$('#wearablesTab').on('click', async() => {
await waitUntil(() => $('#phpWearables li').length > 55);
if($('#phpWearables li').length <= 55) return;
$($('[id^="wearableUseBtn"]').get().reverse()).each(function(){
$($(this).parents().get(2)).insertBefore($("#phpWearables li:eq(0)"));
})
$('#phpWearables').append('');
});
}
// https://stackoverflow.com/questions/2424191/how-do-i-make-an-element-draggable-in-jquery
$.fn.draggable = function(){
var $this = this,
ns = 'draggable_'+(Math.random()+'').replace('.',''),
mm = 'mousemove.'+ns,
mu = 'mouseup.'+ns,
$w = $(unsafeWindow),
isFixed = ($this.css('position') === 'fixed'),
adjX = 0, adjY = 0;
$this.mousedown(function(ev){
let zoom = parseFloat($this.css('zoom')) || 1;
var pos = $this.offset();
if (isFixed) {
adjX = $w.scrollLeft(); adjY = $w.scrollTop();
}
var ox = (ev.pageX - pos.left) / zoom, oy = (ev.pageY - pos.top) / zoom;
$this.data(ns,{ x : ox, y: oy });
$w.on(mm, function(ev){
ev.preventDefault();
ev.stopPropagation();
if (isFixed) {
adjX = $w.scrollLeft(); adjY = $w.scrollTop();
}
var offset = $this.data(ns);
$this.css({ left: Math.round((ev.pageX - adjX - offset.x * zoom) / zoom), top: Math.round((ev.pageY - adjY - offset.y * zoom) / zoom) });
});
$w.on(mu, function(){
$w.off(mm + ' ' + mu).removeData(ns);
});
});
return this;
};
/* xp/coins statistics */
const updateTimeXP = 12e3; // xp bar updates every 12 seconds, don't change
let lastMinXP = [];
let lastHrXP = [];
let currentPercent, currentLevel, currentXP, currentCoins;
unsafeWindow.logStatsScriptXP = !1;
unsafeWindow.logStatsScriptCoins = !1;
let scriptStartCoins = Date.now();
let scriptStartXP = Date.now();
let accounts = {};
const guiDisplay = "none";
let coinsHTMLactive = false;
const updateTimeCoins = 6e3;
let lastMinCoins = [];
let lastHrCoins = [];
// add stats box html
const statsBody = document.createElement('div');
statsBody.setAttribute('id', 'stats-container');
statsBody.style.display = guiDisplay;
statsBody.innerHTML = `
');
} else {
$('#publicSkinsPage tbody').html('');
for(let i = 0; i < totalRows; i++) $('#publicSkinsPage tbody').append('
');
let currRow = 0, currColumn = 0;
for(let i of skinsSearchedArr){
if(++currColumn > 4) currRow++, currColumn = 1;
$('#publicSkinsPage tr').eq(currRow).append(`
${softSanitize(i.name)}
`)
}
}
}
// }, 300);
let pressTimer;
$('#fsfb-skinsearch').on('input', () => {
clearTimeout(pressTimer);
pressTimer = setTimeout(() => handlePress(), Math.round(300 / ($('#fsfb-skinsearch').val().length || 300)));
});
}
currentUser = $('#userCoins2')[0].innerText;
let user_abil = currentUser == 'Please Login First' ? null : misc_settings.abil?.[currentUser];
if(user_abil != undefined && userPreferences.showRemainingAbilityTime){
for(let i in user_abil){
let text = $(`.purchase-btn.confirmation[item="${i}"]`).parents().eq(0).find('div h5'),
active = $('#' + $(`.purchase-btn.confirmation[item="${i}"]`).parents().eq(0)[0].id + ' img').eq(1).css('display') != "none";
// has been 24h+ and the player hasn't logged out since it's expired
if(Date.now() - user_abil[i] > 8.64e7 && active){
text.eq(1).text('EXPIRED IF UNLOG');
text.eq(0).find('div h5').hide();
}
// has been >24h
else if(Date.now() - user_abil[i] < 8.64e7 && active){
text.eq(0).hide();
text.eq(1).text(msToTime(8.64e7 - (Date.now() - user_abil[i]))).show();
}
else { // has been 24h+ & player has logged out
text.eq(0).find('div h5').show();
text.eq(1).find('div h5').hide();
}
}
} else {
$('.white_shopdesc').show();
$('.white_shopdesc.fsfb-fake').hide();
}
if(accounts[currentUser] == null && currentUser !== 'Please Login First'){
xpInfo();
coinsInfo();
accounts = {...accounts, ...{[currentUser] : {coins: currentCoins, xp: currentXP, lvl: round(currentPercent, 3) + currentLevel}}};
}
if(accounts[currentUser] != null && accounts[currentUser].coins == 0) accounts[currentUser].coins = currentCoins;
if(accounts[currentUser] != null && accounts[currentUser].xp == 0) accounts[currentUser].xp = currentXP;
if(accounts[currentUser] != null && accounts[currentUser].lvl == 0) accounts[currentUser].lvl = round(currentPercent, 3) + currentLevel;
if(userPreferences.coinXPstats && intervalCount % 12 == 0){
xpInfo();
lastMinXP.push(new StatLog(round(currentXP, 3), round(currentPercent, 3) + currentLevel, currentUser, lastMinXP));
const prevObjXP = lastMinXP[lastMinXP.length - 1];
if(prevObjXP && prevObjXP.id % (6e4 / updateTimeXP) == 0) lastHrXP.push(new StatLog(round(currentXP, 3), round(currentPercent, 3) + currentLevel, currentUser, lastHrXP));
if(lastMinXP.length > 6e4 / updateTimeXP) lastMinXP.shift();
if(lastHrXP.length > 60) lastHrXP.shift();
if(!coinsHTMLactive && $('#stats-container').css('display') == 'block') updateUI();
// unsafeWindow.logStatsScriptXP && console.log(lastMinXP, lastHrXP);
}
if(userPreferences.coinXPstats && intervalCount % 6 == 0){
coinsInfo();
lastMinCoins.push(new StatLog(currentCoins, 0, currentUser, lastMinCoins));
const lastObjCoins = lastMinCoins[lastMinCoins.length - 1];
if(lastObjCoins && lastObjCoins.id % (6e4 / updateTimeCoins) == 0) lastHrCoins.push(new StatLog(currentCoins, 0, currentUser, lastHrCoins));
if(lastMinCoins.length > 6e4 / updateTimeCoins) lastMinCoins.shift();
if(lastHrCoins.length > 60) lastHrCoins.shift();
if(coinsHTMLactive && $('#stats-container').css('display') == 'block') updateUI();
// unsafeWindow.logStatsScriptCoins && console.log(lastMinCoins, lastHrCoins);
}
if(userPreferences.coinXPstats && $('#stats-container').css('display') == 'block')$('#stats-sesh-length').text(msToTime(Date.now() - (coinsHTMLactive ? scriptStartCoins : scriptStartXP)))
if(intervalCount % 3 == 0 && misc_settings?.statsPos != null){
statsboxPos = {top: $("#stats-container")[0].style.top, left: $("#stats-container")[0].style.left};
misc_settings.statsPos = statsboxPos;
setStorage("fsfb-misc", misc_settings);
}
if(intervalCount % 2 == 0){ // "You have an activated bot pack available: 100 XXL Bots 1 Hours! Restart your bots before they expire!"
if(chatmsgs != null && chatmsgs?.length > 2){ // "You have an activated bot pack available: 100 Bots 24 Hours! Restart your bots before they expire!"
for(let i of chatmsgs){
if(i.name == '' && i.cache != null && i.cache.color2 == '#ff8100'){
if(i.message.match(/(?<=(Welcome back to Agma, )).+/g)?.[0] == currentUser){
const msgBots = $('.memberType').text() == 'GOLD MEMBER' ? chatmsgs?.[chatmsgs.indexOf(i) - 2] : chatmsgs?.[chatmsgs.indexOf(i) - 1],
msgGM = $('.memberType').text() == 'GOLD MEMBER' ? chatmsgs?.[chatmsgs.indexOf(i) - 1] : null;
if(msgBots?.message.includes('Restart your bots before they expire!')){
if(misc_settings.bots[currentUser] != null) misc_settings.bots[currentUser].chatAmt = msgBots.message.match(/\d+.+\d Hours/g)[0];
minsChatAmt[currentUser] = {...minsChatAmt[currentUser], ... {amt: msgBots.message.match(/\d+.+\d Hours/g)[0], started: true}};
setStorage("fsfb-misc", misc_settings);
} else if(msgBots?.message.match(/(?<=(You have a new bot pack available: )).+(?=(! Start your bots in the minion panel.))/g)?.length){
if(misc_settings.bots[currentUser] != null) misc_settings.bots[currentUser].chatAmt = msgBots.message.match(/\d+.+\d Hours/g)[0];
minsChatAmt[currentUser] = {...minsChatAmt[currentUser], ... {amt: msgBots.message.match(/\d+.+\d Hours/g)[0], started: false}};
setStorage("fsfb-misc", misc_settings);
}
if(msgGM != null && msgGM.message.match(/(?<=You have )\d+(?= Days left of Gold Member!)/gm)?.length){
accGoldMem[currentUser] = {...accGoldMem[currentUser], ... {days: msgGM.message.match(/(?<=You have )\d+(?= Days left of Gold Member!)/gm)[0], has: true}};
} else {
accGoldMem[currentUser] = {...accGoldMem[currentUser], ... {has: false}};
}
} else if(i.message.match(/(?<=(You have a new bot pack available: )).+(?=(! Start your bots in the minion panel.))/g)?.length){
if(misc_settings.bots[currentUser] != null) misc_settings.bots[currentUser].chatAmt = i.message.match(/\d+.+\d Hours/g)[0];
minsChatAmt[currentUser] = {...minsChatAmt[currentUser], ... {amt: i.message.match(/\d+.+\d Hours/g)[0], started: false}};
setStorage("fsfb-misc", misc_settings);
}
}
}
}
}
if(currentServerId === 0){
try {
for(let { isCurrent, id } of JSON.parse(localStorage.gameservers)){
if(isCurrent) currentServerId = id;
}
if(currentServerId !== 0) svSwitch = true;
} catch {};
}
uisdoa && (unsafeWindow.__currentServerId = currentServerId);
if(currentUser == 'Please Login First' || $('#level').text() == 0) lastLoggedOut = Date.now();
changeTitle(settings.checkboxes[4].active ? currentUser == 'Please Login First' ? "Agma.io" : "Agma.io | " + currentUser : "Agma.io - A free multiplayer MMO game");
// if($('#friendAcceptAll').length > 0 && userPreferences.friendDeclineAll && $('#friendRejectAll').length < 1 && currentUser != 'Please Login First') addFriendDecline();
// if($('#friendDialogMessage').text() != 'Login to see your friendlist' && $('#friendDialogMessage').text() == 'Loading...' && $('#friendsRequestsAmt').text() == '' && userPreferences.friendDeclineAll && currentUser != 'Please Login First'){
// $('#btnFriends').click().click();
// }
fpsArr.push(+document.getElementById("fps").innerText);
if(fpsArr.length == 6) fpsArr.shift();
avgFps = mean(fpsArr);
setTimeout(mainInterval, 1e3);
}
setTimeout(mainInterval, 1e3);
// }, 1e3);
const antiAFK = () => {
setTimeout(antiAFK, 3e4);
if(!$('#fsfb-antiAFK').is(':checked')) return; // move mouse every 30sec
let [moveX, moveY] = linesplitting ? [pointMove.x, pointMove.y] : [mosX, mosY];
[++moveX, --moveX].forEach(x => $('#canvas').trigger($.Event('mousemove', {clientX: x, clientY: moveY})));
}
setTimeout(antiAFK, 3e4);
uisdoa ?? $('#fsfb-recospeed').parent().hide();
const updateScriptSettingsUI = () => {
for(let i of settings.checkboxes) $('#' + i.id).prop("checked", i.active).trigger("change");
for(let i of settings.hotkeys) $('#' + i.id).text(getName(i.key));
for(let i of settings.quickSettings){
$('#' + i.id1).val(i.set);
$('#' + i.id).text(getName(i.key));
}
// slowfeed
$('#' + settings.slowFeed[0].id).text(getName(settings.slowFeed[0].key));
$('#' + settings.slowFeed[1].id).val(settings.slowFeed[1].val);
$('#' + settings.frozenvirus[0].id).text(getName(settings.frozenvirus[0].key));
$('#' + settings.frozenvirus[1].id).val(settings.frozenvirus[1].val);
for(let i of settings.fastsplit_hotkeys){
i.val == null ? $('#' + i.id).text(getName(i.key)) : $('#' + i.id).val(i.val);
}
for(let i of settings.uiScaling) $('#' + i.id).val(i.level).trigger("input");
for(let i of settings.export_import) $('#' + i.id).prop("checked", i.active).trigger("change");
for(let i of settings.theme){
$('#' + i.id).prop("checked", i.active).trigger("change");
$('#' + i.id1).val(i.color).trigger("input");
}
for(let i of settings.theme_boxes) $('#' + i.id).prop("checked", i.active).trigger("change");
for(let i of settings.name_color){
if('color' in i){
$('#' + i.id).prop("checked", i.active).trigger("change");
$('#' + i.id1).val(i.color).trigger("input");
} else $('#' + i.id).prop("checked", i.active).trigger("change");
}
for(let i of settings.anti_lag) $('#' + i.id).prop("checked", i.active).trigger("change");
for(let i of settings.chat_translate){
'set' in i ? $('#' + i.id).val(i.set) : $('#' + i.id).prop('checked', i.active).trigger('change');
}
}
setTimeout(() => updateScriptSettingsUI(), 1e3);
$('body').append('');
const _replaceCSS = (a,b) => {
document.getElementById(a).innerHTML = b;
}
!function(){
$('body').append(`
Script Documentation
Chat Copy/Cut/Paste - allows you to use the commands in chat (e.g. Ctrl + V becomes avaiable inside chat)
Anti-AFK - prevents you from automatically disconnecting after 10 minutes
Anti-Invis - shows you players even when they have the invisibility ability active
Linesplit Toggle - 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)
Change Page Title - changes the tab's title to just "Agma.io" with the current username
Hide Shouts - prevent megaphone shouts from showing up at all
Hold To Spam - while the powerup's hotkey is held, it will continuously use the powerup
Show Portal Mass - 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)
Power Spawns Overlay - show the locations of where powerups/minion packs spawn with lower opacity (thanks to Light for helping with getting all of the power locations)
Quick Buy - 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
Food/Virus/Mothercell Color - changes the color that's filling these to a custom one
Virus/Mothercell Stroke - changes the color of the stroke (border/outline) to a custom one
Spiked Cells - render all cells with the spikes from the infecton gamemode
Show Mass - show the mass of all players' cells
Only My Skin - hide all skins besides the one you're using
Only Party Skin - hide all skins besides the ones people in your party are using
Only My Nick - hide all nicks besides the one you're using
Only Party Nick - hide all nicks besides the ones people in your party are using
Shoot 7 Ejected - press ejected mass hotkey 7 times (useful to prime room 1 or 2 portal when it's been reset
Linesplit Lock - 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 Wynell's script)
Macrosplit Bots - hold this key to macrosplit your bots without switching controls off of yourself
Hide UI - press this key to toggle showing the game UI (intended for recording/screenshots)
Toggle Slow Feed - (toggle) this presses eject mass hotkey at the defined interval (intended for feeding the gold block while AFK)
Slow Feed Speed - the speed at which eject mass is pressed when slow-feeding
Quick Settings - when the hotkey assigned is pressed, it will toggle the setting that is selected
Toggle Cursor Lock - when pressed, this will keep cursor lock active until you press it again (also works when the tab is unfocused)
Fast Onesplit - performs a fast onesplit (Onesplit -> Freeze -> Unfreeze); speed is dependent fps (low fps indicates slow CPU, slow CPU can mess timings up)
Fast Doublesplit - performs a fast Doublesplit (Doublesplit -> Freeze -> Unfreeze); speed is dependent fps (low fps indicates slow CPU, slow CPU can mess timings up)
Chat Size - make the size of chat bigger/smaller
Inventory Size - make the size of powerups inventory bigger/smaller
Statsbox Size - make the size of XP/coins stats bigger/smaller
Export - select the boxes of the settings you wish to export and press the button, a .txt file will be downloaded with your settings inside
Import - 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
Chat Command
Description
${userPreferences.chatPrefix}help
list all available chat commands
${userPreferences.chatPrefix}bots
show which bot pack you have active and how much time is remaining
${userPreferences.chatPrefix}pws
show the amount of powerups in your inventory (recommended to use ${userPreferences.chatPrefix}pws1, ${userPreferences.chatPrefix}pws2, and ${userPreferences.chatPrefix}pws3)
${userPreferences.chatPrefix}totalpws
show the total amount of powerups you have in your inventory
${userPreferences.chatPrefix}xp
show then amount of xp you've completed for this level
${userPreferences.chatPrefix}lvl
show your level and how much of the level you've completed
${userPreferences.chatPrefix}coins
show the amount of coins you have
${userPreferences.chatPrefix}hours
show the hours you have on your account
${userPreferences.chatPrefix}rank
show your ranking
${userPreferences.chatPrefix}ping
show your current ping
${userPreferences.chatPrefix}fps
show your current FPS
${userPreferences.chatPrefix}topmass
show your highest mass
${userPreferences.chatPrefix}cells
show your cell count
${userPreferences.chatPrefix}pws1
show the first part of your powerup inventory
${userPreferences.chatPrefix}pws2
show the second part of your powerup inventory
${userPreferences.chatPrefix}pws3
show the third part of your powerup inventory
${userPreferences.chatPrefix}friends
show how many friends you have and how many are online
${userPreferences.chatPrefix}requests
show how many friend requests you have
${userPreferences.chatPrefix}gold
show how many days of gold member you have left remaining
${userPreferences.chatPrefix}alive
show the amount of time you've been alive
${userPreferences.chatPrefix}mass
show your current mass
${userPreferences.chatPrefix}user
show your current username
${userPreferences.chatPrefix}customs
show the amount of custom skins you own
${userPreferences.chatPrefix}wearables
show how many wearables you own
${userPreferences.chatPrefix}cloak
show the remaining time of your cloak ability
${userPreferences.chatPrefix}add [user]
type this command to quickly add a friend using chat
${userPreferences.chatPrefix}partymembers
show how many people are in your party
${userPreferences.chatPrefix}players
show how many players are online in your server and are online in all agma servers
${userPreferences.chatPrefix}server
show which server you're currently in
${userPreferences.chatPrefix}abils
show your currently active abilities
${userPreferences.chatPrefix}xpproj
show the predicted amount of XP you will gain in an hour
${userPreferences.chatPrefix}coinsproj
show the predicted amount of coins you will gain in an hour
${userPreferences.chatPrefix}xprem
show the amount of XP remaining for your level
${userPreferences.chatPrefix}xphour
show the amount of xp you've gained in the last hour
${userPreferences.chatPrefix}coinshour
show the amount of coins you've gained in the last hour
${userPreferences.chatPrefix}xpmin
show the amount of XP you've gained in the last minute
${userPreferences.chatPrefix}coinsmin
show the amount of coins you've gained in the last minute
${userPreferences.chatPrefix}xp12s
show the amount of coins you've gained in the last 12 seconds
${userPreferences.chatPrefix}xpsesh
show the amount of XP you've gained in this session
${userPreferences.chatPrefix}lifetimexp
show the total amount of XP you've earned in your account's lifetime
${userPreferences.chatPrefix}seshcoins
show the amount of coins you've gained in this session
${userPreferences.chatPrefix}seshcoins
show the amount of coins you've gained in this session
${userPreferences.chatPrefix}waifu [user]
rate the waifu of the selected username (leave user blank to rate yourself)
${userPreferences.chatPrefix}pro [user]
show how pro someone is (leave user blank to rate yourself)
${userPreferences.chatPrefix}dog [user]
show how dog someone is (leave user blank to rate yourself)
${userPreferences.chatPrefix}king [user]
show how king someone is (leave user blank to rate yourself)
${userPreferences.chatPrefix}dice [sides]
roll a die with the desired number of sides
${userPreferences.chatPrefix}rng [min] [max]
generate a random number in a range
${userPreferences.chatPrefix}coinflip
flip a coin and see if it lands on heads or tails
${userPreferences.chatPrefix}script
show the current script you're using & which version
${userPreferences.chatPrefix}time
show your current locale date & time
${userPreferences.chatPrefix}skins
show your bought skins and their worth (limited as well)
anything written after this command will have alternating lowercase/capital letters
${userPreferences.chatPrefix}sparkles [text]
anything written after this command will be surrounded with star emojis
${userPreferences.chatPrefix}cfix
resets canvas, can sometimes fix bugs with rendering
${userPreferences.chatPrefix}fact
sends a random fact in chat
${userPreferences.chatPrefix}fastsplit
show your fastsplit delays
${userPreferences.chatPrefix}progress
show your progress for the daily challenge
${userPreferences.chatPrefix}sorapoints
show how many SoraPoints you have
${userPreferences.chatPrefix}keys
show how many myster box keys you have
${userPreferences.chatPrefix}stars
show how many challenge stars you have
Hide Ads - both video and image ads will be removed from the screen
Skin Search - search through skins by their names/ids
Improved Shop - added larger amounts that can be purchased at a time, can also buy a specified amount at one time
Sort Wearables - wearables are automatically sorted by owned (the ones you own will be before all others)
Extra Bot Packs - added hidden bot packs that can be purchased with coins (originally discovered by firebone)
Context Menu Copy Info - 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
Copy Chat - right click on screen, then click "Copy Chat Messages" to copy the currently visible chat messages to your clipboard
Abilities Remaining Time - shows the remaining time left of abilities (only works if the abilities were purchased in the same browser)
Unlock Free Skins - gives you access to the facebook & youtube free skins
Hover For Skin ID - hovering skins in the skin menu will shop their ID
In Depth XP/Coins Stats - click on coins/xp progress bar in top left to view respective statistics
XP Bar Decimals - show the percentage up to 2 decimal places
White Border For Black Cells - show a white border around black cells (from minion nuker) so they're easier to see with dark backgrounds
Inventory Single Row - put powerups inventory on a single row instead of on 2 seperate rows (inspired by Principito)
Public Name Color - enables other fsfb users to see your colored name and you to see the colored name of other fsfb users
Name Color - change the color of your name
Name Stroke - change the outline color of your name, when this setting is disabled and the Name Color enabled, a color will be generated that fits to the name color
Hide Small Minions - hides small minions, but lets them appear once they grow bigger to reduce lag
No Minion Skins - makes minions appear without skins to reduce lag
Hide Ejected Mass - hides all ejected (W) mass to reduce lag
Hide Static Ejected - only hides non-moving ejected mass to reduce lag
No Eat Anims - removes the absorbtion animation when eating a cell to reduce lag
Translate Chat - translates chat messages sent by players to the selected language (english by default), the translation appears in square brackets
Translate Server - translates chat messages sent by the server to the selected language (english by default), the translation appears in square brackets