Kawaii Anime Helper & Drawing Bot for Gartic.io

Helper for Gartic.io with auto-guess, drawing assistance, and drawing bot

Устаревшая версия за 11.03.2025. Перейдите к последней версии.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         Kawaii Anime Helper & Drawing Bot for Gartic.io
// @namespace    http://tampermonkey.net/
// @version      2025-03-11
// @description  Helper for Gartic.io with auto-guess, drawing assistance, and drawing bot
// @author       anonimbiri & Gartic-Developers
// @license      MIT
// @match        https://gartic.io/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=gartic.io
// @run-at       document-start
// @grant        none
// ==/UserScript==

// I used the word list from 'https://github.com/Gartic-Developers/Gartic-WordList/'.
// Thanks to Gartic Developers for providing this resource. Also, thanks to Qwyua!

(function() {
    'use strict';

    // Script interception (unchanged)
    Node.prototype.appendChild = new Proxy(Node.prototype.appendChild, {
        apply: function(target, thisArg, argumentsList) {
            const node = argumentsList[0];
            if (node.nodeName.toLowerCase() === 'script' && node.src && node.src.includes('room')) {
                console.log('Target script detected:', node.src);
                fetch(node.src)
                    .then(response => response.text())
                    .then(scriptContent => {
                    let modifiedContent = scriptContent
                    .replace(
                        'r.created||c?Rt("input",{type:"text",name:"chat",className:"mousetrap",autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",value:i,placeholder:this._lang.chatHere,maxLength:100,enterKeyHint:"send",onChange:this.handleText,ref:this._ref}):Rt("input",{type:"text",name:"chat",className:"mousetrap",autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",value:this._lang.loginChat,maxLength:100,ref:this._ref,disabled:!0})',
                        'Rt("input",{type:"text",name:"chat",className:"mousetrap",autoComplete:"off",autoCorrect:"off",autoCapitalize:"off",value:i,placeholder:this._lang.chatHere,maxLength:100,enterKeyHint:"send",onChange:this.handleText,ref:this._ref})'
                    )
                    .replace(
                        'this._timerAtivo=setInterval((function(){Date.now()-e._ativo>15e4&&(O(Object(f.a)(n.prototype),"emit",e).call(e,"avisoInativo"),e._ativo=Date.now())}),1e3)',
                        'this._timerAtivo=setInterval((function(){Date.now()-e._ativo>15e4&&e.active()}),1e3)'
                    )
                    .replace(
                        'e.unlock()}',
                        'e.unlock();window.game=e;setInterval(()=>{window.game=e},1000);e.on("votekick",(t,i,o)=>{if(i.id===e.me.id){e.votekick(t.id,true);}});}'
                    );
                    let blob = new Blob([modifiedContent], { type: 'application/javascript' });
                    let blobUrl = URL.createObjectURL(blob);
                    node.src = blobUrl;
                    node.textContent = '';
                    return target.apply(thisArg, [node]);
                })
                    .catch(error => console.error('Failed to fetch/modify script:', error));
                return node;
            }
            return target.apply(thisArg, argumentsList);
        }
    });

    // Load fonts
    const fontLink = document.createElement('link');
    fontLink.rel = 'stylesheet';
    fontLink.href = 'https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@400;700&display=swap';
    document.head.appendChild(fontLink);

    // Inject HTML
    const kawaiiHTML = `
        <div class="kawaii-cheat" id="kawaiiCheat">
            <div class="kawaii-header" id="kawaiiHeader">
                <img src="https://i.imgur.com/8z3K9qN.png" alt="Anime Girl" class="header-icon">
                <h2>✧ Kawaii Helper ✧</h2>
                <button class="minimize-btn" id="minimizeBtn">▼</button>
            </div>
            <div class="kawaii-body" id="kawaiiBody">
                <div class="kawaii-tabs">
                    <button class="kawaii-tab active" data-tab="guessing">Guessing</button>
                    <button class="kawaii-tab" data-tab="drawing">Drawing</button>
                </div>
                <div class="kawaii-content" id="guessing-tab">
                    <div class="checkbox-container">
                        <input type="checkbox" id="autoGuess">
                        <label for="autoGuess">Auto Guess</label>
                    </div>
                    <div class="slider-container" id="speedContainer" style="display: none;">
                        <div class="slider-label">Speed</div>
                        <div class="custom-slider">
                            <input type="range" id="guessSpeed" min="100" max="5000" value="1000" step="100">
                            <div class="slider-track"></div>
                            <span id="speedValue">1s</span>
                        </div>
                    </div>
                    <div class="checkbox-container">
                        <input type="checkbox" id="customWords">
                        <label for="customWords">Custom Words</label>
                    </div>
                    <div class="dropzone-container" id="wordListContainer" style="display: none;">
                        <div class="dropzone" id="wordListDropzone">
                            <input type="file" id="wordList" accept=".txt">
                            <div class="dropzone-content">
                                <div class="dropzone-icon">❀</div>
                                <p>Drop word list here or click to upload</p>
                            </div>
                        </div>
                    </div>
                    <div class="input-container">
                        <input type="text" id="guessPattern" placeholder="Enter pattern (e.g., ___e___)">
                    </div>
                    <div class="hit-list" id="hitList">
                        <div class="message">Type a pattern to see matches ✧</div>
                    </div>
                </div>
                <div class="kawaii-content" id="drawing-tab" style="display: none;">
                    <div class="dropzone-container">
                        <div class="dropzone" id="imageDropzone">
                            <input type="file" id="imageUpload" accept="image/*">
                            <div class="dropzone-content">
                                <div class="dropzone-icon">✎</div>
                                <p>Drop image here or click to upload</p>
                            </div>
                        </div>
                        <div class="image-preview" id="imagePreview" style="display: none;">
                            <img id="previewImg">
                            <div class="preview-controls">
                                <button class="cancel-btn" id="cancelImage">✕</button>
                            </div>
                        </div>
                    </div>
                    <div class="slider-container">
                        <div class="slider-label">Draw Speed</div>
                        <div class="custom-slider">
                            <input type="range" id="drawSpeed" min="1" max="50" value="20" step="1">
                            <div class="slider-track"></div>
                            <span id="drawSpeedValue">20</span>
                        </div>
                    </div>
                    <div class="slider-container">
                        <div class="slider-label">Max Colors</div>
                        <div class="custom-slider">
                            <input type="range" id="maxColors" min="3" max="100" value="20" step="1">
                            <div class="slider-track"></div>
                            <span id="maxColorsValue">20</span>
                        </div>
                    </div>
                    <button class="draw-btn" id="sendDraw" disabled>Draw Now ✧</button>
                </div>
                <div class="kawaii-footer">
                    <span class="credit-text">Made with ♥ by Anonimbiri & Gartic-Developers</span>
                </div>
            </div>
        </div>
    `;

    const waitForBody = setInterval(() => {
        if (document.body) {
            clearInterval(waitForBody);
            document.body.insertAdjacentHTML('beforeend', kawaiiHTML);
            addStylesAndBehavior();
        }
    }, 100);

    function addStylesAndBehavior() {
        const style = document.createElement('style');
        style.textContent = `
            :root {
                --primary-color: #FF69B4;
                --primary-dark: #FF1493;
                --primary-light: #FFC0CB;
                --bg-color: #FFB6C1;
                --text-color: #5d004f;
                --panel-bg: rgba(255, 182, 193, 0.95);
                --panel-border: #FF69B4;
                --element-bg: rgba(255, 240, 245, 0.7);
                --element-hover: rgba(255, 240, 245, 0.9);
                --element-active: #FF69B4;
                --element-active-text: #FFF0F5;
            }

            .kawaii-cheat {
                position: fixed;
                top: 20px;
                right: 20px;
                width: 280px;
                background: var(--panel-bg);
                border-radius: 15px;
                box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
                padding: 10px;
                display: flex;
                flex-direction: column;
                gap: 10px;
                color: var(--text-color);
                user-select: none;
                z-index: 1000;
                font-family: 'M PLUS Rounded 1c', sans-serif;
                border: 2px solid var(--panel-border);
                transition: all 0.4s ease-in-out;
                max-height: calc(100vh - 40px);
                overflow: hidden;
            }

            .kawaii-cheat.minimized {
                height: 50px;
                opacity: 0.9;
                transform: scale(0.95);
                overflow: hidden;
            }

            .kawaii-cheat:not(.minimized) {
                opacity: 1;
                transform: scale(1);
            }

            .kawaii-cheat.minimized .kawaii-body {
                opacity: 0;
                max-height: 0;
                overflow: hidden;
                transition: opacity 0.2s ease-in-out, max-height 0.4s ease-in-out;
            }

            .kawaii-cheat:not(.minimized) .kawaii-body {
                opacity: 1;
                max-height: 500px;
                transition: opacity 0.2s ease-in-out 0.2s, max-height 0.4s ease-in-out;
            }

            .kawaii-cheat.dragging {
                opacity: 0.8;
                transition: none; /* Sürükleme sırasında animasyonu devre dışı bırak */
            }

            .kawaii-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                padding: 5px 10px;
                cursor: move;
                background: var(--element-bg);
                border-radius: 10px;
                border: 2px solid var(--primary-color);
            }

            .header-icon {
                width: 30px;
                height: 30px;
                border-radius: 50%;
                margin-right: 10px;
                border: 1px dashed var(--primary-color);
            }

            .kawaii-header h2 {
                margin: 0;
                font-size: 18px;
                font-weight: 700;
                color: var(--primary-dark);
                text-shadow: 1px 1px 2px var(--primary-light);
            }

            .minimize-btn {
                background: transparent;
                border: 2px solid var(--primary-dark);
                border-radius: 6px;
                width: 24px;
                height: 24px;
                color: var(--primary-dark);
                font-size: 16px;
                line-height: 20px;
                text-align: center;
                cursor: pointer;
                transition: all 0.3s ease;
            }

            .minimize-btn:hover {
                background: var(--primary-color);
                color: var(--element-active-text);
                border-color: var(--primary-color);
                transform: rotate(180deg);
            }

            .kawaii-tabs {
                display: flex;
                gap: 8px;
                padding: 5px 0;
            }

            .kawaii-tab {
                flex: 1;
                background: var(--element-bg);
                border: 1px dashed var(--primary-color);
                padding: 6px;
                border-radius: 10px;
                font-size: 12px;
                font-weight: 700;
                color: var(--text-color);
                cursor: pointer;
                transition: background 0.3s ease, transform 0.3s ease;
                text-align: center;
            }

            .kawaii-tab.active {
                background: var(--primary-color);
                color: var(--element-active-text);
                border-color: var(--primary-dark);
            }

            .kawaii-tab:hover:not(.active) {
                background: var(--element-hover);
                transform: scale(1.05);
            }

            .kawaii-content {
                display: flex;
                flex-direction: column;
                gap: 10px;
                max-height: 55vh;
                overflow-y: auto;
                padding: 5px;
            }

            .checkbox-container {
                display: flex;
                align-items: center;
                gap: 8px;
                background: var(--element-bg);
                padding: 8px;
                border-radius: 10px;
                border: 1px dashed var(--primary-color);
                cursor: pointer;
                transition: background 0.3s ease;
            }

            .checkbox-container:hover {
                background: var(--element-hover);
            }

            .checkbox-container input[type="checkbox"] {
                appearance: none;
                width: 18px;
                height: 18px;
                background: var(--element-active-text);
                border: 1px dashed var(--primary-color);
                border-radius: 50%;
                cursor: pointer;
                position: relative;
            }

            .checkbox-container input[type="checkbox"]:checked {
                background: var(--primary-color);
                border-color: var(--primary-dark);
            }

            .checkbox-container input[type="checkbox"]:checked::after {
                content: "♥";
                position: absolute;
                top: 50%;
                left: 50%;
                transform: translate(-50%, -50%);
                color: var(--element-active-text);
                font-size: 12px;
            }

            .checkbox-container label {
                font-size: 12px;
                font-weight: 700;
                color: var(--text-color);
                cursor: pointer;
            }

            .input-container {
                background: var(--element-bg);
                padding: 8px;
                border-radius: 10px;
                border: 1px dashed var(--primary-color);
            }

            .input-container input[type="text"] {
                width: 100%;
                background: var(--element-active-text);
                border: 1px dashed var(--primary-light);
                border-radius: 8px;
                padding: 6px 10px;
                color: var(--text-color);
                font-size: 12px;
                font-weight: 500;
                box-sizing: border-box;
                transition: border-color 0.3s ease;
                outline: none;
            }

            .input-container input[type="text"]:focus {
                border-color: var(--primary-dark);
            }

            .dropzone-container {
                display: flex;
                flex-direction: column;
                gap: 10px;
            }

            .dropzone {
                position: relative;
                background: var(--element-bg);
                border: 1px dashed var(--primary-color);
                border-radius: 10px;
                padding: 15px;
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                cursor: pointer;
                transition: background 0.3s ease, border-color 0.3s ease;
                min-height: 80px;
            }

            .dropzone:hover, .dropzone.drag-over {
                background: var(--element-hover);
                border-color: var(--primary-dark);
            }

            .dropzone input[type="file"] {
                position: absolute;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                opacity: 0;
                cursor: pointer;
            }

            .dropzone-content {
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 8px;
                text-align: center;
                pointer-events: none;
            }

            .dropzone-icon {
                font-size: 24px;
                color: var(--primary-color);
                animation: pulse 1.5s infinite ease-in-out;
            }

            @keyframes pulse {
                0%, 100% { transform: scale(1); }
                50% { transform: scale(1.1); }
            }

            .dropzone-content p {
                margin: 0;
                color: var(--text-color);
                font-size: 12px;
                font-weight: 500;
            }

            .slider-container {
                display: flex;
                flex-direction: column;
                gap: 6px;
                background: var(--element-bg);
                padding: 8px;
                border-radius: 10px;
                border: 1px dashed var(--primary-color);
            }

            .slider-label {
                font-size: 12px;
                color: var(--text-color);
                font-weight: 700;
                text-align: center;
            }

            .custom-slider {
                position: relative;
                height: 25px;
                padding: 0 8px;
            }

            .custom-slider input[type="range"] {
                -webkit-appearance: none;
                width: 100%;
                height: 6px;
                background: transparent;
                position: absolute;
                top: 50%;
                left: 0;
                transform: translateY(-50%);
                z-index: 2;
            }

            .custom-slider .slider-track {
                position: absolute;
                top: 50%;
                left: 0;
                width: 100%;
                height: 6px;
                background: linear-gradient(to right, var(--primary-dark) 0%, var(--primary-dark) var(--slider-progress), var(--primary-light) var(--slider-progress), var(--primary-light) 100%);
                border-radius: 3px;
                transform: translateY(-50%);
                z-index: 1;
            }

            .custom-slider input[type="range"]::-webkit-slider-thumb {
                -webkit-appearance: none;
                width: 16px;
                height: 16px;
                background: var(--primary-color);
                border-radius: 50%;
                border: 1px dashed var(--element-active-text);
                cursor: pointer;
                transition: transform 0.3s ease;
            }

            .custom-slider input[type="range"]::-webkit-slider-thumb:hover {
                transform: scale(1.2);
            }

            .custom-slider span {
                position: absolute;
                bottom: -15px;
                left: 50%;
                transform: translateX(-50%);
                font-size: 10px;
                color: var(--text-color);
                background: var(--element-active-text);
                padding: 2px 6px;
                border-radius: 8px;
                border: 1px dashed var(--primary-color);
                white-space: nowrap;
            }

            .hit-list {
                max-height: 180px;
                overflow-y: scroll;
                background: var(--element-bg);
                border: 1px dashed var(--primary-color);
                border-radius: 10px;
                padding: 8px;
                display: flex;
                flex-direction: column;
                gap: 6px;
                scrollbar-width: thin;
                scrollbar-color: var(--primary-color) var(--element-bg);
            }

            .hit-list::-webkit-scrollbar {
                width: 6px;
            }

            .hit-list::-webkit-scrollbar-thumb {
                background-color: var(--primary-color);
                border-radius: 10px;
            }

            .hit-list::-webkit-scrollbar-track {
                background: var(--element-bg);
            }

            .hit-list button {
                background: rgba(255, 240, 245, 0.8);
                border: 1px dashed var(--primary-color);
                padding: 6px 10px;
                border-radius: 8px;
                color: var(--text-color);
                font-size: 12px;
                font-weight: 700;
                cursor: pointer;
                transition: background 0.3s ease, transform 0.3s ease;
                text-align: left;
            }

            .hit-list button:hover:not(.tried) {
                background: var(--primary-color);
                color: var(--element-active-text);
                transform: scale(1.03);
            }

            .hit-list button.tried {
                background: rgba(255, 182, 193, 0.6);
                border-color: var(--primary-light);
                color: var(--primary-dark);
                opacity: 0.7;
                cursor: not-allowed;
            }

            .hit-list .tried-label {
                font-size: 10px;
                color: var(--primary-dark);
                text-align: center;
                padding: 4px;
                background: var(--element-active-text);
                border-radius: 8px;
                border: 1px dashed var(--primary-color);
            }

            .hit-list .message {
                font-size: 12px;
                color: var(--text-color);
                text-align: center;
                padding: 8px;
            }

            .image-preview {
                position: relative;
                margin-top: 10px;
                background: var(--element-bg);
                padding: 8px;
                border-radius: 10px;
                border: 1px dashed var(--primary-color);
            }

            .image-preview img {
                max-width: 100%;
                max-height: 120px;
                border-radius: 8px;
                display: block;
                margin: 0 auto;
            }

            .preview-controls {
                position: absolute;
                top: 12px;
                right: 12px;
                display: flex;
                gap: 6px;
            }

            .cancel-btn {
                background: transparent;
                border: 2px solid var(--primary-dark);
                border-radius: 6px;
                width: 24px;
                height: 24px;
                color: var(--primary-dark);
                font-size: 16px;
                line-height: 20px;
                text-align: center;
                cursor: pointer;
                transition: all 0.3s ease;
            }

            .cancel-btn:hover {
                background: var(--primary-dark);
                color: var(--element-active-text);
                transform: scale(1.1);
            }

            .draw-btn {
                background: var(--primary-color);
                border: 1px dashed var(--primary-dark);
                padding: 8px;
                border-radius: 10px;
                color: var(--element-active-text);
                font-size: 14px;
                font-weight: 700;
                cursor: pointer;
                transition: background 0.3s ease, transform 0.3s ease;
                text-align: center;
            }

            .draw-btn:hover:not(:disabled) {
                background: var(--primary-dark);
                transform: scale(1.05);
            }

            .draw-btn:disabled {
                background: rgba(255, 105, 180, 0.5);
                cursor: not-allowed;
            }

            .kawaii-footer {
                display: flex;
                justify-content: center;
                align-items: center;
                margin-top: 10px;
                padding: 6px;
                background: var(--element-bg);
                border-radius: 10px;
                border: 2px solid var(--primary-color);
            }

            .credit-text {
                font-size: 10px;
                color: var(--text-color);
                font-weight: 700;
            }
        `;
        document.head.appendChild(style);

        // DOM Elements
        const kawaiiCheat = document.getElementById('kawaiiCheat');
        const kawaiiHeader = document.getElementById('kawaiiHeader');
        const minimizeBtn = document.getElementById('minimizeBtn');
        const tabButtons = document.querySelectorAll('.kawaii-tab');
        const tabContents = document.querySelectorAll('.kawaii-content');
        const autoGuessCheckbox = document.getElementById('autoGuess');
        const speedContainer = document.getElementById('speedContainer');
        const guessSpeed = document.getElementById('guessSpeed');
        const speedValue = document.getElementById('speedValue');
        const customWordsCheckbox = document.getElementById('customWords');
        const wordListContainer = document.getElementById('wordListContainer');
        const wordListDropzone = document.getElementById('wordListDropzone');
        const wordListInput = document.getElementById('wordList');
        const guessPattern = document.getElementById('guessPattern');
        const hitList = document.getElementById('hitList');
        const imageDropzone = document.getElementById('imageDropzone');
        const imageUpload = document.getElementById('imageUpload');
        const imagePreview = document.getElementById('imagePreview');
        const previewImg = document.getElementById('previewImg');
        const cancelImage = document.getElementById('cancelImage');
        const drawSpeed = document.getElementById('drawSpeed');
        const drawSpeedValue = document.getElementById('drawSpeedValue');
        const maxColors = document.getElementById('maxColors');
        const maxColorsValue = document.getElementById('maxColorsValue');
        const sendDraw = document.getElementById('sendDraw');

        // Variables
        let isDragging = false;
        let initialX, initialY;
        let xOffset = 0, yOffset = 0;
        let rafId = null; // requestAnimationFrame ID
        let autoGuessInterval = null;
        let wordList = { "Custom": [] };
        let triedLabelAdded = false;

        const wordListURLs = {
            "General (en)": "https://cdn.jsdelivr.net/gh/Gartic-Developers/Gartic-WordList@master/languages/English/general.json",
            "General (tr)": "https://cdn.jsdelivr.net/gh/Gartic-Developers/Gartic-WordList@master/languages/Turkish/general.json"
        };

        // Utility Functions
        function updateSliderTrack(slider) {
            const min = parseInt(slider.min);
            const max = parseInt(slider.max);
            const value = parseInt(slider.value);
            const progress = ((value - min) / (max - min)) * 100;
            slider.parentElement.querySelector('.slider-track').style.setProperty('--slider-progress', `${progress}%`);
        }

        function preventDefaults(e) {
            e.preventDefault();
            e.stopPropagation();
        }

        // Initial Setup
        updateSliderTrack(guessSpeed);
        updateSliderTrack(drawSpeed);
        updateSliderTrack(maxColors);

        // Dragging Functionality with Optimization
        kawaiiHeader.addEventListener('mousedown', (e) => {
            if (e.target !== minimizeBtn) {
                initialX = e.clientX - xOffset;
                initialY = e.clientY - yOffset;
                isDragging = true;
                kawaiiCheat.classList.add('dragging');
                if (rafId) cancelAnimationFrame(rafId); // Önceki frame'i iptal et
            }
        });

        document.addEventListener('mousemove', (e) => {
            if (isDragging) {
                e.preventDefault();
                const newX = e.clientX - initialX;
                const newY = e.clientY - initialY;

                if (rafId) cancelAnimationFrame(rafId); // Tekrarlanan frame'leri önle
                rafId = requestAnimationFrame(() => {
                    kawaiiCheat.style.transform = `translate(${newX}px, ${newY}px)`;
                    xOffset = newX;
                    yOffset = newY;
                });
            }
        });

        document.addEventListener('mouseup', () => {
            if (isDragging) {
                isDragging = false;
                kawaiiCheat.classList.remove('dragging');
                if (rafId) cancelAnimationFrame(rafId); // Son frame'i temizle
            }
        });

        // Minimize Button
        minimizeBtn.addEventListener('click', () => {
            kawaiiCheat.classList.toggle('minimized');
            minimizeBtn.textContent = kawaiiCheat.classList.contains('minimized') ? '▲' : '▼';
        });

        // Tab Switching
        tabButtons.forEach(btn => {
            btn.addEventListener('click', () => {
                tabButtons.forEach(b => b.classList.remove('active'));
                tabContents.forEach(c => c.style.display = 'none');
                btn.classList.add('active');
                document.getElementById(`${btn.dataset.tab}-tab`).style.display = 'flex';
            });
        });

        // Checkbox Container Click
        document.querySelectorAll('.checkbox-container').forEach(container => {
            container.addEventListener('click', (e) => {
                const checkbox = container.querySelector('input[type="checkbox"]');
                if (e.target !== checkbox) {
                    checkbox.checked = !checkbox.checked;
                    checkbox.dispatchEvent(new Event('change'));
                }
            });
        });

        // Auto Guess Checkbox
        autoGuessCheckbox.addEventListener('change', (e) => {
            speedContainer.style.display = e.target.checked ? 'flex' : 'none';
            if (!e.target.checked) stopAutoGuess();
            else if (guessPattern.value) startAutoGuess();
        });

        // Guess Speed Slider
        guessSpeed.addEventListener('input', (e) => {
            updateSliderTrack(e.target);
            speedValue.textContent = e.target.value >= 1000 ? `${e.target.value / 1000}s` : `${e.target.value}ms`;
            if (autoGuessCheckbox.checked && autoGuessInterval) {
                stopAutoGuess();
                startAutoGuess();
            }
        });

        // Custom Words Checkbox
        customWordsCheckbox.addEventListener('change', (e) => {
            wordListContainer.style.display = e.target.checked ? 'block' : 'none';
            updateHitList(guessPattern.value.trim());
        });

        // Word List Dropzone
        ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
            wordListDropzone.addEventListener(eventName, preventDefaults, false);
        });

        wordListDropzone.addEventListener('dragenter', () => wordListDropzone.classList.add('drag-over'));
        wordListDropzone.addEventListener('dragover', () => wordListDropzone.classList.add('drag-over'));
        wordListDropzone.addEventListener('dragleave', () => wordListDropzone.classList.remove('drag-over'));
        wordListDropzone.addEventListener('drop', (e) => {
            wordListDropzone.classList.remove('drag-over');
            const file = e.dataTransfer.files[0];
            if (file && file.type === 'text/plain') handleWordListFile(file);
        });

        wordListInput.addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (file) {
                handleWordListFile(file);
                e.target.value = '';
            }
        });

        function handleWordListFile(file) {
            const reader = new FileReader();
            reader.onload = function(event) {
                wordList["Custom"] = event.target.result.split('\n').map(word => word.trim()).filter(word => word.length > 0);
                alert(`Loaded ${wordList["Custom"].length} words from ${file.name}`);
                updateHitList(guessPattern.value.trim());
            };
            reader.readAsText(file);
        }

        // Guess Pattern Input
        guessPattern.addEventListener('input', (e) => updateHitList(e.target.value.trim()));

        // Hit List Functionality
        hitList.addEventListener('click', (e) => {
            if (e.target.tagName === 'BUTTON' && !e.target.classList.contains('tried')) {
                const button = e.target;
                button.classList.add('tried');
                if (!triedLabelAdded && hitList.querySelectorAll('button.tried').length === 1) {
                    const triedLabel = document.createElement('div');
                    triedLabel.classList.add('tried-label');
                    triedLabel.textContent = 'Tried Words';
                    hitList.appendChild(triedLabel);
                    triedLabelAdded = true;
                }
                if (window.game && window.game._socket) {
                    window.game._socket.emit(13, window.game._codigo, button.textContent);
                }
                hitList.appendChild(button);
            }
        });

        function startAutoGuess() {
            if (!autoGuessCheckbox.checked) return;
            stopAutoGuess();
            const speed = parseInt(guessSpeed.value);
            autoGuessInterval = setInterval(() => {
                const buttons = hitList.querySelectorAll('button:not(.tried)');
                if (buttons.length > 0 && window.game && window.game._socket) {
                    const word = buttons[0].textContent;
                    buttons[0].classList.add('tried');
                    window.game._socket.emit(13, window.game._codigo, word);
                    if (!triedLabelAdded && hitList.querySelectorAll('button.tried').length === 1) {
                        const triedLabel = document.createElement('div');
                        triedLabel.classList.add('tried-label');
                        triedLabel.textContent = 'Tried Words';
                        hitList.appendChild(triedLabel);
                        triedLabelAdded = true;
                    }
                    hitList.appendChild(buttons[0]);
                }
            }, speed);
        }

        function stopAutoGuess() {
            if (autoGuessInterval) {
                clearInterval(autoGuessInterval);
                autoGuessInterval = null;
            }
        }

        function updateHitList(pattern) {
            hitList.innerHTML = '';
            triedLabelAdded = false;
            const activeTheme = customWordsCheckbox.checked || !window.game || !window.game._dadosSala || !window.game._dadosSala.tema
            ? "Custom" : window.game._dadosSala.tema;
            const activeList = wordList[activeTheme] || [];

            if (!pattern) {
                if (activeList.length === 0) {
                    hitList.innerHTML = `<div class="message">${customWordsCheckbox.checked ? 'Upload a custom word list ✧' : 'No words available ✧'}</div>`;
                } else {
                    activeList.forEach(word => {
                        const button = document.createElement('button');
                        button.textContent = word;
                        hitList.appendChild(button);
                    });
                }
                return;
            }

            const regex = new RegExp(`^${pattern.split('').map(char => char === '_' ? '.' : char).join('')}$`, 'i');
            const matches = activeList.filter(word => regex.test(word));

            if (matches.length === 0) {
                hitList.innerHTML = '<div class="message">No matches found ✧</div>';
            } else {
                matches.forEach(word => {
                    const button = document.createElement('button');
                    button.textContent = word;
                    hitList.appendChild(button);
                });
            }
        }

        async function fetchWordList(theme) {
            if (!wordList[theme] && wordListURLs[theme]) {
                try {
                    const response = await fetch(wordListURLs[theme]);
                    if (!response.ok) throw new Error(`Failed to fetch ${theme} word list`);
                    const data = await response.json();
                    wordList[theme] = data.words || data;
                    console.log(`Loaded ${wordList[theme].length} words for ${theme}`);
                } catch (error) {
                    console.error(`Error fetching word list for ${theme}:`, error);
                    wordList[theme] = [];
                }
            }
        }

        // Image Upload
        ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
            imageDropzone.addEventListener(eventName, preventDefaults, false);
        });

        imageDropzone.addEventListener('dragenter', () => imageDropzone.classList.add('drag-over'));
        imageDropzone.addEventListener('dragover', () => imageDropzone.classList.add('drag-over'));
        imageDropzone.addEventListener('dragleave', () => imageDropzone.classList.remove('drag-over'));
        imageDropzone.addEventListener('drop', (e) => {
            imageDropzone.classList.remove('drag-over');
            const file = e.dataTransfer.files[0];
            if (file && file.type.startsWith('image/')) handleImageFile(file);
        });

        imageUpload.addEventListener('change', (e) => {
            const file = e.target.files[0];
            if (file) {
                handleImageFile(file);
                e.target.value = '';
            }
        });

        function handleImageFile(file) {
            const reader = new FileReader();
            reader.onload = function(event) {
                previewImg.src = event.target.result;
                imageDropzone.style.display = 'none';
                imagePreview.style.display = 'block';
                sendDraw.disabled = false;
            };
            reader.readAsDataURL(file);
        }

        cancelImage.addEventListener('click', () => {
            previewImg.src = '';
            imageDropzone.style.display = 'flex';
            imagePreview.style.display = 'none';
            sendDraw.disabled = true;
            imageUpload.value = '';
        });

        drawSpeed.addEventListener('input', (e) => {
            updateSliderTrack(e.target);
            drawSpeedValue.textContent = e.target.value;
        });

        maxColors.addEventListener('input', (e) => {
            updateSliderTrack(e.target);
            maxColorsValue.textContent = e.target.value;
        });

        sendDraw.addEventListener('click', () => {
            if (previewImg.src) {
                if (!window.game || !window.game.turn) {
                    alert('Not your turn or game not loaded! ✧');
                    return;
                }
                sendDraw.disabled = true;
                processAndDrawImage(previewImg.src);
            }
        });

        // Socket Integration
        const checkGame = setInterval(() => {
            if (window.game && window.game._socket) {
                clearInterval(checkGame);
                const currentTheme = window.game._dadosSala.tema || "Custom";
                if (currentTheme !== "Custom") {
                    fetchWordList(currentTheme).then(() => updateHitList(guessPattern.value.trim()));
                }

                window.game._socket.on(30, (hint) => {
                    hint = String(hint).replace(/,/g, '');
                    guessPattern.value = hint;
                    updateHitList(hint);
                    if (autoGuessCheckbox.checked) startAutoGuess();
                });

                window.game._socket.on(19, () => {
                    guessPattern.value = '';
                    stopAutoGuess();
                    updateHitList('');
                });

                window.game._socket.on(15, (playerId) => {
                    if (playerId === window.game.me.id) {
                        guessPattern.value = '';
                        stopAutoGuess();
                        updateHitList('');
                    }
                });

                let lastTheme = currentTheme;
                setInterval(() => {
                    const newTheme = window.game._dadosSala.tema || "Custom";
                    if (newTheme !== lastTheme && newTheme !== "Custom") {
                        lastTheme = newTheme;
                        fetchWordList(newTheme).then(() => updateHitList(guessPattern.value.trim()));
                    }
                }, 1000);
            }
        }, 100);
    }

function processAndDrawImage(imageSrc) {
    if (!window.game || !window.game._socket || !window.game._desenho || !window.game.turn) {
        alert('Game not ready or not your turn! ✧');
        return;
    }

    const img = new Image();
    img.crossOrigin = "Anonymous";
    img.onload = async function() {
        const gameCanvas = window.game._desenho._canvas.canvas;
        if (!gameCanvas || !gameCanvas.width || !gameCanvas.height) {
            alert('Canvas not accessible! ✧');
            sendDraw.disabled = false;
            return;
        }

        const ctx = gameCanvas.getContext('2d');
        if (!ctx) {
            alert('Canvas context not available! ✧');
            sendDraw.disabled = false;
            return;
        }

        const canvasWidth = Math.floor(gameCanvas.width);
        const canvasHeight = Math.floor(gameCanvas.height);

        const tempCanvas = document.createElement('canvas');
        const tempCtx = tempCanvas.getContext('2d');
        if (!tempCtx) {
            alert('Temp canvas context failed! ✧');
            sendDraw.disabled = false;
            return;
        }

        const scale = Math.min(canvasWidth / img.width, canvasHeight / img.height);
        const newWidth = Math.floor(img.width * scale);
        const newHeight = Math.floor(img.height * scale);

        tempCanvas.width = canvasWidth;
        tempCanvas.height = canvasHeight;

        const offsetX = Math.floor((canvasWidth - newWidth) / 2);
        const offsetY = Math.floor((canvasHeight - newHeight) / 2);

        tempCtx.drawImage(img, offsetX, offsetY, newWidth, newHeight);

        let imageData;
        try {
            imageData = tempCtx.getImageData(0, 0, canvasWidth, canvasHeight);
        } catch (e) {
            alert('Image data error: ' + e.message + ' ✧');
            sendDraw.disabled = false;
            return;
        }

        const data = imageData.data;
        const drawSpeedValue = Math.max(300, parseInt(drawSpeed.value) || 500);

        // Get maxColors from menu
        const maxColorsValue = parseInt(drawSpeed.value) || 3;

        // Image bounds
        const imgLeft = offsetX;
        const imgRight = offsetX + newWidth - 1;
        const imgTop = offsetY;
        const imgBottom = offsetY + newHeight - 1;

        // Background detection
        const colorCounts = new Map();
        let backgroundColor = [255, 255, 255];
        const sampleStep = Math.max(1, Math.floor(newWidth / 50));

        for (let x = imgLeft; x <= imgRight; x += sampleStep) {
            for (let y = imgTop; y <= imgBottom; y += sampleStep) {
                const index = (y * canvasWidth + x) * 4;
                const r = Math.round(data[index] / 20) * 20;
                const g = Math.round(data[index+1] / 20) * 20;
                const b = Math.round(data[index+2] / 20) * 20;
                const key = `${r},${g},${b}`;
                colorCounts.set(key, (colorCounts.get(key) || 0) + 1);
            }
        }

        let maxCount = 0;
        for (const [key, count] of colorCounts) {
            if (count > maxCount) {
                maxCount = count;
                backgroundColor = key.split(',').map(Number);
            }
        }

        const bgHex = 'x' + backgroundColor.map(c =>
            c.toString(16).padStart(2, '0').toUpperCase()
        ).join('');

        // Clear and set background (both socket and local)
        window.game._socket.emit(10, window.game._codigo, [4]);
        ctx.clearRect(0, 0, canvasWidth, canvasHeight);
        await new Promise(resolve => setTimeout(resolve, drawSpeedValue));

        window.game._socket.emit(10, window.game._codigo, [5, bgHex]);
        ctx.fillStyle = `#${bgHex.slice(1)}`;
        await new Promise(resolve => setTimeout(resolve, drawSpeedValue));

        window.game._socket.emit(10, window.game._codigo, [3, 0, 0, canvasWidth, canvasHeight]);
        ctx.fillRect(0, 0, canvasWidth, canvasHeight);
        await new Promise(resolve => setTimeout(resolve, drawSpeedValue));

        // Color clustering for foreground
        const colorClusters = new Map();
        for (let y = imgTop; y < imgBottom; y += sampleStep) {
            for (let x = imgLeft; x < imgRight; x += sampleStep) {
                const index = (y * canvasWidth + x) * 4;
                const r = Math.round(data[index] / 20) * 20;
                const g = Math.round(data[index+1] / 20) * 20;
                const b = Math.round(data[index+2] / 20) * 20;
                const key = `${r},${g},${b}`;

                if (colorDistance([r, g, b], backgroundColor) > 60) {
                    colorClusters.set(key, (colorClusters.get(key) || 0) + 1);
                }
            }
        }

        const topColors = [...colorClusters.entries()]
            .sort((a, b) => b[1] - a[1])
            .slice(0, maxColorsValue)
            .map(([key]) => ({
                rgb: key.split(',').map(Number),
                hex: 'x' + key.split(',').map(c =>
                    Number(c).toString(16).padStart(2, '0').toUpperCase()
                ).join('')
            }));

        // Fill regions by color
        const fillsByColor = {};
        const visited = new Set();
        const stripHeight = 2;
        const minStripWidth = 10;

        for (let y = imgTop; y < imgBottom; y += stripHeight) {
            let startX = null;
            let currentColor = null;
            let stripWidth = 0;

            for (let x = imgLeft; x < imgRight; x += 1) {
                const index = (y * canvasWidth + x) * 4;
                const pixelColor = [data[index], data[index+1], data[index+2]];
                const bgDist = colorDistance(pixelColor, backgroundColor);

                if (bgDist > 60 && !visited.has(`${x},${y}`)) {
                    const nearestColor = topColors.reduce((prev, curr) =>
                        colorDistance(pixelColor, prev.rgb) < colorDistance(pixelColor, curr.rgb) ? prev : curr
                    );

                    if (startX === null || currentColor?.hex !== nearestColor.hex) {
                        if (startX !== null && stripWidth >= minStripWidth) {
                            if (!fillsByColor[currentColor.hex]) fillsByColor[currentColor.hex] = [];
                            fillsByColor[currentColor.hex].push([startX, y, stripWidth, stripHeight]);
                            for (let dx = 0; dx < stripWidth; dx++) {
                                visited.add(`${startX + dx},${y}`);
                            }
                        }
                        startX = x;
                        currentColor = nearestColor;
                        stripWidth = 1;
                    } else {
                        stripWidth++;
                    }
                } else if (startX !== null && bgDist <= 60) {
                    if (stripWidth >= minStripWidth) {
                        if (!fillsByColor[currentColor.hex]) fillsByColor[currentColor.hex] = [];
                        fillsByColor[currentColor.hex].push([startX, y, stripWidth, stripHeight]);
                        for (let dx = 0; dx < stripWidth; dx++) {
                            visited.add(`${startX + dx},${y}`);
                        }
                    }
                    startX = null;
                    currentColor = null;
                    stripWidth = 0;
                }
            }

            if (startX !== null && stripWidth >= minStripWidth) {
                if (!fillsByColor[currentColor.hex]) fillsByColor[currentColor.hex] = [];
                fillsByColor[currentColor.hex].push([startX, y, stripWidth, stripHeight]);
                for (let dx = 0; dx < stripWidth; dx++) {
                    visited.add(`${startX + dx},${y}`);
                }
            }
        }

        // Draw fills grouped by color (socket and local)
        for (const color in fillsByColor) {
            const fillCommand = [3];
            fillsByColor[color].forEach(([x, y, width, height]) => {
                fillCommand.push(x, y, width, height);
            });

            window.game._socket.emit(10, window.game._codigo, [5, color]);
            ctx.fillStyle = `#${color.slice(1)}`;
            await new Promise(resolve => setTimeout(resolve, drawSpeedValue));

            window.game._socket.emit(10, window.game._codigo, fillCommand);
            fillsByColor[color].forEach(([x, y, width, height]) => {
                ctx.fillRect(x, y, width, height);
            });
            await new Promise(resolve => setTimeout(resolve, drawSpeedValue));
        }

        // Contours for details
        const contours = [];
        const stepSize = 6;
        for (let y = imgTop + stepSize; y < imgBottom - stepSize; y += stepSize) {
            for (let x = imgLeft + stepSize; x < imgRight - stepSize; x += stepSize) {
                if (visited.has(`${x},${y}`)) continue;

                const index = (y * canvasWidth + x) * 4;
                const pixelColor = [data[index], data[index+1], data[index+2]];
                if (colorDistance(pixelColor, backgroundColor) <= 60) continue;

                const nearestColor = topColors.reduce((prev, curr) =>
                    colorDistance(pixelColor, prev.rgb) < colorDistance(pixelColor, curr.rgb) ? prev : curr
                );

                const contour = [];
                let currentX = x;
                let currentY = y;
                contour.push([currentX, currentY]);
                visited.add(`${currentX},${currentY}`);

                let tracing = true;
                let steps = 0;
                const maxSteps = 20;

                while (tracing && steps < maxSteps) {
                    const directions = [
                        [0, stepSize], [stepSize, 0], [0, -stepSize], [-stepSize, 0],
                        [stepSize, stepSize], [-stepSize, -stepSize],
                        [stepSize, -stepSize], [-stepSize, stepSize]
                    ];

                    let bestNext = null;
                    let bestScore = -Infinity;

                    for (const [dx, dy] of directions) {
                        const newX = currentX + dx;
                        const newY = currentY + dy;
                        if (newX >= imgLeft && newX < imgRight &&
                            newY >= imgTop && newY < imgBottom &&
                            !visited.has(`${newX},${newY}`)) {
                            const ni = (newY * canvasWidth + newX) * 4;
                            const newColor = [data[ni], data[ni+1], data[ni+2]];
                            const colorDist = colorDistance(newColor, nearestColor.rgb);
                            const bgDist = colorDistance(newColor, backgroundColor);
                            const edgeStrength = Math.abs(
                                data[ni] - data[(newY * canvasWidth + (newX + 1)) * 4]
                            );
                            const score = (bgDist > 60 && colorDist < 50) ? edgeStrength - colorDist : -Infinity;

                            if (score > bestScore) {
                                bestScore = score;
                                bestNext = [newX, newY];
                            }
                        }
                    }

                    if (bestNext) {
                        currentX = bestNext[0];
                        currentY = bestNext[1];
                        contour.push([currentX, currentY]);
                        visited.add(`${currentX},${currentY}`);
                        steps++;
                    } else {
                        tracing = false;
                    }
                }

                if (contour.length > 3) {
                    contours.push({ points: contour, color: nearestColor.hex });
                }
            }
        }

        // Draw contours (socket and local)
        const maxPoints = 15;
        for (const { points, color } of contours) {
            window.game._socket.emit(10, window.game._codigo, [5, color]);
            ctx.strokeStyle = `#${color.slice(1)}`;
            await new Promise(resolve => setTimeout(resolve, drawSpeedValue));

            window.game._socket.emit(10, window.game._codigo, [6, 4]);
            ctx.lineWidth = 4;
            await new Promise(resolve => setTimeout(resolve, drawSpeedValue));

            for (let i = 0; i < points.length - 1; i += maxPoints - 1) {
                const segment = points.slice(i, i + maxPoints);
                if (segment.length >= 2) {
                    const drawCommand = [1, 6];
                    segment.forEach(([x, y]) => drawCommand.push(x, y));
                    window.game._socket.emit(10, window.game._codigo, drawCommand);

                    ctx.beginPath();
                    ctx.moveTo(segment[0][0], segment[0][1]);
                    for (let j = 1; j < segment.length; j++) {
                        ctx.lineTo(segment[j][0], segment[j][1]);
                    }
                    ctx.stroke();
                    await new Promise(resolve => setTimeout(resolve, drawSpeedValue));
                }
            }
        }

        alert('Drawing completed! ✧');
        sendDraw.disabled = false;
    };
    img.onerror = function() {
        alert('Failed to load image! ✧');
        sendDraw.disabled = false;
    };
    img.src = imageSrc;
}

// Color distance helper
function colorDistance(color1, color2) {
    return Math.sqrt(
        Math.pow(color1[0] - color2[0], 2) +
        Math.pow(color1[1] - color2[1], 2) +
        Math.pow(color1[2] - color2[2], 2)
    );
}

// Color distance helper
function colorDistance(color1, color2) {
    return Math.sqrt(
        Math.pow(color1[0] - color2[0], 2) +
        Math.pow(color1[1] - color2[1], 2) +
        Math.pow(color1[2] - color2[2], 2)
    );
}
})();