NEW! custom upgrade builds update! Your upgrade builds will save now! This is still in beta!
// ==UserScript==
// @name Diep.io Custom Points Upgrader - Optimized
// @namespace http://tampermonkey.net/
// @version 10.08
// @homepage https://greasyfork.org/scripts/416440
// @description NEW! custom upgrade builds update! Your upgrade builds will save now! This is still in beta!
// @author -{Abyss⌬}-ora
// @match https://diep.io/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
"use strict";
// ========== CONSTANTS ==========
const MAX_POINTS = 33;
const MAX_ATTRIBUTE_LEVEL = 7;
const STORAGE_KEYS = {
USERNAME: "quickSpawnUsername",
SAVED_NAMES: "savedNames",
SAVED_BUTTONS: "savedButtons",
REMOVED_DEFAULTS: "removedDefaults",
BACKUP_DATA: "backupData",
SCRIPT_VERSION: "scriptVersion",
MENU_POSITION: "menuPosition"
};
const ATTRIBUTES = [
{ name: "Health Regen", color: "rgb(232, 188, 157)", maxLevel: 10 },
{ name: "Max Health", color: "rgb(230, 128, 234)", maxLevel: 10 },
{ name: "Body Damage", color: "rgb(165, 128, 234)", maxLevel: 10 },
{ name: "Bullet Speed", color: "rgb(128, 162, 234)", maxLevel: 7 },
{ name: "Bullet Penetration", color: "rgb(234, 215, 128)", maxLevel: 7 },
{ name: "Bullet Damage", color: "rgb(234, 128, 128)", maxLevel: 7 },
{ name: "Reload", color: "rgb(164, 234, 128)", maxLevel: 7 },
{ name: "Movement Speed", color: "rgb(128, 234, 230)", maxLevel: 10 }
];
const BACKGROUND_IMAGE = "url('https://media.tenor.com/images/f3f5354b7c304bc61882dbb1183885e7/tenor.gif')";
// ========== HELPER FUNCTIONS ==========
const getStorage = (key) => localStorage.getItem(key);
const setStorage = (key, value) => localStorage.setItem(key, value);
const getStorageJSON = (key) => JSON.parse(getStorage(key) || "null") || [];
const setStorageJSON = (key, value) => setStorage(key, JSON.stringify(value));
const createElement = (tag, className, attributes = {}) => {
const el = document.createElement(tag);
if (className) el.className = className;
Object.entries(attributes).forEach(([key, value]) => {
if (key === 'style') {
Object.assign(el.style, value);
} else {
el[key] = value;
}
});
return el;
};
const applyStyles = (element, styles) => Object.assign(element.style, styles);
const toggleVisibility = (element) => {
if (element) {
element.style.display = element.style.display === "none" ? "block" : "none";
}
};
function getScriptVersion() {
const metadataBlock = `
// ==UserScript==
// @version 10.08
// ==/UserScript==`;
const versionMatch = metadataBlock.match(/@version\s+([\d.]+)/);
return versionMatch ? versionMatch[1] : null;
}
function saveDataBeforeUpdate() {
const dataToSave = Object.fromEntries(
Object.entries(STORAGE_KEYS)
.filter(([key]) => key !== 'BACKUP_DATA' && key !== 'SCRIPT_VERSION')
.map(([_, value]) => [value, getStorage(value)])
);
setStorageJSON(STORAGE_KEYS.BACKUP_DATA, dataToSave);
}
function retrieveDataAfterUpdate() {
const backupData = getStorageJSON(STORAGE_KEYS.BACKUP_DATA);
if (backupData && Object.keys(backupData).length) {
Object.entries(backupData).forEach(([key, value]) => {
if (value) setStorage(key, value);
});
}
}
function checkAndUpdateVersion() {
const currentVersion = getScriptVersion();
const savedVersion = getStorage(STORAGE_KEYS.SCRIPT_VERSION);
if (!savedVersion || savedVersion < currentVersion) {
retrieveDataAfterUpdate();
setStorage(STORAGE_KEYS.SCRIPT_VERSION, currentVersion);
}
}
function spawnWithBuild(cmd) {
const spawnName = userInput.value.trim();
window.input.execute(`game_spawn ${spawnName}`);
window.input.execute(`game_stats_build ${cmd}`);
toggleVisibility(document.getElementById("myhover"));
}
function dragElement(elmnt) {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
const header = document.getElementById(elmnt.id + "Header");
(header || elmnt).onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
function adjustFontSize(element) {
let fontSize = 16;
element.style.fontSize = fontSize + "px";
while (element.scrollWidth > element.clientWidth && fontSize > 6) {
element.style.fontSize = --fontSize + "px";
}
}
// ========== ATTRIBUTE EDITOR CREATION ==========
function createAttributeEditor(popupId = "", initialCode = "", onUpdate) {
const container = createElement("div");
let code = initialCode;
let totalPoints = MAX_POINTS;
const pointsCounter = createElement("div", null, {
id: `pointsCounter${popupId ? '-' + popupId : ''}`,
textContent: `Points: ${MAX_POINTS}`,
style: {
display: "inline-block",
padding: "2px 6px",
backgroundColor: "rgba(0, 0, 0, 0.5)",
borderRadius: "5px",
marginBottom: "5px"
}
});
const codeString = createElement("input", null, {
id: `codeString${popupId ? '-' + popupId : ''}`,
type: "text",
value: code,
style: {
display: "block",
width: "80%",
margin: "0 auto 10px auto",
textAlign: "center",
fontSize: "18px"
}
});
function updateAttributes() {
totalPoints = MAX_POINTS;
let isSmasherBuild = false;
ATTRIBUTES.forEach((attribute, index) => {
const attributeRow = document.getElementById(`attributeRow-${popupId}-${index}`);
if (!attributeRow) return;
const maxLevel = attribute.maxLevel || MAX_ATTRIBUTE_LEVEL;
const attributeCount = Math.min(
(code.match(new RegExp(index + 1, "g")) || []).length,
maxLevel
);
// Check if this is a smasher attribute with more than 7 points
if (maxLevel > MAX_ATTRIBUTE_LEVEL && attributeCount > MAX_ATTRIBUTE_LEVEL) {
isSmasherBuild = true;
}
// Update minus button color based on whether row has points
const minusButton = attributeRow.children[0];
if (attributeCount > 0) {
minusButton.style.backgroundColor = attribute.color;
} else {
minusButton.style.backgroundColor = "gray";
}
// Handle base 7 squares
for (let i = 0; i < MAX_ATTRIBUTE_LEVEL; i++) {
const square = attributeRow.children[i + 1];
if (i < Math.min(attributeCount, MAX_ATTRIBUTE_LEVEL)) {
square.style.backgroundColor = attribute.color;
square.style.border = "1px solid black";
totalPoints--;
} else {
square.style.backgroundColor = "gray";
square.style.border = "0";
}
}
// Handle bonus squares (8th, 9th, 10th) for attributes with maxLevel > 7
if (maxLevel > MAX_ATTRIBUTE_LEVEL) {
const bonusSquaresNeeded = Math.max(0, attributeCount - MAX_ATTRIBUTE_LEVEL);
const existingBonusSquares = attributeRow.querySelectorAll('.bonus-square').length;
// Add bonus squares if needed
for (let i = existingBonusSquares; i < bonusSquaresNeeded; i++) {
const bonusSquare = createElement("div", "bonus-square", {
style: {
border: "1px solid black",
backgroundColor: attribute.color,
width: "30px",
height: "30px",
display: "inline-block",
position: "relative",
padding: "0 5px",
animation: "bounceIn 0.3s ease-out"
}
});
attributeRow.insertBefore(bonusSquare, attributeRow.lastChild);
totalPoints--;
}
// Remove bonus squares if needed
const allBonusSquares = Array.from(attributeRow.querySelectorAll('.bonus-square'));
for (let i = bonusSquaresNeeded; i < allBonusSquares.length; i++) {
allBonusSquares[i].remove();
}
}
});
// Update points display
if (isSmasherBuild || totalPoints < 0 || totalPoints > MAX_POINTS) {
pointsCounter.textContent = `Points: modified for Smashers`;
} else {
pointsCounter.textContent = `Points: ${totalPoints}`;
}
if (onUpdate) onUpdate(code, totalPoints);
}
function createAttributeRow(attribute, index) {
const attributeRow = createElement("div", "attribute", {
id: `attributeRow-${popupId}-${index}`,
style: { position: "relative" }
});
const minusButton = createElement("button", null, {
textContent: "-",
style: {
padding: "0 5px",
border: "1px solid black",
borderRadius: "50px 0 0 50px",
width: "32px",
height: "32px",
color: "black",
fontWeight: "bold",
backgroundColor: "gray"
}
});
minusButton.onclick = () => {
const coloredSquares = Array.from(attributeRow.children).filter(
child => child !== plusButton && child !== minusButton && child.style.backgroundColor === attribute.color
);
if (coloredSquares.length > 0) {
const square = coloredSquares[coloredSquares.length - 1];
square.style.backgroundColor = "gray";
square.style.border = "0";
code = code.slice(0, -1);
codeString.value = code;
updateAttributes();
}
};
attributeRow.appendChild(minusButton);
for (let i = 0; i < MAX_ATTRIBUTE_LEVEL; i++) {
const colorDiv = createElement("div", null, {
style: {
border: "0",
backgroundColor: "gray",
width: "30px",
height: "30px",
display: "inline-block",
position: "relative",
padding: "0 5px"
}
});
if (i === 3) {
const textSpan = createElement("span", null, {
textContent: attribute.name,
style: {
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
color: "white",
pointerEvents: "none",
whiteSpace: "nowrap",
zIndex: "1",
textShadow: "1px 1px #000, 2px 2px #555, -1px -1px #000, -2px -2px #555"
}
});
colorDiv.appendChild(textSpan);
}
attributeRow.appendChild(colorDiv);
}
const plusButton = createElement("button", null, {
textContent: "+",
style: {
padding: "0 5px",
border: "1px solid black",
borderRadius: "0 50px 50px 0",
width: "32px",
height: "32px",
color: "black",
fontWeight: "bold",
backgroundColor: attribute.color
}
});
plusButton.onclick = () => {
const maxLevel = attribute.maxLevel || MAX_ATTRIBUTE_LEVEL;
const currentCount = (code.match(new RegExp(index + 1, "g")) || []).length;
if (currentCount < maxLevel) {
code += (index + 1);
codeString.value = code;
updateAttributes();
}
};
attributeRow.appendChild(plusButton);
return attributeRow;
}
codeString.addEventListener("input", (event) => {
code = event.target.value;
updateAttributes();
});
container.appendChild(pointsCounter);
container.appendChild(codeString);
ATTRIBUTES.forEach((attribute, index) => {
container.appendChild(createAttributeRow(attribute, index));
});
// Call updateAttributes after a short delay to ensure DOM is ready
setTimeout(() => updateAttributes(), 0);
return {
container,
getCode: () => code,
pointsCounter,
codeString
};
}
// ========== POPUP CREATION ==========
function createPopup(title, popupId = `popup-${Date.now()}`) {
const popup = createElement("div", "popup", {
id: popupId,
innerHTML: `<div class="popup-header" id="${popupId}Header">${title}</div>`
});
const closeButton = createElement("button", "close-btn", {
textContent: "X",
onclick: () => document.body.removeChild(popup)
});
popup.appendChild(closeButton);
document.body.appendChild(popup);
dragElement(popup);
return popup;
}
function createEditPopup(buttonContainer, buttonData) {
const popupId = `editPopup-${Date.now()}`;
const popup = createPopup("Drag me", popupId);
const buildNameInput = createElement("input", null, {
type: "text",
placeholder: "Build Name",
value: buttonData.name,
style: {
display: "block",
width: "80%",
margin: "0 auto 10px auto"
}
});
popup.appendChild(buildNameInput);
const { container, getCode } = createAttributeEditor(popupId, buttonData.cmd);
popup.appendChild(container);
// Trigger update after container is in DOM
setTimeout(() => {
const event = new Event('input', { bubbles: true });
popup.querySelector(`#codeString-${popupId}`).dispatchEvent(event);
}, 10);
const saveChangesButton = createElement("button", null, {
textContent: "Save Changes",
style: {
display: "block",
margin: "10px auto",
width: "80%",
fontSize: "14px",
padding: "8px",
backgroundColor: "white",
border: "2px solid black",
borderRadius: "5px",
cursor: "pointer"
},
onclick: () => {
const buildName = buildNameInput.value.trim();
if (!buildName) {
alert("Please enter a build name.");
return;
}
buttonData.name = buildName;
buttonData.cmd = getCode();
buttonContainer.querySelector(".button").textContent = buildName;
const savedButtons = getStorageJSON(STORAGE_KEYS.SAVED_BUTTONS);
const updatedButtons = savedButtons.map(b =>
b.cmd === buttonData.cmd && b.name !== buildName ? b :
b.name === buttonData.name || b.cmd === buttonData.cmd ? buttonData : b
);
setStorageJSON(STORAGE_KEYS.SAVED_BUTTONS, updatedButtons);
document.body.removeChild(popup);
}
});
const deleteButton = createElement("button", null, {
textContent: "Delete Build",
style: {
display: "block",
margin: "10px auto",
width: "80%",
fontSize: "14px",
padding: "8px",
backgroundColor: "white",
border: "2px solid black",
borderRadius: "5px",
cursor: "pointer"
},
onclick: () => {
const confirmPopup = createPopup("Drag Me", `confirmDelete-${popupId}`);
const message = createElement("div", null, {
textContent: "Are you sure you want to delete this build?",
style: {
textAlign: "center",
marginBottom: "20px"
}
});
const btnContainer = createElement("div", null, {
style: {
display: "flex",
justifyContent: "space-between"
}
});
const noBtn = createElement("button", "button", {
textContent: "NO",
onclick: () => document.body.removeChild(confirmPopup)
});
const yesBtn = createElement("button", "button", {
textContent: "YES",
onclick: () => {
buttonContainer.remove();
const savedButtons = getStorageJSON(STORAGE_KEYS.SAVED_BUTTONS);
setStorageJSON(STORAGE_KEYS.SAVED_BUTTONS,
savedButtons.filter(b => b.name !== buttonData.name)
);
document.body.removeChild(confirmPopup);
document.body.removeChild(popup);
}
});
btnContainer.appendChild(noBtn);
btnContainer.appendChild(yesBtn);
confirmPopup.appendChild(message);
confirmPopup.appendChild(btnContainer);
}
});
popup.appendChild(saveChangesButton);
popup.appendChild(deleteButton);
}
// ========== INITIALIZATION ==========
saveDataBeforeUpdate();
checkAndUpdateVersion();
// ========== STYLES ==========
const style = createElement("style", null, {
textContent: `
#myhover a {
z-index: 999;
position: absolute;
top: 300px;
transition: 0.3s;
width: 250px;
padding: 45px 15px 15px 15px; /* extra top space for drag bar */
background-image: ${BACKGROUND_IMAGE};
background-color: #555;
text-decoration: none;
font-size: 10px;
font-family: 'Monoton', cursive;
text-shadow: 1px 1px #000, 2px 2px #555;
color: white;
border: double thick white;
border-radius: 20px;
/* cursor removed to allow normal pointer except drag header */
}
/* Drag header inside main menu */
#myhover a .main-drag-header {
position: absolute;
top: 5px;
left: 5px;
right: 45px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(5,5,5,0.85);
color: #fff;
font-size: 12px;
font-family: 'Monoton', cursive;
text-shadow: 1px 1px #000, 2px 2px #555;
border: 1px solid white;
border-radius: 12px;
cursor: grab;
user-select: none;
}
#myhover a .main-drag-header:active { cursor: grabbing; }
/* Right side (default) */
#myhover a.snap-right { right: -260px; left: auto; }
#myhover a.snap-right:hover { right: 0; }
/* Left side */
#myhover a.snap-left { left: -260px; right: auto; }
#myhover a.snap-left:hover { left: 0; }
/* During drag: disable hover and let JS control position */
.dragging,
.dragging * {
transform: none !important;
transition: none !important;
}
.dragging:hover {
transform: none !important;
transition: none !important;
}
/* Magnet snap animation - ease in (slow start) then ease out (slow end) with bounce */
#myhover a.magnet-snap {
transition: left 0.6s cubic-bezier(0.34, 1.56, 0.64, 1),
right 0.6s cubic-bezier(0.34, 1.56, 0.64, 1),
top 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.button {
display: block;
margin: 5px auto;
width: 90%;
text-align: center;
font-size: 18px;
font-family: 'Jersey 10', sans-serif;
color: black;
background-color: white;
border-radius: 5px;
transition: 0.4s;
cursor: pointer;
}
.button:hover { transform: translateX(-10px); }
#userInput {
margin: 5px auto;
width: 90%;
padding: 8px;
font-family: 'Monoton', cursive;
text-align: center;
}
#specialButton {
display: block;
margin: 5px auto;
width: 90%;
text-align: center;
font-size: 18px;
font-family: 'Jersey 10', sans-serif;
color: white;
background-color: black;
border-radius: 5px;
border-color: white;
transition: 0.4s;
cursor: pointer;
opacity: 0.8;
}
#specialButton:hover {
color: black;
background-color: darkgray;
transform: translateX(-10px);
}
.popup {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 300px;
background-image: ${BACKGROUND_IMAGE};
background-color: white;
border: double thick white;
border-radius: 10px;
padding: 20px;
padding-top: 50px;
z-index: 1000;
text-shadow: 1px 1px #000, 2px 2px #555;
}
.attribute {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.close-btn {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 1px;
right: 0px;
width: 40px;
height: 35px;
color: white;
cursor: pointer;
font-size: 18px;
transition: background-color 0.3s;
text-shadow: 1px 1px #000, 2px 2px #555;
border-radius: 10px;
background-color: rgba(0, 0, 0, 0.8);
z-index: 10;
}
.close-btn:hover { background-color: red; }
#topRightButton {
position: absolute;
top: 5px;
right: 5px;
width: 30px;
height: 30px;
padding: 0;
background-color: rgba(5,5,5,0.85);
color: white;
border: 1px solid white;
border-radius: 12px;
cursor: pointer;
font-size: 16px;
display: flex;
justify-content: center;
align-items: center;
font-family: 'Monoton', cursive;
text-shadow: 1px 1px #000, 2px 2px #555;
transition: background-color 0.3s;
z-index: 1001;
}
#topRightButton:hover { background-color: rgba(255,0,0,0.85); }
#buildButtonsContainer {
max-height: 400px;
overflow-y: scroll;
margin-left: 5px;
-ms-overflow-style: none;
scrollbar-width: none;
}
::-webkit-scrollbar { width: 10px; }
::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 10px;
}
::-webkit-scrollbar-thumb {
background: #888;
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover { background: #555; }
.popup-header {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 1px;
left: 0px;
right: 35px;
height: 30px;
cursor: grabbing;
z-index: 10;
background-color: rgba(5, 5, 5, 0.8);
color: #fff;
font-size: 10px;
font-family: 'Monoton', cursive;
text-shadow: 1px 1px #000, 2px 2px #555;
border-radius: 10px;
margin-bottom: 10px;
}
.edit-button {
background-color: gray;
border: none;
color: white;
font-size: 18px;
cursor: pointer;
width: 30px;
height: 30px;
margin-right: 5px;
transition: background-color 0.3s;
}
.edit-button:hover { background-color: lightgray; }
@keyframes bounceIn {
0% { transform: scale(0); opacity: 0; }
50% { transform: scale(1.2); }
100% { transform: scale(1); opacity: 1; }
}
`});
document.head.appendChild(style);
// ========== MENU CREATION ==========
const hoverMenu = createElement("div", "hover", { id: "myhover" });
const modMenu = createElement("a", null, { id: "modtab" });
// Start snapped to right by default
modMenu.classList.add('snap-right');
const dragHeader = createElement('div','main-drag-header',{textContent:'Drag Menu'});
const menuHeader = createElement("h1");
const versionText = createElement("small");
const smallerVersionText = createElement("small", null, {
textContent: `Version: ${getScriptVersion()}`
});
versionText.appendChild(smallerVersionText);
menuHeader.appendChild(versionText);
menuHeader.appendChild(document.createElement("br"));
menuHeader.appendChild(document.createTextNode("-{Abyss⌬}-ora's Mod Menu beta"));
modMenu.appendChild(dragHeader);
modMenu.appendChild(menuHeader);
// Enable drag & snap behavior for main menu anchor
(function enableDragAndSnap(el, handle){
let dragging = false;
let offsetX = 0;
let offsetY = 0;
// Load saved position
function loadPosition() {
const saved = getStorageJSON(STORAGE_KEYS.MENU_POSITION);
if (saved && saved.snapSide) {
el.classList.remove('snap-right', 'snap-left');
el.classList.add(saved.snapSide);
if (saved.top !== undefined) {
el.style.top = saved.top + 'px';
}
}
}
// Save position
function savePosition(snapSide, top) {
setStorageJSON(STORAGE_KEYS.MENU_POSITION, { snapSide, top });
}
// Keep menu on screen when window resizes
function keepOnScreen() {
if (dragging) return;
const saved = getStorageJSON(STORAGE_KEYS.MENU_POSITION);
const rect = el.getBoundingClientRect();
const viewportHeight = window.innerHeight;
const menuHeight = rect.height;
// Always try to use saved position if it exists
let targetTop = saved && saved.top !== undefined ? saved.top : rect.top;
// Check if saved position would fit on screen
let newTop = targetTop;
// If menu doesn't fit on screen at all, show top part
if (menuHeight > viewportHeight) {
newTop = 0;
} else {
// Clamp to viewport bounds
if (targetTop + menuHeight > viewportHeight) {
newTop = viewportHeight - menuHeight;
}
if (targetTop < 0) {
newTop = 0;
}
}
// Apply position with bounce animation if changed
if (newTop !== rect.top) {
el.classList.add('magnet-snap');
el.style.top = newTop + 'px';
setTimeout(() => { el.classList.remove('magnet-snap'); }, 600);
}
// Note: We don't save here - only save when user manually moves it
}
function onMouseDown(e){
if(e.button !== 0) return;
dragging = true;
// Freeze all transforms so visuals match reality
el.classList.add("dragging");
// Get actual pixel position on screen ignoring transforms
const rect = el.getBoundingClientRect();
// Calculate offset from mouse to element
offsetX = e.clientX - rect.left;
offsetY = e.clientY - rect.top;
// Remove snap classes
el.classList.remove('snap-right', 'snap-left');
// Switch to absolute positioning locked to the screen location
el.style.position = "absolute";
el.style.left = rect.left + "px";
el.style.top = rect.top + "px";
el.style.right = "auto";
e.preventDefault();
}
function onMouseMove(e){
if (!dragging) return;
// Move EXACTLY with the mouse but constrain to viewport
const newLeft = e.clientX - offsetX;
const newTop = e.clientY - offsetY;
// Get element dimensions
const rect = el.getBoundingClientRect();
const maxLeft = window.innerWidth - rect.width;
const maxTop = window.innerHeight - rect.height;
// Constrain position within viewport bounds
el.style.left = Math.max(0, Math.min(newLeft, maxLeft)) + "px";
el.style.top = Math.max(0, Math.min(newTop, maxTop)) + "px";
}
function onMouseUp(){
if (!dragging) return;
dragging = false;
el.classList.remove("dragging");
// Determine snap side based on center position
const rect = el.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const mid = window.innerWidth / 2;
let snapRight = centerX > mid;
let snapSide = snapRight ? 'snap-right' : 'snap-left';
// Apply magnet snap animation
el.classList.add('magnet-snap');
if(snapRight){
el.classList.add('snap-right');
el.style.left = 'auto';
el.style.right = '';
} else {
el.classList.add('snap-left');
el.style.right = 'auto';
el.style.left = '';
}
// Save position
savePosition(snapSide, rect.top);
setTimeout(() => { el.classList.remove('magnet-snap'); }, 600);
}
handle.addEventListener('mousedown', onMouseDown);
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
window.addEventListener('resize', keepOnScreen);
// Load saved position on init
loadPosition();
// Check position after a brief delay to ensure DOM is ready
setTimeout(keepOnScreen, 100);
})(modMenu, dragHeader);
// ========== USERNAME INPUT ==========
const inputContainer = createElement("div", null, {
style: {
position: "relative",
width: "90%",
margin: "5px auto"
}
});
const heartIcon = createElement("span", null, {
textContent: "♥",
style: {
position: "absolute",
left: "10px",
top: "50%",
transform: "translateY(-50%)",
cursor: "pointer",
fontSize: "18px",
color: "black"
}
});
const userInput = createElement("input", null, {
id: "userInput",
type: "text",
placeholder: "Enter Username",
value: getStorage(STORAGE_KEYS.USERNAME) || "",
style: {
width: "100%",
padding: "8px 30px",
border: "2px solid black",
borderRadius: "5px",
boxSizing: "border-box",
fontFamily: "'Monoton', cursive",
textAlign: "center"
}
});
const dropdownArrow = createElement("span", null, {
textContent: "▼",
style: {
position: "absolute",
right: "10px",
top: "50%",
transform: "translateY(-50%)",
cursor: "pointer",
fontSize: "14px",
color: "black"
}
});
const dropdown = createElement("div", null, {
id: "dropdownMenu",
style: {
position: "absolute",
width: "100%",
top: "100%",
left: "0",
backgroundImage: BACKGROUND_IMAGE,
border: "4px double white",
borderTop: "none",
display: "none",
zIndex: "100",
maxHeight: "150px",
overflowY: "auto"
}
});
function updateDropdown(names) {
dropdown.innerHTML = "";
names.forEach(name => {
const optionContainer = createElement("div", null, {
style: {
display: "flex",
alignItems: "center",
justifyContent: "space-between",
padding: "8px",
cursor: "pointer",
fontFamily: "'Monoton', cursive",
textAlign: "center",
fontSize: "18px",
backgroundImage: BACKGROUND_IMAGE,
transition: "background 0.3s"
}
});
const option = createElement("div", null, {
textContent: name,
style: { flexGrow: "1", textAlign: "left" }
});
option.addEventListener("mouseover", () => {
optionContainer.style.backgroundColor = "rgba(169, 169, 169, 0.8)";
});
option.addEventListener("mouseout", () => {
optionContainer.style.backgroundColor = "transparent";
});
option.addEventListener("click", () => {
userInput.value = name;
dropdown.style.display = "none";
setStorage(STORAGE_KEYS.USERNAME, name);
updateHeartIcon();
});
optionContainer.appendChild(option);
dropdown.appendChild(optionContainer);
});
}
function updateHeartIcon() {
const name = userInput.value.trim();
const savedNames = getStorageJSON(STORAGE_KEYS.SAVED_NAMES);
heartIcon.style.color = savedNames.includes(name) ? "red" : "black";
}
dropdownArrow.addEventListener("click", () => toggleVisibility(dropdown));
document.addEventListener("click", (event) => {
if (!inputContainer.contains(event.target)) {
dropdown.style.display = "none";
}
});
userInput.addEventListener("input", (event) => {
setStorage(STORAGE_KEYS.USERNAME, event.target.value);
updateHeartIcon();
});
heartIcon.addEventListener("click", () => {
const name = userInput.value.trim();
if (!name) return;
const savedNames = getStorageJSON(STORAGE_KEYS.SAVED_NAMES);
const nameIndex = savedNames.indexOf(name);
if (nameIndex === -1) {
savedNames.push(name);
heartIcon.style.color = "red";
} else {
savedNames.splice(nameIndex, 1);
heartIcon.style.color = "black";
}
setStorageJSON(STORAGE_KEYS.SAVED_NAMES, savedNames);
updateDropdown(savedNames);
});
inputContainer.appendChild(heartIcon);
inputContainer.appendChild(userInput);
inputContainer.appendChild(dropdownArrow);
inputContainer.appendChild(dropdown);
modMenu.appendChild(inputContainer);
const savedNames = getStorageJSON(STORAGE_KEYS.SAVED_NAMES);
updateDropdown(savedNames);
updateHeartIcon();
// ========== BUILD BUTTONS CONTAINER ==========
const buildButtonsContainer = createElement("div", null, { id: "buildButtonsContainer" });
modMenu.appendChild(buildButtonsContainer);
function createButton(buttonData) {
const buttonContainer = createElement("div", null, {
style: {
display: "flex",
alignItems: "center",
justifyContent: "center"
}
});
const editButton = createElement("button", "edit-button", {
textContent: "🖉",
onclick: () => createEditPopup(buttonContainer, buttonData)
});
const button = createElement("button", "button", {
textContent: buttonData.name,
style: { backgroundColor: buttonData.color },
onclick: () => spawnWithBuild(buttonData.cmd)
});
buttonContainer.appendChild(editButton);
buttonContainer.appendChild(button);
buildButtonsContainer.appendChild(buttonContainer);
}
// Load saved buttons
const savedButtons = getStorageJSON(STORAGE_KEYS.SAVED_BUTTONS);
savedButtons.forEach(createButton);
// ========== ADD BUILD BUTTON ==========
const specialButton = createElement("button", null, {
id: "specialButton",
textContent: "[+]",
onclick: () => {
const popupId = "createBuildPopup";
const popup = createPopup("Drag me", popupId);
const buildNameInput = createElement("input", null, {
type: "text",
placeholder: "Build Name",
style: {
display: "block",
width: "80%",
margin: "0 auto 10px auto"
}
});
popup.appendChild(buildNameInput);
const { container, getCode } = createAttributeEditor(popupId);
popup.appendChild(container);
const createBuildButton = createElement("button", null, {
textContent: "Create Build",
style: {
display: "block",
margin: "20px auto 0 auto",
width: "80%",
fontSize: "18px",
padding: "10px",
backgroundColor: "white",
border: "2px solid black",
borderRadius: "5px",
cursor: "pointer"
},
onclick: () => {
const buildName = buildNameInput.value.trim();
if (!buildName) {
alert("Please enter a build name.");
return;
}
const newButtonData = {
name: buildName,
color: "#C0C0C0",
cmd: getCode()
};
const savedButtons = getStorageJSON(STORAGE_KEYS.SAVED_BUTTONS);
savedButtons.push(newButtonData);
setStorageJSON(STORAGE_KEYS.SAVED_BUTTONS, savedButtons);
createButton(newButtonData);
document.body.removeChild(popup);
}
});
popup.appendChild(createBuildButton);
}
});
modMenu.appendChild(specialButton);
// ========== RESET BUTTON ==========
const topRightButton = createElement("button", null, {
id: "topRightButton",
textContent: "↻",
onclick: () => {
const resetPopup = createPopup("Reset Confirmation");
const confirmationMessage = createElement("div", null, {
textContent: "Do you really want to reset to default builds?",
style: {
textAlign: "center",
marginBottom: "20px"
}
});
const buttonsContainer = createElement("div", null, {
style: {
display: "flex",
justifyContent: "space-between"
}
});
const noButton = createElement("button", "button", {
textContent: "NO",
onclick: () => document.body.removeChild(resetPopup)
});
const yesButton = createElement("button", "button", {
textContent: "YES",
onclick: () => {
localStorage.removeItem(STORAGE_KEYS.SAVED_BUTTONS);
localStorage.removeItem(STORAGE_KEYS.REMOVED_DEFAULTS);
location.reload();
}
});
buttonsContainer.appendChild(noButton);
buttonsContainer.appendChild(yesButton);
resetPopup.appendChild(confirmationMessage);
resetPopup.appendChild(buttonsContainer);
}
});
modMenu.appendChild(topRightButton);
hoverMenu.appendChild(modMenu);
document.body.appendChild(hoverMenu);
// ========== KEYBOARD SHORTCUT ==========
document.addEventListener("keydown", (event) => {
if ((event.key === "r" || event.key === "R") &&
!["INPUT", "TEXTAREA"].includes(event.target.tagName)) {
toggleVisibility(document.getElementById("myhover"));
}
});
})();