Gartic.io Custom Background

This userscript let you customize your own gartic.io background

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Gartic.io Custom Background
// @namespace    http://tampermonkey.net/
// @version      1.25
// @description  This userscript let you customize your own gartic.io background
// @author       arcticrevurne
// @icon         https://em-content.zobj.net/source/twitter/408/fox_1f98a.png
// @match        *://*.gartic.io/*
// @license      MIT
// @noframes
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const defaultBackgroundColor = '#241500';
    const oldDefaultOrange = '#FF9800';

    function injectCustomCss() {
        const style = document.createElement('style');
        style.id = 'gartic-custom-styles';
        style.textContent = `
            #background::before {
                content: none !important;
                background-image: none !important;
                display: none !important;
                opacity: 0 !important;
            }
            body {
                background-color: ${defaultBackgroundColor} !important;
            }
        `;
        document.head.appendChild(style);
    }

    function applyCustomBackground(value) {
        const body = document.querySelector('body');
        if (body) {
            if (value.startsWith('http://') || value.startsWith('https://') || value.startsWith('data:')) {
                body.style.backgroundImage = `url('${value}')`;
                body.style.backgroundSize = 'cover';
                body.style.backgroundRepeat = 'no-repeat';
                body.style.backgroundAttachment = 'fixed';
                body.style.backgroundPosition = 'center center';
                body.style.removeProperty('background-color');
            } else {
                body.style.backgroundImage = 'none';
                body.style.setProperty('background-color', value, 'important');
            }
        }
    }

    function resetBackground() {
        const body = document.querySelector('body');
        if (body) {
            body.style.backgroundImage = '';
            body.style.removeProperty('background-color');
        }
        localStorage.removeItem('gartic_custom_background');
    }

    function showNotification(message, isError = false) {
        const notificationArea = document.getElementById('gartic-bg-notification');
        if (!notificationArea) return;

        notificationArea.textContent = message;
        notificationArea.style.opacity = '1';
        notificationArea.style.backgroundColor = isError ? '#FF5722' : '#4CAF50';
        notificationArea.style.color = '#FFFFFF';
        notificationArea.style.padding = '8px';
        notificationArea.style.borderRadius = '4px';
        notificationArea.style.marginBottom = '10px';
        notificationArea.style.textAlign = 'center';

        setTimeout(() => {
            notificationArea.style.opacity = '0';
            notificationArea.style.padding = '0';
            notificationArea.style.marginBottom = '0';
        }, 3000);
    }

    function createSettingsMenu() {
        if (document.getElementById('gartic-custom-bg-menu')) {
            return;
        }

        const menuContainer = document.createElement('div');
        menuContainer.id = 'gartic-custom-bg-menu';
        menuContainer.style.position = 'fixed';
        menuContainer.style.backgroundColor = '#FFFFFF';
        menuContainer.style.border = '1px solid #FF9800';
        menuContainer.style.borderRadius = '6px';
        menuContainer.style.padding = '18px';
        menuContainer.style.zIndex = '99999';
        menuContainer.style.display = 'none';
        menuContainer.style.boxShadow = '0 4px 10px rgba(0, 0, 0, 0.15)';
        menuContainer.style.color = '#444444';
        menuContainer.style.fontFamily = 'Arial, sans-serif';
        menuContainer.style.maxWidth = '300px';
        menuContainer.style.width = '90%'; // Tetap pakai ini agar responsif di layar kecil
        menuContainer.style.boxSizing = 'border-box'; // Penting untuk padding dan border tidak menambah lebar

        const menuTitle = document.createElement('h3');
        menuTitle.textContent = 'Customise Background';
        menuTitle.style.marginBottom = '18px';
        menuTitle.style.color = '#FB8C00';
        menuTitle.style.textAlign = 'center';
        menuTitle.style.fontSize = '1.1em';
        menuTitle.style.fontWeight = '600';
        menuContainer.appendChild(menuTitle);

        const notificationArea = document.createElement('div');
        notificationArea.id = 'gartic-bg-notification';
        notificationArea.style.opacity = '0';
        notificationArea.style.transition = 'opacity 0.3s ease-in-out, padding 0.3s ease-in-out, margin-bottom 0.3s ease-in-out';
        notificationArea.style.overflow = 'hidden';
        notificationArea.style.maxHeight = '50px';
        menuContainer.appendChild(notificationArea);

        const fileInputLabel = document.createElement('label');
        fileInputLabel.textContent = 'Select Image File:';
        fileInputLabel.style.display = 'block';
        fileInputLabel.style.marginBottom = '6px';
        fileInputLabel.style.fontWeight = 'bold';
        fileInputLabel.style.fontSize = '0.9em';
        menuContainer.appendChild(fileInputLabel);

        const fileInput = document.createElement('input');
        fileInput.type = 'file';
        fileInput.accept = 'image/jpeg, image/png, image/gif';
        fileInput.style.width = '100%'; // Atur lebar ke 100% dari parent (menuContainer)
        fileInput.style.padding = '8px 0';
        fileInput.style.marginBottom = '15px';
        fileInput.style.backgroundColor = 'transparent';
        fileInput.style.border = 'none';
        fileInput.style.color = '#444444';
        fileInput.style.cursor = 'pointer';
        fileInput.style.fontSize = '0.9em';
        fileInput.style.boxSizing = 'border-box'; // Pastikan padding tidak membuat lebar melebihi 100%
        menuContainer.appendChild(fileInput);

        const divider = document.createElement('hr');
        divider.style.borderColor = '#FFCC80';
        divider.style.margin = '18px 0';
        menuContainer.appendChild(divider);

        const inputUrlLabel = document.createElement('label');
        inputUrlLabel.textContent = 'Or Enter Image URL or Hex Colour:';
        inputUrlLabel.style.display = 'block';
        inputUrlLabel.style.marginBottom = '6px';
        inputUrlLabel.style.fontWeight = 'bold';
        inputUrlLabel.style.fontSize = '0.9em';
        menuContainer.appendChild(inputUrlLabel);

        const imageUrlInput = document.createElement('input');
        imageUrlInput.type = 'text';
        imageUrlInput.placeholder = 'e.g., #FF9800 or https://example.com/image.jpg';
        imageUrlInput.style.width = '100%'; // Atur lebar ke 100% dari parent (menuContainer)
        imageUrlInput.style.padding = '9px';
        imageUrlInput.style.marginBottom = '20px';
        imageUrlInput.style.backgroundColor = '#FFF3E0';
        imageUrlInput.style.border = '1px solid #FFB74D';
        imageUrlInput.style.borderRadius = '4px';
        imageUrlInput.style.color = '#444444';
        imageUrlInput.style.fontSize = '0.9em';
        imageUrlInput.style.boxSizing = 'border-box'; // Pastikan padding dan border tidak membuat lebar melebihi 100%
        menuContainer.appendChild(imageUrlInput);

        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.justifyContent = 'space-between';
        buttonContainer.style.gap = '10px';
        menuContainer.appendChild(buttonContainer);

        const applyButton = document.createElement('button');
        applyButton.textContent = 'Apply';
        applyButton.style.flexGrow = '1';
        applyButton.style.backgroundColor = '#FF9800';
        applyButton.style.color = '#FFFFFF';
        applyButton.style.border = 'none';
        applyButton.style.padding = '10px 15px';
        applyButton.style.borderRadius = '5px';
        applyButton.style.cursor = 'pointer';
        applyButton.style.fontWeight = 'bold';
        applyButton.style.fontSize = '0.9em';
        applyButton.style.transition = 'background-color 0.2s ease, transform 0.1s ease, box-shadow 0.2s ease';
        applyButton.onmouseover = () => { applyButton.style.backgroundColor = '#F57C00'; applyButton.style.boxShadow = '0 2px 6px rgba(0,0,0,0.2)'; };
        applyButton.onmouseout = () => { applyButton.style.backgroundColor = '#FF9800'; applyButton.style.boxShadow = 'none'; };
        applyButton.onmousedown = () => applyButton.style.transform = 'scale(0.98)';
        applyButton.onmouseup = () => applyButton.style.transform = 'scale(1)';

        applyButton.onclick = () => {
            if (fileInput.files.length > 0) {
                const file = fileInput.files[0];
                const reader = new FileReader();
                reader.onloadend = function() {
                    const dataUrl = reader.result;
                    applyCustomBackground(dataUrl);
                    localStorage.setItem('gartic_custom_background', dataUrl);
                    showNotification('Background from file applied successfully!');
                };
                reader.onerror = function() {
                    showNotification('Failed to read file.', true);
                };
                reader.readAsDataURL(file);
            } else {
                const url = imageUrlInput.value.trim();
                if (url) {
                    applyCustomBackground(url);
                    localStorage.setItem('gartic_custom_background', url);
                    showNotification('Background from URL or colour applied successfully!');
                } else {
                    showNotification('Please select an image file or enter an image URL or hex colour.', true);
                }
            }
            fileInput.value = '';
            imageUrlInput.value = '';
        };
        buttonContainer.appendChild(applyButton);

        const resetButton = document.createElement('button');
        resetButton.textContent = 'Reset';
        resetButton.style.flexGrow = '1';
        resetButton.style.backgroundColor = '#FFB74D';
        resetButton.style.color = '#444444';
        resetButton.style.border = 'none';
        resetButton.style.padding = '10px 15px';
        resetButton.style.borderRadius = '5px';
        resetButton.style.cursor = 'pointer';
        resetButton.style.fontWeight = 'bold';
        resetButton.style.fontSize = '0.9em';
        resetButton.style.transition = 'background-color 0.2s ease, transform 0.1s ease, box-shadow 0.2s ease';
        resetButton.onmouseover = () => { resetButton.style.backgroundColor = '#FFA726'; resetButton.style.boxShadow = '0 2px 6px rgba(0,0,0,0.15)'; };
        resetButton.onmouseout = () => { resetButton.style.backgroundColor = '#FFB74D'; resetButton.style.boxShadow = 'none'; };
        resetButton.onmousedown = () => resetButton.style.transform = 'scale(0.98)';
        resetButton.onmouseup = () => resetButton.style.transform = 'scale(1)';

        resetButton.onclick = () => {
            resetBackground();
            imageUrlInput.value = '';
            fileInput.value = '';
            showNotification('Background has been reset to default.');
        };
        buttonContainer.appendChild(resetButton);

        document.body.appendChild(menuContainer);

        const menuToggleButton = document.createElement('button');
        menuToggleButton.textContent = '🖼️';
        menuToggleButton.id = 'gartic-custom-bg-toggle';
        menuToggleButton.classList.add('gartic-userscript-button-left');

        menuToggleButton.style.position = 'fixed';
        menuToggleButton.style.left = '15px';
        menuToggleButton.style.top = '15px';
        menuToggleButton.style.backgroundColor = '#FF9800';
        menuToggleButton.style.color = '#FFFFFF';
        menuToggleButton.style.border = 'none';
        menuToggleButton.style.padding = '8px 14px';
        menuToggleButton.style.borderRadius = '5px';
        menuToggleButton.style.cursor = 'pointer';
        menuToggleButton.style.zIndex = '100000';
        menuToggleButton.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.15)';
        menuToggleButton.style.fontWeight = 'bold';
        menuToggleButton.style.fontSize = '0.9em';
        menuToggleButton.style.transition = 'background-color 0.2s ease, transform 0.1s ease, box-shadow 0.2s ease';
        menuToggleButton.onmouseover = () => { menuToggleButton.style.backgroundColor = '#F57C00'; menuToggleButton.style.boxShadow = '0 3px 7px rgba(0,0,0,0.2)'; };
        menuToggleButton.onmouseout = () => { menuToggleButton.style.backgroundColor = '#FF9800'; menuToggleButton.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.15)'; };
        menuToggleButton.onmousedown = () => menuToggleButton.style.transform = 'scale(0.98)';
        menuToggleButton.onmouseup = () => menuToggleButton.style.transform = 'scale(1)';

        menuToggleButton.onclick = (event) => {
            event.stopPropagation();
            if (menuContainer.style.display === 'none') {
                menuContainer.style.display = 'block';
                const toggleRect = menuToggleButton.getBoundingClientRect();
                menuContainer.style.left = `${toggleRect.right + 15}px`;
                menuContainer.style.top = `${toggleRect.top}px`;

                if (parseFloat(menuContainer.style.top) < 5) {
                    menuContainer.style.top = '5px';
                }
                if (parseFloat(menuContainer.style.top) + menuContainer.offsetHeight > window.innerHeight - 5) {
                    menuContainer.style.top = `${window.innerHeight - menuContainer.offsetHeight - 5}px`;
                }

                imageUrlInput.value = '';
                fileInput.value = '';
                notificationArea.style.opacity = '0';
                notificationArea.style.padding = '0';
                notificationArea.style.marginBottom = '0';
            } else {
                menuContainer.style.display = 'none';
            }
        };
        document.body.appendChild(menuToggleButton);

        document.addEventListener('click', (event) => {
            if (menuContainer.style.display === 'block' &&
                !menuContainer.contains(event.target) &&
                !menuToggleButton.contains(event.target)) {
                menuContainer.style.display = 'none';
            }
        });
    }

    // Fungsi ini akan menerapkan gaya awal dan menangani background yang tersimpan.
    // CSS bawaan untuk menu userscript kita akan diatur secara inline di createSettingsMenu
    // untuk memastikan tidak ada konflik dengan theme selector.
    injectCustomCss();

    let savedBackground = localStorage.getItem('gartic_custom_background');
    if (!savedBackground || savedBackground === oldDefaultOrange) {
        applyCustomBackground(defaultBackgroundColor);
        localStorage.setItem('gartic_custom_background', defaultBackgroundColor);
    } else {
        applyCustomBackground(savedBackground);
    }

    const observer = new MutationObserver((mutationsList, observer) => {
        const garticCoreElement = document.querySelector('.main-menu') || document.querySelector('.game-container') || document.body;

        if (garticCoreElement) {
            createSettingsMenu();
            observer.disconnect();
        } else if (document.body && !document.getElementById('gartic-custom-bg-menu')) {
            setTimeout(() => {
                if (!document.getElementById('gartic-custom-bg-menu')) {
                    createSettingsMenu();
                }
            }, 1000);
            observer.disconnect();
        }
    });

    observer.observe(document.body, { childList: true, subtree: true });

    document.addEventListener('DOMContentLoaded', () => {
        if (!document.getElementById('gartic-custom-bg-menu')) {
            setTimeout(createSettingsMenu, 1500);
        }
    });

})();