Gartic.io Custom Background

This userscript let you customize your own gartic.io background

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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);
        }
    });

})();