// ==UserScript==
// @name CarManiac's Improved Color Picker
// @namespace http://tampermonkey.net/
// @version v1.40
// @description Extends player appearance editor functionality
// @author CarManiac
// @run-at document-idle
// @license MIT
// @match https://heav.io/game.html
// @match https://hitbox.io/game.html
// @match https://heav.io/game2.html
// @match https://hitbox.io/game2.html
// @match https://hitbox.io/game-beta.html
// @icon https://www.google.com/s2/favicons?sz=64&domain=heav.io
// @grant none
// ==/UserScript==
const style = document.createElement("style");
style.textContent = `
button:hover,
.crossButton:hover {
background-color: #5c85b4 !important;
}
`;
document.head.appendChild(style);
const targetImage = 'graphics/ui/hitbox.svg';
const newImageSrc = 'https://i.ibb.co/F5RLpmx/hitbox-1.png';
const images = document.querySelectorAll(`img[src="${targetImage}"]`);
images.forEach(img => {
const overlayImage = document.createElement('img');
overlayImage.src = newImageSrc;
overlayImage.style.position = 'absolute';
overlayImage.style.left = img.offsetLeft + 'px';
overlayImage.style.top = img.offsetTop + 'px';
overlayImage.style.width = img.width + 'px';
overlayImage.style.height = img.height + 'px';
overlayImage.style.pointerEvents = 'none';
img.parentNode.appendChild(overlayImage);
});
const originalSend = window.WebSocket.prototype.send;
let WSS = 0;
window.WebSocket.prototype.send = async function(args) {
if (this.url && this.url.includes("peer")) {
return;
}
if (this.url.includes("/socket.io/?EIO=3&transport=websocket&sid=")) {
if (typeof args === "string") {
if (!WSS) {
WSS = this;
}
}
}
return originalSend.call(this, args);
};
const iroScript = document.createElement("script");
iroScript.src = "https://unpkg.com/@jaames/iro/dist/iro.min.js";
document.head.appendChild(iroScript);
const savedColors = JSON.parse(localStorage.getItem('savedColors')) || [];
let colorPickerContainer;
let deletionMode = false;
function hexToDecimal(hex) {
if (hex.startsWith("#")) hex = hex.slice(1);
return parseInt(hex, 16);
}
function rgbToDecimal(rgb) {
const rgbValues = rgb.match(/\d+/g);
return rgbValues.reduce((acc, val) => (acc << 8) + parseInt(val), 0);
}
function createColorInput() {
if (colorPickerContainer) return;
colorPickerContainer = document.createElement("div");
colorPickerContainer.id = "colorPickerContainer";
colorPickerContainer.style.cssText = "background-color: rgb(37, 38, 42); border-radius: 7px; color: rgb(235, 235, 235); display: flex; flex-direction: column; font-family: 'Bai Jamjuree', sans-serif; position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%); padding: 10px; width: 300px;";
const crossButton = document.createElement("div");
crossButton.className = "crossButton";
crossButton.style.cssText = `
background-color: rgb(74, 122, 177);
background-image: url(https://hitbox.io/graphics/ui/close.svg);
background-position: 50% 50%;
background-repeat: no-repeat;
border-radius: 20px;
color: rgb(235, 235, 235);
cursor: pointer;
display: block;
height: 25.9929px;
position: absolute;
right: -9px;
top: -9px;
width: 25.9929px;
`;
crossButton.addEventListener("click", closeBoth);
const label = document.createElement("label");
label.textContent = "Select Color";
label.style.cssText = "color: rgb(235, 235, 235); font-weight: bold; font-size: 15px; margin-bottom: 10px;";
const colorWheelContainer = document.createElement("div");
colorWheelContainer.style.textAlign = "center";
const colorInput = document.createElement("input");
colorInput.type = "text";
colorInput.style.cssText = `
background-color: rgb(100, 100, 100);
color: white;
padding: 5px;
border: 1px solid #ccc;
border-radius: 5px;
margin-top: 5px;
font-family: 'Bai Jamjuree', sans-serif;
`;
colorInput.placeholder = "Hex (#RRGGBB) or rgb(R, G, B)";
const buttonContainer = document.createElement("div");
buttonContainer.style.display = "flex";
buttonContainer.style.justifyContent = "space-between";
buttonContainer.style.marginTop = "10px";
const applyButton = createButton("APPLY COLOR", "margin: 5px;");
const randomButton = createButton("RANDOM COLOR", "margin: 5px;");
const saveButton = createButton("SAVE COLOR", "margin: 5px;");
const feedbackMessage = document.createElement("div");
feedbackMessage.style.cssText = "margin-top: 10px; color: green; font-size: 15px;";
const savedColorsContainer = document.createElement("div");
savedColorsContainer.style.cssText = "margin-top: 10px; padding-top: 10px; border-top: 1px solid #ccc; color: rgb(235, 235, 235);";
const savedColorsTitle = document.createElement("h4");
savedColorsTitle.textContent = "Saved Colors";
savedColorsTitle.style.cssText = "margin-bottom: 10px; font-size: 14px;";
const savedColorsList = document.createElement("div");
savedColorsList.style.display = "flex";
savedColorsList.style.flexWrap = "wrap";
savedColorsList.style.gap = "10px";
const deleteButton = createButton("DELETE COLORS", "margin-top: 10px; width: 100px;");
colorPickerContainer.append(crossButton, label, colorWheelContainer, colorInput, buttonContainer, saveButton, feedbackMessage, savedColorsContainer);
buttonContainer.append(applyButton, randomButton);
savedColorsContainer.append(savedColorsTitle, savedColorsList, deleteButton);
document.body.appendChild(colorPickerContainer);
displaySavedColors();
const colorWheel = new iro.ColorPicker(colorWheelContainer, {
width: 225,
color: "#ff0000"
});
function getLuminance(hex) {
let r = parseInt(hex.slice(1, 3), 16);
let g = parseInt(hex.slice(3, 5), 16);
let b = parseInt(hex.slice(5, 7), 16);
return (0.299 * r + 0.587 * g + 0.114 * b) / 255;
}
colorWheel.on('color:change', function(color) {
const hexColor = color.hexString;
colorInput.value = hexColor;
colorInput.style.backgroundColor = hexColor;
const luminance = getLuminance(hexColor);
colorInput.style.color = luminance > 0.5 ? 'black' : 'white';
});
saveButton.addEventListener("click", () => saveColor(colorInput.value.trim(), feedbackMessage, savedColorsList));
applyButton.addEventListener("click", () => applyColor(colorInput.value.trim(), feedbackMessage));
randomButton.addEventListener("click", () => generateRandomColor(colorInput, feedbackMessage));
deleteButton.addEventListener("click", () => toggleDeletionMode());
function createButton(text, additionalStyles = "") {
const button = document.createElement("button");
button.textContent = text;
button.style.cssText = `padding: 5px; border: none; font-family: 'Bai Jamjuree', sans-serif; border-radius: 2px; cursor: pointer; color: rgb(235, 235, 235); background-color: rgb(74, 122, 177); ${additionalStyles}`;
return button;
}
function displaySavedColors() {
savedColorsList.innerHTML = '';
savedColors.forEach(color => {
const colorBox = document.createElement("div");
colorBox.style.cssText = `width: 30px; height: 30px; background-color: ${color}; cursor: pointer; border: 1px solid #ccc; border-radius: 3px;`;
colorBox.addEventListener("click", () => {
if (deletionMode) {
confirmDeletion(color);
} else {
colorInput.value = color;
colorInput.style.backgroundColor = color;
const luminance = getLuminance(color);
colorInput.style.color = luminance > 0.5 ? 'black' : 'white';
feedbackMessage.textContent = `Color selected: ${color}`;
feedbackMessage.style.color = "green";
}
});
savedColorsList.appendChild(colorBox);
});
}
function saveColor(colorValue, feedback, list) {
if (colorValue && !savedColors.includes(colorValue)) {
savedColors.push(colorValue);
localStorage.setItem('savedColors', JSON.stringify(savedColors));
displaySavedColors();
feedback.textContent = `Color ${colorValue} saved!`;
feedback.style.color = "green";
} else {
feedback.textContent = "Color is either empty or already saved!";
feedback.style.color = "red";
}
}
function applyColor(colorValue, feedback) {
let decimalValue;
if (colorValue.startsWith("#")) {
decimalValue = hexToDecimal(colorValue);
} else if (colorValue.startsWith("rgb")) {
decimalValue = rgbToDecimal(colorValue);
} else {
feedback.textContent = "Please enter a valid color!";
feedback.style.color = "red";
return;
}
feedback.textContent = `Color applied successfully: ${colorValue}`;
feedback.style.color = "green";
localStorage.setItem("basic_col_1", decimalValue);
if (WSS) {
WSS.send(`42[1,[40,{"1": ${decimalValue}}]]`);
}
}
function generateRandomColor(input, feedback) {
const randomColor = "#" + Math.floor(Math.random() * 16777215).toString(16);
input.value = randomColor;
input.style.backgroundColor = randomColor;
const luminance = getLuminance(randomColor);
input.style.color = luminance > 0.5 ? 'black' : 'white';
feedback.textContent = `Random color generated: ${randomColor}`;
feedback.style.color = "green";
}
function toggleDeletionMode() {
if (deletionMode) {
deletionMode = false;
deleteButton.style.backgroundColor = "rgb(74, 122, 177)";
deleteButton.textContent = "DELETE COLORS";
deleteButton.classList.remove("cancel-delete-hover");
} else {
if (localStorage.getItem("noAskAgain") !== "true") {
createConfirmationPopup(() => {
deletionMode = true;
deleteButton.style.backgroundColor = "rgb(255, 0, 0)";
deleteButton.textContent = "CANCEL DELETE";
deleteButton.classList.add("cancel-delete-hover");
});
} else {
deletionMode = true;
deleteButton.style.backgroundColor = "rgb(255, 0, 0)";
deleteButton.textContent = "CANCEL DELETE";
deleteButton.classList.add("cancel-delete-hover");
}
}
}
const style = document.createElement('style');
style.textContent = `
.cancel-delete-hover:hover {
background-color: #ff171b !important;
}
`;
document.head.appendChild(style);
function confirmDeletion(color) {
const index = savedColors.indexOf(color);
if (index > -1) {
savedColors.splice(index, 1);
localStorage.setItem('savedColors', JSON.stringify(savedColors));
displaySavedColors();
}
}
function createConfirmationPopup(callback) {
const popupContainer = document.createElement("div");
popupContainer.style.cssText = "position: fixed; left: 50%; top: 50%; transform: translate(-50%, -50%); background: rgba(0, 0, 0, 0.8); color: white; padding: 20px; border-radius: 10px; z-index: 1000;";
const message = document.createElement("p");
message.textContent = "Are you sure you want to enable deletion mode?";
const checkbox = document.createElement("input");
checkbox.type = "checkbox";
checkbox.id = "noAskAgain";
const checkboxLabel = document.createElement("label");
checkboxLabel.setAttribute("for", "noAskAgain");
checkboxLabel.textContent = "Don't ask again";
const buttonContainer = document.createElement("div");
buttonContainer.style.display = "flex";
buttonContainer.style.justifyContent = "space-between";
const cancelButton = createButton("CANCEL", "margin-right: 5px; background-color: red;");
const confirmButton = createButton("CONFIRM");
buttonContainer.append(cancelButton, confirmButton);
popupContainer.append(message, checkboxLabel, checkbox, buttonContainer);
document.body.appendChild(popupContainer);
cancelButton.addEventListener("click", () => {
popupContainer.remove();
});
confirmButton.addEventListener("click", () => {
if (checkbox.checked) {
localStorage.setItem("noAskAgain", "true");
}
callback();
popupContainer.remove();
});
}
}
const appearancePageObserver = new MutationObserver(() => {
const appearancePage = document.querySelector('.cosmeticWindow');
if (appearancePage) {
createColorInput();
} else {
closeBoth();
}
});
function checkAppearancePage() {
appearancePageObserver.observe(document.body, { childList: true, subtree: true });
}
function closeColorPicker() {
if (colorPickerContainer) {
colorPickerContainer.remove();
colorPickerContainer = null;
}
}
function closeBoth() {
closeColorPicker();
const cosmeticWindow = document.querySelector('.cosmeticWindow');
if (cosmeticWindow) {
cosmeticWindow.remove();
}
const blockerContainer = document.querySelector('.blockerContainer');
if (blockerContainer) {
blockerContainer.remove();
}
}
checkAppearancePage();