`);
$('#' + slowfeedhotkey.id).on('contextmenu', e => {
$('#' + slowfeedhotkey.id).text('');
slowfeedhotkey.key = 0;
saveSettings();
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();
if($('#' + settings.slowFeed[1].id).val() == "") settings.slowFeed[1].val = 0;
saveSettings();
}, 5);
});
// add quick settings HTML
$('#fsfb-sect-quickSettings').append(`
Quick Settings
`);
for(let i of settings.quickSettings){
$('#fsfb-sect-quickSettings').append(``);
$('#' + i.id).on('contextmenu', e =>{
$('#' + i.id).text('');
i.key = 0;
saveSettings();
e.preventDefault();
});
};
$('.fsfb-quickchange').on("change", function(){
settings.quickSettings[+this.id.replace('fsfb-quick-select', '') - 1].set = this.value;
saveSettings();
});
// add event listeners
let mosX, mosY;
let keys = {};
let keysChanging = false;
$(document).on('click', e => {
for(let i = 0; i < settings.hotkeys.length; i++) checkHotkeyClicked(e, settings.hotkeys[i]);
for(let i = 0; i < settings.quickSettings.length; i++) checkHotkeyClicked(e, settings.quickSettings[i]);
checkHotkeyClicked(e, settings.slowFeed[0]);
checkPowerupClicked(e);
})
.on('mousemove', e => {
if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
({clientX: mosX, clientY: mosY} = e);
if (linesplitting) linesplit();
})
.on("keydown", e => {
if(e.originalEvent == null || !e.originalEvent.isTrusted) return;
for(let i = 0; i < settings.hotkeys.length; i++) checkNewHotkey(e, settings.hotkeys[i]);
for(let i = 0; i < settings.quickSettings.length; i++) checkNewHotkey(e, settings.quickSettings[i]);
checkNewHotkey(e, settings.slowFeed[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);
});
$('#fsfb-export-btn').on('click', () => {
let script_settings = {},
script_themes = {};
for (let i in settings) { // put the settings & themes into different objects
if (i == "theme" || i == "theme_boxes") script_themes[i] = settings[i];
else if(i == "export_import") {}
else script_settings[i] = settings[i];
}
let obj = {
selected: {
game_settings: $('#fsfb-game-settings').is(':checked'),
game_controls: $('#fsfb-game-controls').is(':checked'),
script_settings: $('#fsfb-script-settings').is(':checked'),
script_theme: $('#fsfb-theme-settings').is(':checked')
},
game_settings: localStorage.settings,
game_controls: localStorage.hotkeys,
script_settings: script_settings,
script_theme: script_themes
}
// if the setting is turned off, then dont need to export it - set it to null
if(!obj.selected.game_settings) obj.game_settings = null;
if(!obj.selected.game_controls) obj.game_controls = null;
if(!obj.selected.script_settings) obj.script_settings = null;
if(!obj.selected.script_theme) obj.script_theme = null;
const a = document.createElement('a'),
file = new Blob([JSON.stringify(obj)], {type: "text/plain"});
a.href = URL.createObjectURL(file), a.download = "fishy & firebone script settings", a.click(), URL.revokeObjectURL(a.href); // download a txt file of exported settings
});
$('#fsfb-import-btn').on('click', () => {
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 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'))){
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();
updateScriptSettingsUI();
} catch (error){
swal({
title: "Something went wrong!",
text: "Please make sure you've entered in valid settings",
type: "error"
});
}
});
});
$('#fsfb-settings-right').append(``)
// PUT MODAL HERE
localStorage.ad_l_time = "9e99"; // smth like time since last ad
$('[id^="agma-io_"], [id^="adWrapper"], #preroll').addClass("fuckAds"); // move ads way off the screen
let width = unsafeWindow.innerWidth, height = unsafeWindow.innerHeight,
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}];
$(unsafeWindow).on('resize', function(){
width = unsafeWindow.innerWidth, height = unsafeWindow.innerHeight;
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}];
});
let pointMove;
const linesplit = () => {
if(!linesplitting) return;
const distance = p => Math.sqrt(Math.pow(mosX - p.x, 2) + Math.pow(mosY - p.y, 2));
let closest = points.reduce((a, b) => distance(a) < distance(b) ? a : b);
for (let i = 0; i < points.length; i++) {
if (closest.x == points[i].x && closest.y == points[i].y) {
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
$('canvas').trigger($.Event('mousemove', {
clientX: points[i].nx,
clientY: points[i].ny
}));
}
}
$("#linesplit-markers div").css('background-color', 'transparent');
$(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.';
};
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)
amtDropdown.removeEventListener('change', dropdownChange);
amtDropdown.addEventListener('change', dropdownChange);
}
catch (error){
amtDropdown.addEventListener('change', dropdownChange);
};
}, 30);
};
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: false,
confirmButtonColor: '#4CAF50',
confirmButtonText: 'Yes, confirm purchase',
cancelButtonText: 'No, cancel purchase'
}, function(confirmed){
if(confirmed) 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), 250 * i + 250);
purchaseItem(shopID, pwAmt % 255);
} else purchaseItem(shopID, pwAmt);
};
if(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', () => {
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());
});
}
}
}
// add extra bot packs
const newBots = [
{title: "100 Bots", time: "24 HOURS", price: "700,000", id: 9},
{title: "125 Bots", time: "48 HOURS", price: "800,000", id: 10},
{title: "300 Bots", time: "24 HOURS", price: "900,000", id: 7},
{title: "100 MASS Bots", time: "1 HOURS", price: "800,000", id: 8},
{title: "300 Bots", time: "72 HOURS", price: "2,000,000", id: 11},
{title: "100 MASS Bots", time: "24 HOURS", price: "2,500,000", id: 12}
]
const minul = document.getElementsByClassName('tab-container-section minion scroll')[0].children[0].children[0]
$('.confirm-minion[item=7], .confirm-minion[item=8]').parent().hide();
document.getElementById('extraTab').childNodes[0].setAttribute('onclick', '');
function createEl(i) {
const botEl = document.createElement('li');
botEl.setAttribute('class', 'masterTooltip extra-min');
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.');
minul.appendChild(botEl);
botEl.innerHTML = `
` + i.title + `
` + i.time + `
` + i.price + `
Buy
`;
}
if(extraBotPacks){
for(let i of newBots) createEl(i);
}
// $('.confirm-minion.extra-min').click(function(e) {
// e.preventDefault(); // Prevent the href from redirecting directly
// var linkURL = $(this).attr("href");
// var priceK = $(this).attr("price");
// var itemId = $(this).attr("item");
// setMinionUi(true);
// warnBeforeMinion(linkURL,priceK,itemId);
// });
function warnBeforeMinion(linkURL, priceK,itemId) {
swal({
title: "Confirm",
text: 'If you click "Buy", you will purchase these minions. They cost ' + priceK,
type: "warning",
showCancelButton: true,
confirmButtonColor: "#4CAF50",
confirmButtonText: "Yes, confirm purchase",
cancelButtonText: "No, cancel purchase"
}, function() {
purchaseMinion(itemId);
});
}
// context menu: click on skin -> skin ID to clipboard, click on name -> name to clipboard
$('#contextPlayer').on('click', e => {
if(!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]+/g)[0].substring(7); // 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
const curserMsg = (msg, color) => {
if(color == "green") color = "rgb(0, 192, 0)";
if(color == "red") color = "rgb(255, 0, 0)";
if(color == "gray") color = "rgb(153, 153, 153)";
$('#curser').text(msg).show().css('color', color)
setTimeout(() => $('#curser').fadeOut(400), 4e3);
}
// ability time remaining
let acc_abil = get("fsfb-abil", {});
let lastClickedPrice, currentPrice, lastID;
const confirmClicked = () => {
setTimeout(() => {
if($('.sweet-alert.showSweetAlert.visible h2').text() != "Success!" || lastClickedPrice != currentPrice) return;
acc_abil[currentUser] = {...acc_abil[currentUser], ...{[lastID] : Date.now()}};
set("fsfb-abil", acc_abil);
}, 1200);
};
[18, 20, 22, 23].forEach(el => {
if(!showRemainingAbilityTime) return;
let h5 = $(`.purchase-btn.confirmation[item="${el}"]`).parents().eq(0).find('div h5');
h5.clone().insertAfter(h5).addClass('fsfb-fake').hide();
$(`.purchase-btn[item="${el}"]`).on('click', function () {
lastClickedPrice = this.getAttribute('price');
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().replaceAll(/\D+/g, '');
}, 750);
setTimeout(() => $('.confirm')[0].addEventListener('click', confirmClicked), 500);
})
});
/* 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 scriptStart = 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 = `
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)
Copy Chat Messages - 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
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
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
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
Hide Ads - both video and image ads will be removed from the screen
Improved Shop - added larger amounts that can be purchased at a time, can also buy a specified amount at one time
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
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
White Border For Black Cells - show a white border around black cells (from minion nuker) so they're easier to see with dark backgrounds