// ==UserScript==
// @name Better TPT
// @namespace http://tampermonkey.net/
// @version 1.1.5
// @description QoL changes for The Prestige Tree.
// @author Yhvr
// @match https://jacorb90.github.io/Prestige-Tree/index.html
// @match http://despatpt.glitch.me
// @match http://tptrespec.glitch.me
// @match https://aarextiaokhiao.github.io/Prestige-Tree/
// @grant none
// ==/UserScript==
// TOFIX: Unlocking new row when `darkTheme` is `false` doesn't invert that row's respec button
console.log(`!!! IMPORTANT !!!
If you think you've found a bug in the game, please disable Better TPT and make sure it occurs without it.
If you think you've found a bug in Better TPT, please disable Better TPT and make sure it only occurs with it.`);
addGlobalStyle(`
.btptButtons {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: flex-start;
}
.btptWindowContainer {
position: absolute;
background-color: grey;
transition: 0s;
/* always last */
z-index: 1000
}
.btptWindowContent {
resize: both;
overflow: auto;
transition: 0s;
min-width: 150px;
width: 500px
}
.opt {
margin-left: 0;
margin-right: 0;
}
.back {
position: sticky;
right: calc(100% - 45px);
}
`)
// Mod detection
window.mod = null;
try {
mod = modInfo.id
} catch (e) {
console.info(`[BTPT] mod assignment failed, must be playing vanilla`)
}
// Keybinds!
/* const keydown = document.onkeydown
const PROPER_LAYERS = ["p", "b", "g", "sb", "t", "e", "s", "h", "q"]
document.onkeydown = e => {
keydown(e)
if (e.key === "ArrowLeft") player.tab = PROPER_LAYERS[PROPER_LAYERS.indexOf(player.tab) - 1 % PROPER_LAYERS.length]
if (e.key === "ArrowRight") player.tab = PROPER_LAYERS[PROPER_LAYERS.indexOf(player.tab) + 1 % PROPER_LAYERS.length]
} */
// Init. alt fonts
let altFont = false;
// Themeing
let darkTheme = true;
const darkCSS = `
body {
background-color: black;
color: white;
}
${ mod === null ? `.s_txt, .ss_txt {
color: white;
}` : ""}
.back {
background-color: black;
color: white;
}`
const lightCSS = `
body {
background-color: white;
color: black;
}
#points {
color: black;
}
${ mod === null ? ` .s_txt, .ss_txt {
color: black;
}` : ""}
.back {
background-color: white;
color: black;
}`
function toggleTheme(change = true) {
if (change) darkTheme = !darkTheme;
const el = document.getElementById("btpttheme");
if (darkTheme) {
if (el) el.innerHTML = darkCSS;
else addGlobalStyle(darkCSS, "btpttheme");
document.querySelectorAll("img").forEach(n => (n.style.filter = "invert(0%)"));
} else {
if (el) el.innerHTML = lightCSS;
else addGlobalStyle(lightCSS, "btpttheme");
document.querySelectorAll("img").forEach(n => (n.style.filter = "invert(100%)"));
}
if (player === undefined) {
setTimeout(() => {
drawTree();
document.getElementById("optionWheel").onclick = newBTPTPrompt;
}, 250)
} else drawTree();
}
const customFont = `*{font-family:"JetBrains Mono", "Menlo", "monospace"}`;
const normalFont = `*{font-family:"Lucida Console", "Courier New", monospace}`
function toggleFont(change = true) {
if (change) altFont = !altFont
const el = document.getElementById("btptfont");
if (altFont) {
if (change) fontNotice();
if (el) el.innerHTML = customFont;
else addGlobalStyle(customFont, "btptfont");
} else {
if (el) el.innerHTML = normalFont;
else addGlobalStyle(normalFont, "btptfont");
}
}
function promptBuilder(name, func, height = 300, isFlex = false) {
return () => {
if (document.getElementById(`btpt${name}`)) document.getElementById(`btpt${name}`).remove();
const el = document.createElement("div");
const el2 = document.getElementById("btptmain") || document.body;
el.classList.add("btptWindowContainer");
el.style.left = `${el2.offsetLeft}px`;
el.style.top = `${el2.offsetTop + el2.offsetHeight}px`;
el.id = `btpt${name}`
el.innerHTML = `${header(`btpt${name}`)}<h3${name}</h3>`;
const el3 = document.createElement("div");
el3.classList.add("btptWindowContent");
el3.style.height = `${height}px`;
if (isFlex) el3.classList.add("btptButtons");
func(el3);
el.appendChild(el3);
document.body.appendChild(el);
dragElement(document.getElementById(`btpt${name}`));
}
}
const fontNotice = promptBuilder("notice", el => {
el.innerHTML += "Enabled Alt. font! Please note, you'll need to manually" +
" install JetBrains Mono. Find it here https://www.jetbrains.com/lp/mono/"
}, 100, true)
const stylePrompt = promptBuilder("styling", el => {
el.appendChild(button("Change Theme", toggleTheme));
el.appendChild(button("Change Font", toggleFont));
}, 150, true)
const autoBuyPrompt = promptBuilder("autobuy", el => {
LAYERS.filter(n => Object.keys(LAYER_UPGS[n] || []).length > 2).map(n => {
const btn = document.createElement("button");
btn.classList.add("treeNode");
btn.classList.add(n);
btn.textContent = n.toUpperCase();
btn.onclick = () => toggleAutobuyer(n);
btn.id = `btptAutobuy${n}`;
btn.classList.add(autobuy[n] ? "can" : "locked");
return btn;
}).forEach(n => el.appendChild(n));
})
function toggleAutobuyer(autobuyer) {
autobuy[autobuyer] = !autobuy[autobuyer];
if (autobuy[autobuyer]) {
const btn = document.getElementById(`btptAutobuy${autobuyer}`);
btn.classList.remove("locked");
btn.classList.add("can");
} else {
const btn = document.getElementById(`btptAutobuy${autobuyer}`);
btn.classList.remove("can");
btn.classList.add("locked");
}
}
const autoPrestigePrompt = promptBuilder("autoprestige", el => {
LAYERS.filter(n => LAYER_TYPE[n] === "static").map(n => {
const btn = document.createElement("button");
btn.classList.add("treeNode");
btn.classList.add(n);
btn.textContent = n.toUpperCase();
btn.onclick = () => toggleAutoPrestige(n);
btn.id = `btptAutoPrestige${n}`;
btn.classList.add(autoprestige[n] ? "can" : "locked");
return btn;
}).forEach(n => el.appendChild(n));
document.body.appendChild(el);
})
function toggleAutoPrestige(n) {
autoprestige[n] = !autoprestige[n];
if (autoprestige[n]) {
const btn = document.getElementById(`btptAutoPrestige${n}`)
btn.classList.remove("locked")
btn.classList.add("can")
} else {
const btn = document.getElementById(`btptAutoPrestige${n}`)
if (!btn) return;
btn.classList.remove("can")
btn.classList.add("locked")
}
}
const miscPrompt = promptBuilder("auto", el => {
// TODO: Use switch instead?
if (mod === null || mod === "default") {
el.appendChild(button("Auto Enhance", () => (enhanceAuto = !enhanceAuto)));
el.appendChild(button("Auto Capsule", () => (capsuleAuto = !capsuleAuto)));
}
if (mod === "dalt") {
el.appendChild(button("No automation for DespaTPT... :(", () => {}))
}
}, 150, true)
const infoPrompt = promptBuilder("info", el => {
el.innerHTML += `
<h2>Better TPT</h2>
<h5>Version 1.1.5</h5>
<p>Made by Yhvr
<ul>
<li><a href="https://greasyfork.org/en/scripts/409845-better-tpt">GreasyFork</a></li>
<li><a href="https://discord.gg/cGuyHPG">Discord</a></li>
</ul></p>`
}, 150)
toggleOpt = (opt, text) => {
player[opt] = !player[opt]
const el = document.getElementById(opt)
if (el) el.textContent = text(player[opt])
}
adjustMSDisp = () => {
let displays = ["always", "automation", "incomplete", "never"];
player.msDisplay = displays[(displays.indexOf(player.msDisplay) + 1) % displays.length]
document.getElementById("msdisp").innerHTML = `Show Milestones: ${player.msDisplay.toUpperCase()}`
}
const optPrompt = promptBuilder("vanilla options", el => {
el.appendChild(button("Save", save));
el.appendChild(button("HARD RESET", hardReset))
el.appendChild(button("Export", exportSave))
el.appendChild(button("Import", importSave))
el.appendChild(button(`Autosave: ${player.autosave ? "ON" : "OFF"}`, () => toggleOpt("autosave", bool => `Autosave: ${bool ? "ON" : "OFF"}`), "autosave"))
el.appendChild(button(`Show Milestones: ${player.msDisplay.toUpperCase()}`, adjustMSDisp, "msdisp"))
}, 250, true)
drawTreeBranch = (a, b) => {
let c = document.getElementById(a).getBoundingClientRect();
let d = document.getElementById(b).getBoundingClientRect();
let e = c.left + (c.width / 2) + (document.getElementById("treeTab").scrollLeft || document.body.scrollLeft);
let f = c.top + (c.height / 2) + (document.getElementById("treeTab").scrollTop || document.body.scrollTop);
let g = d.left + (d.width / 2) + (document.getElementById("treeTab").scrollLeft || document.body.scrollLeft);
let h = d.top + (d.height / 2) + (document.getElementById("treeTab").scrollTop || document.body.scrollTop);
ctx.lineWidth = 15;
ctx.beginPath();
ctx.strokeStyle = darkTheme ? "white" : "black";
ctx.moveTo(e, f);
ctx.lineTo(g, h);
ctx.stroke();
};
// HTML changing-upping
function button(text, click, id) {
const btn = document.createElement("button");
btn.classList.add("opt");
btn.innerHTML = text;
btn.onclick = click;
if (id) btn.id = id;
return btn;
}
function header(name) {
const el = document.createElement("div");
el.zIndex = "1000";
el.padding = "10px";
el.style.userSelect = "none";
el.style.backgroundColor = "darkgrey";
el.id = `${name}header`;
el.style.transition = "0s";
el.style.textTransform = "capitalize";
el.style.overflow = "hidden";
el.innerHTML = `${name.substr(4)}<button style="
position: absolute;
right: 0;
background: none;
color: red;
border: none;
" onmousedown="this.parentElement.parentElement.remove()">x</button>`;
return el.outerHTML;
}
function newBTPTPrompt() {
if (document.getElementById("btptmain")) document.getElementById("btptmain").remove();
const el = document.createElement("div")
el.style.position = "absolute";
el.style.backgroundColor = "grey";
el.style.left = "0px";
el.style.top = "0px";
el.style.transition = "0s";
el.id = "btptmain"
el.innerHTML = header("btptmain");
el.style.zIndex = "999";
const el2 = document.createElement("div");
el2.innerHTML = "<h3 style='display: block'>Better TPT</h3>"
el2.style.resize = "both";
el2.style.overflow = "auto";
el2.style.transition = "0s";
el2.style.width = "300px";
el2.style.height = "250px";
el2.appendChild(button("Auto Upgrades", autoBuyPrompt));
el2.appendChild(button("Auto Prestige", autoPrestigePrompt));
el2.appendChild(button("Misc. Automation", miscPrompt));
el2.appendChild(button("Style Options", stylePrompt));
el2.appendChild(button("Vanilla Options", optPrompt));
el2.appendChild(button("About Better TPT", infoPrompt));
el.appendChild(el2)
document.body.appendChild(el);
dragElement(document.getElementById("btptmain"));
}
showTab = name => {
if (mod === "default") {
if (LAYERS.includes(name) && !layerUnl(name)) return
}
else if (!TAB_REQS[name]()) return
const bk = player.tab;
player.tab = name
if (mod === "default") {
var toTreeTab = name == "tree"
if (toTreeTab != onTreeTab) {
document.getElementById("treeTab").className = toTreeTab ? "fullWidth" : "col left"
onTreeTab = toTreeTab
resizeCanvas()
}
delete player.notify[name]
} else {
if (name === "tree") {
needCanvasUpdate = true;
}
}
if (name === "options") {
player.tab = bk;
newBTPTPrompt();
document.getElementById("optionWheel").onclick = newBTPTPrompt;
}
if (!darkTheme) {
setTimeout(() => {
document.querySelectorAll("img").forEach(n => (n.style.filter = "invert(100%)"))
}, 0);
}
LAYERS.forEach(layer => {
try {
document.querySelector(`.${layer.toLowerCase()}`).oncontextmenu = e => {
e.preventDefault();
doReset(layer);
}
} catch (e) {}
})
};
// Autobuy!
let capsuleAuto = false;
let enhanceAuto = false;
let autoprestige = {};
let autobuy = {};
const cache = {};
function buyAll(type) {
if (!cache[type]) {
if (!LAYER_UPGS[type]) return;
cache[type] = Object.keys(LAYER_UPGS[type]).filter(n => Number(n) == n).map(Number);
}
cache[type].forEach(upg => buyUpg(type, upg));
}
clearInterval(interval);
interval = setInterval(() => {
if (player === undefined || tmp === undefined) return;
let diff = (Date.now() - player.time) / 1000
if (offTime.remain > 0) {
offTime.speed = offTime.remain / 5 + 1
diff += offTime.speed/50
offTime.remain = Math.max(offTime.remain - offTime.speed / 50, 0)
}
player.time = Date.now()
if (needCanvasUpdate) resizeCanvas();
updateTemp();
gameLoop(new Decimal(diff))
Object.keys(autoprestige).filter(n => autoprestige[n]).forEach(n => doReset(n));
Object.keys(autobuy).filter(n => autobuy[n]).forEach(buyAll);
if (capsuleAuto) buyExtCapsule();
if (enhanceAuto) buyEnhancer();
}, 50);
setInterval(() => {
localStorage.setItem("btpt", JSON.stringify({
altFont, darkTheme, autobuy, autoprestige, capsuleAuto, enhanceAuto
}));
}, 2500)
// Load, if it's there
const BTPTData = localStorage.getItem("btpt")
if (BTPTData) {
const d = JSON.parse(BTPTData)
if (d.altFont !== undefined) { altFont = d.altFont; toggleFont(false); }
if (d.darkTheme !== undefined) { darkTheme = d.darkTheme; toggleTheme(false); }
if (d.autoprestige !== undefined) autoprestige = d.autoprestige;
if (d.autobuy !== undefined) autobuy = d.autobuy;
if (d.capsuleAuto !== undefined) capsuleAuto = d.capsuleAuto;
if (d.enhanceAuto !== undefined) enhanceAuto = d.enhanceAuto;
}
var zIndex = 1000;
// TAKEN FROM https://www.w3schools.com/howto/howto_js_draggable.asp
// Make the DIV element draggable:
function dragElement(elmnt) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "header")) {
// if present, the header is where you move the DIV from:
document.getElementById(elmnt.id + "header").onmousedown = dragMouseDown;
} else {
// otherwise, move the DIV from anywhere inside the DIV:
elmnt.onmousedown = dragMouseDown;
}
elmnt.onclick = () => { zIndex++; elmnt.style.zIndex = zIndex; };
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
// get the mouse cursor position at startup:
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// call a function whenever the cursor moves:
document.onmousemove = elementDrag;
zIndex++;
elmnt.style.zIndex = zIndex;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// calculate the new cursor position:
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// set the element's new position:
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
// stop moving when mouse button is released:
document.onmouseup = null;
document.onmousemove = null;
}
}
// https://stackoverflow.com/a/46285637/10637301
function addGlobalStyle(css, id) {
var head, style;
head = document.getElementsByTagName('head')[0];
if (!head) { return; }
style = document.createElement('style');
style.type = 'text/css';
if (id) style.id = id;
style.innerHTML = css.replace(/;/g, ' !important;');
head.appendChild(style);
}