// ==UserScript==
// @name paper2hack
// @description Modding utility/menu for paper.io
// @version 0.1.21
// @author its-pablo
// @match https://paper-io.com
// @match https://paper-io.com/teams/
// @match https://paper-io.com/battleroyale/
// @match https://paperanimals.io
// @match https://amogus.io
// @require https://cdn.jsdelivr.net/npm/tweakpane@3.1.4/dist/tweakpane.min.js
// @license GPL-3.0-only
// @icon https://paper-io.com/favicon.ico
// @grant none
// @namespace https://greasyfork.org/users/1204224
// ==/UserScript==
adblock = () => false //this detects if adblock is on, we make it always return false so that the impostor skin loads
window.addEventListener('load', function () {
"use strict";
const VERSION = "beta 0.1.21"
let newApi
let finish = false; // Start booting
// New api loads too slow!!
// Maybe plan in only supporting the old api? just an opinion
(async() => {
console.log("[paper2hack] Waiting for api");
while(!window.hasOwnProperty("paperio2api")) // paperio2api was first defined!
if (window.hasOwnProperty("paper2")) {newApi = false;return;} // paper2 was first defined!
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait!!
console.log("[paper2hack] api defined!");
})();
window.api = {
/**
* Gets the configuration
* @returns the configuration
*/
config: function () {
if (newApi) {
return paperio2api.config;
} else {
return paper2.currentConfig;
}
},
/**
* Gets the game
* @returns the game
*/
game: function () {
if (newApi) {
return paperio2api.game
} else {
return paper2.game
}
},
/**
* Gets the player
* @returns the player
*/
player: function () {
return this.game().player;
},
/**
* Search an unit by the name
* If no unit is found, it will return `null`
* If an unit is found, it will return that unit
* If multiple names are used
* The first one will be returned
* @param {*} name The name to be searched for
* @returns The unit if found, null if not
*/
searchForUnit: function (name) {
for (let i = 0; i < this.game().units.length; i++) {
if (this.game().units[i].name.toLowerCase() == name.toLowerCase()) {return this.game().units[i];}
}
return null;
},
/**
* Create a message, that will appear above the specified unit.
* @param {*} unit The unit
* @param {*} text The text
* @param {*} hexColor The color, in hex, for example : `#FF00FF`
*/
addMessage: function (unit, text, hexColor) {
unit.addLabel({
"unit": undefined, // Assign to the unit
"text": text,
"color": hexColor
})
},
/**
* Kills the unit, by the other unit
* @param {*} unitToKill The unit that will be killed
* @param {*} unitToGetKill The unit that'll get the kill stat for
*/
kill: function (unitToKill, unitToGetKill) {
if (unitToGetKill == undefined) {unitToGetKill = this.player()}
this.game().kill(unitToKill, unitToGetKill)
},
/**
* Returns the resource located at the resource array (`a0_0x344c`)
* This is only intended for reverse engineering
* @param {number} res the resource, starting at `196`
* @returns the resource, as a `string` can also be undefined
*/
getResource: function (res) {
return a0_0x2cc6(res);
}
}
let ETC = {
"newMessage":function (message) {
let newMessage = document.createElement("p");
document.querySelectorAll("#message")[1].appendChild(newMessage);
newMessage.innerText = message;
// Return the new message, if you need it
return newMessage;
},
"reset": function () { alert("Cannot be done with tweakpane!\nTry clearing site data.") },
"zoomScroll": false,
"debugging": false,
"map": false,
"despawnK": false,
"speed": api.config().unitSpeed,
"skin": "",
"skinUnlock": () => {
try {
shop.btnsData.forEach(item => {
if (item.codeName) {
unlockSkin(item.codeName)
}
})
console.log("[paper2hack] skins unlocked!")
} catch (e) {
console.log("[paper2hack] Error unlocking skins!", e)
}
},
"_skins": [],
"pause": function () {
if (!newApi) {
// Toggle between paused and unpaused
// This is not possible in the new api (I believe)
api.game().paused = !paper2.game.paused;
if (api.game().paused) {
console.log("[paper2hack] Paused");
} else {
console.log("[paper2hack] Unpaused");
}
// Return so the unit speed doesn't change
return;
}
if (api.config().unitSpeed !== 0) {
api.config().unitSpeed = 0
console.log("[paper2hack] Paused")
} else {
api.config().unitSpeed = 90
console.log("[paper2hack] Unpaused")
}
return;
},
"despawnOthers": function () {
// Array where we store the units to kill
let unitkills = [];
for (let i = 0; i < api.game().units.length; i++) {
if (api.game().units[i].isPlayer) {continue;} // Ignore if we get the player unit
unitkills.push(api.game().units[i]);
}
// Iterate through the units that we're going to kill
unitkills.forEach((obj) => {
api.kill(obj, obj);
})
},
"help": function () {
alert(`
paper2hack ${VERSION} written by stretch07 and contributors.\n\n
https://github.com/stretch07/paper2hack \n
Issues? https://github.com/stretch07/paper2hack/issues
If you encounter any issues with paper2hack, refresh the page, hit the 'Reset' button, or uninstall/reinstall the mod. As a last resort, try clearing site data.
`)
},
"keysList": function () {
alert(`Keyboard shortcuts:\n- Space: Pause/play\n- K: Despawn all other players`)
},
"openGithub": function () {
window.open("https://github.com/stretch07/paper2hack", '_blank').focus();
}
}
function scrollE(e) {
if (e.deltaY > 0) {
if (api.config().maxScale > 0.45) {
api.config().maxScale -= 0.2
}
} else if (e.deltaY < 0) {
if (api.config().maxScale < 4.5) {
api.config().maxScale += 0.2
}
}
}
let pane = new Tweakpane.Pane({ title: "paper2hack"})
let mods = pane.addFolder({ title: "Mods" })
mods.addInput(ETC, "speed", { min: 5, max: 500, count: 5 }).on("change", ev => {
api.config().unitSpeed = ev.value;
})
mods.addInput(ETC, "skin", {
label: "Skin",
// Yeah unreadable i know
// Got this with a simple js trick ;)
options: {"No skin":"skin_00","Orange":"skin_20","Burger":"skin_19","Matrix":"skin_49","Green Goblin":"skin_48","Squid Game":"skin_47","Venom":"skin_46","Money Heist":"skin_45","Doge":"skin_44","Baby Yoda":"skin_43","Chess Queen":"skin_42","Impostor":"skin_41","Cyber Punk":"skin_40","Stay safe":"skin_39","Sanitizer":"skin_38","Doctor":"skin_37","COVID-19":"skin_36","Geralt":"skin_35","Batman":"skin_30","Joker":"skin_29","Pennywise":"skin_28","Reaper":"skin_27","Captain America":"skin_26","Thanos":"skin_25","Cupid":"skin_24","Snowman":"skin_23","Present":"skin_22","Christmas":"skin_21","Ladybug":"skin_18","Tank":"skin_17","Duck":"skin_16","Cake":"skin_15","Cash":"skin_14","Sushi":"skin_13","Bat":"skin_12","Heart":"skin_11","Rainbow":"skin_10","Nyan cat":"skin_01","Watermelon":"skin_02","Ghost":"skin_03","Pizza":"skin_04","Minion":"skin_05","Freddy":"skin_06","Spiderman":"skin_07","Teletubby":"skin_08","Unicorn":"skin_09","Eye":"eye", "Frankenstein":"Frank", "Santa":"santa", "Rudolph":"rudolf"}
}).on("change", ev => {
if (newApi && finish) {window.alert("Cannot do this in this version!");}
// Oh boy! No player No skin!
if (!api.game() || !api.player()) {return;}
let id = ev.value;
// The skin manager uses the codeName to get the skin itself
let codeName;
let secret = true;
shop.btnsData.forEach(s => {
if (s.useId == id) {
codeName = s.codeName;
// There is no skins with the same code name, so we can return!
return;
}
})
// Edge cases since these skins don't have use ids
if (id == "eye") {codeName = id;}
else if (id == "Frank") {codeName = id;}
else if (id == "santa") {codeName = id;}
else if (id == "rudolf") {codeName = id;}
else { secret = false; }
// The skin manager treats the default skin as undefined
// if we don't do this it will create an error and will not change the skin
if (codeName == "default") {codeName = undefined;}
// Get the skin from the code name
let skin = api.game().skinManager.getPlayerSkin(codeName);
// And set it to the player!
api.player().setSkin(skin);
if (secret) {return;}
shop.chosenSkin = id;
Cookies.set('skin', id);
})
mods.addInput(ETC, "debugging", { label: "Debug" }).on("change", ev => {
api.game().debug = ev.value
api.game().debugGraph = ev.value
})
mods.addInput(ETC, "map", { label: "Map"}).on("change", ev => {
api.game().debugView = ev.value;
})
mods.addButton({ title: "Pause/Play" }).on("click", ETC.pause)
if (!newApi) {
mods.addButton({ title: "Unlock skins", }).on("click", ETC.skinUnlock)
}
mods.addButton({ title: "I give up" }).on("click",() => {
api.kill(api.player());
})
mods.addButton({ title: "Despawn others" }).on("click", ETC.despawnOthers)
mods.addInput(ETC, "despawnK", { label: "Despawn Others on K" })
// keyboard shortcuts
// pause: Spacebar
// kill everyone: K
document.addEventListener("keydown", ev => {
if (!api.player()) {return;} //If not in game, return
if (event.key === 'k') {
if (!ETC.despawnK) return;
ETC.despawnOthers()
}
if (event.key === " ") {
ETC.pause()
}
})
mods.addInput(ETC, "zoomScroll", { label: "Scroll to Zoom" }).on("change", ev => {
if (ev.value === true) {
window.addEventListener("wheel", scrollE)
} else {
window.removeEventListener("wheel", scrollE)
}
})
mods.addButton({ title: "Reset" }).on('click', ETC.reset);
let about = pane.addFolder({ title: "About", expanded: false });
about.addButton({ title: "Help" }).on("click", ETC.help);
about.addButton({ title: "Keyboard Shortcuts" }).on("click", ETC.keysList);
about.addButton({ title: "GitHub" }).on("click", ETC.openGithub);
/*Last things*/
if (!localStorage.getItem('paper2hack')) {
this.localStorage.setItem('paper2hack', JSON.stringify({}))
}
pane.importPreset(JSON.parse(localStorage.getItem("paper2hack")))
pane.on("change", e => {
localStorage.setItem("paper2hack", JSON.stringify(pane.exportPreset()))
})
document.querySelector(".tp-dfwv").style.zIndex = "50"; //Make sure the menu shows up over some GUI
finish = true; // Ended booting
let messages = this.document.querySelectorAll("#message p");
messages.forEach((msg)=>{msg.remove();}) //Remove all messages
ETC.newMessage(`paper2hack ${VERSION}`);
let checkinstallmessage = ETC.newMessage('');
checkinstallmessage.innerHTML = `<a style="color: white" href="https://github.com/stretch07/paper2hack">check/install update</a>`;
ETC.newMessage(`have fun hacking!`);
}, false);