您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Helper for Gartic.io with auto-guess, drawing assistance, and drawing bot
当前为
// ==UserScript== // @name Kawaii Helper & Drawing Bot for Gartic.io // @name:tr Gartic.io için Kawaii Yardımcı & Çizim Botu // @namespace http://tampermonkey.net/ // @version 2025-03-15 // @description Helper for Gartic.io with auto-guess, drawing assistance, and drawing bot // @description:tr Gartic.io için otomatik tahmin, çizim yardımı ve çizim botu ile yardımcı // @author anonimbiri & Gartic-Developers // @license MIT // @match https://gartic.io/* // @icon https://i.imgur.com/efEvdp5.png // @run-at document-start // @grant none // ==/UserScript== (function() { 'use strict'; class KawaiiHelper { constructor() { this.translations = { en: { "✧ Kawaii Helper ✧": "✧ Kawaii Helper ✧", "Guessing": "Guessing", "Drawing": "Drawing", "Auto Guess": "Auto Guess", "Speed": "Speed", "Custom Words": "Custom Words", "Drop word list here or click to upload": "Drop word list here or click to upload", "Enter pattern (e.g., ___e___)": "Enter pattern (e.g., ___e___)", "Type a pattern to see matches ✧": "Type a pattern to see matches ✧", "Upload a custom word list ✧": "Upload a custom word list ✧", "No words available ✧": "No words available ✧", "No matches found ✧": "No matches found ✧", "Tried Words": "Tried Words", "Drop image here or click to upload": "Drop image here or click to upload", "Draw Speed": "Draw Speed", "Max Colors": "Max Colors", "Draw Now ✧": "Draw Now ✧", "Made with ♥ by Anonimbiri & Gartic-Developers": "Made with ♥ by Anonimbiri & Gartic-Developers", "Loaded ${wordList['Custom'].length} words from ${file.name}": "Loaded ${wordList['Custom'].length} words from ${file.name}", "Not your turn or game not loaded! ✧": "Not your turn or game not loaded! ✧", "Game not ready or not your turn! ✧": "Game not ready or not your turn! ✧", "Canvas not accessible! ✧": "Canvas not accessible! ✧", "Canvas context not available! ✧": "Canvas context not available! ✧", "Temp canvas context failed! ✧": "Temp canvas context failed! ✧", "Image data error: ${e.message} ✧": "Image data error: ${e.message} ✧", "Drawing completed! ✧": "Drawing completed! ✧", "Failed to load image! ✧": "Failed to load image! ✧", "Drawing stopped! ✧": "Drawing stopped! ✧" }, tr: { "✧ Kawaii Helper ✧": "✧ Kawaii Yardımcı ✧", "Guessing": "Tahmin", "Drawing": "Çizim", "Auto Guess": "Otomatik Tahmin", "Speed": "Hız", "Custom Words": "Özel Kelimeler", "Drop word list here or click to upload": "Kelime listesini buraya bırak veya yüklemek için tıkla", "Enter pattern (e.g., ___e___)": "Desen gir (ör., ___e___)", "Type a pattern to see matches ✧": "Eşleşmeleri görmek için bir desen yaz ✧", "Upload a custom word list ✧": "Özel bir kelime listesi yükle ✧", "No words available ✧": "Kelime yok ✧", "No matches found ✧": "Eşleşme bulunamadı ✧", "Tried Words": "Denenen Kelimeler", "Drop image here or click to upload": "Resmi buraya bırak veya yüklemek için tıkla", "Draw Speed": "Çizim Hızı", "Max Colors": "Maksimum Renk", "Draw Now ✧": "Şimdi Çiz ✧", "Made with ♥ by Anonimbiri & Gartic-Developers": "Anonimbiri & Gartic-Developers tarafından ♥ ile yapıldı", "Loaded ${wordList['Custom'].length} words from ${file.name}": "${file.name} dosyasından ${wordList['Custom'].length} kelime yüklendi", "Not your turn or game not loaded! ✧": "Sıra sende değil veya oyun yüklenmedi! ✧", "Game not ready or not your turn! ✧": "Oyun hazır değil veya sıra sende değil! ✧", "Canvas not accessible! ✧": "Tuval erişilemez! ✧", "Canvas context not available! ✧": "Tuval bağlamı kullanılamıyor! ✧", "Temp canvas context failed! ✧": "Geçici tuval bağlamı başarısız! ✧", "Image data error: ${e.message} ✧": "Görüntü verisi hatası: ${e.message} ✧", "Drawing completed! ✧": "Çizim tamamlandı! ✧", "Failed to load image! ✧": "Görüntü yüklenemedi! ✧", "Drawing stopped! ✧": "Çizim durduruldu! ✧" } }; this.currentLang = navigator.language.split('-')[0] in this.translations ? navigator.language.split('-')[0] : 'en'; this.isDrawingActive = false; this.wordList = { "Custom": [] }; this.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" }; this.elements = {}; this.state = { isDragging: false, initialX: 0, initialY: 0, xOffset: 0, yOffset: 0, rafId: null, autoGuessInterval: null, triedLabelAdded: false }; } static init() { const helper = new KawaiiHelper(); helper.setup(); return helper; } localize(key, params = {}) { let text = this.translations[this.currentLang][key] || key; for (const [param, value] of Object.entries(params)) { text = text.replace(`\${${param}}`, value); } return text; } showNotification(message, duration = 3000) { const notification = document.createElement('div'); notification.className = 'kawaii-notification'; notification.innerHTML = ` <span class="kawaii-notification-icon">✧</span> <span class="kawaii-notification-text">${message}</span> <button class="kawaii-notification-close">✕</button> `; this.elements.notifications.appendChild(notification); setTimeout(() => notification.classList.add('show'), 10); const timeout = setTimeout(() => { notification.classList.remove('show'); setTimeout(() => notification.remove(), 300); }, duration); notification.querySelector('.kawaii-notification-close').addEventListener('click', () => { clearTimeout(timeout); notification.classList.remove('show'); setTimeout(() => notification.remove(), 300); }); } setup() { this.interceptScripts(); this.injectFonts(); this.waitForBody(() => { this.injectHTML(); this.cacheElements(); this.addStyles(); this.bindEvents(); this.initializeGameCheck(); }); } interceptScripts() { Node.prototype.appendChild = new Proxy(Node.prototype.appendChild, { apply: (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);}});}' ); const blob = new Blob([modifiedContent], { type: 'application/javascript' }); node.src = URL.createObjectURL(blob); node.textContent = ''; return target.apply(thisArg, [node]); }) .catch(error => console.error('Failed to fetch/modify script:', error)); return node; } return target.apply(thisArg, argumentsList); } }); } injectFonts() { 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); } waitForBody(callback) { const interval = setInterval(() => { if (document.body) { clearInterval(interval); callback(); } }, 100); } injectHTML() { const kawaiiHTML = ` <div class="kawaii-cheat" id="kawaiiCheat"> <div class="kawaii-header" id="kawaiiHeader"> <img src="https://i.imgur.com/efEvdp5.png" alt="Anime Girl" class="header-icon"> <h2 data-translate="✧ Kawaii Helper ✧">✧ 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" data-translate="Guessing">Guessing</button> <button class="kawaii-tab" data-tab="drawing" data-translate="Drawing">Drawing</button> </div> <div class="kawaii-content" id="guessing-tab"> <div class="checkbox-container"> <input type="checkbox" id="autoGuess"> <label for="autoGuess" data-translate="Auto Guess">Auto Guess</label> </div> <div class="slider-container" id="speedContainer" style="display: none;"> <div class="slider-label" data-translate="Speed">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" data-translate="Custom Words">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 data-translate="Drop word list here or click to upload">Drop word list here or click to upload</p> </div> </div> </div> <div class="input-container"> <input type="text" id="guessPattern" data-translate-placeholder="Enter pattern (e.g., ___e___)" placeholder="Enter pattern (e.g., ___e___)"> </div> <div class="hit-list" id="hitList"> <div class="message" data-translate="Type a pattern to see matches ✧">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 data-translate="Drop image here or click to upload">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" data-translate="Draw Speed">Draw Speed</div> <div class="custom-slider"> <input type="range" id="drawSpeed" min="20" max="5000" value="200" step="100"> <div class="slider-track"></div> <span id="drawSpeedValue">200ms</span> </div> </div> <div class="slider-container"> <div class="slider-label" data-translate="Max Colors">Max Colors</div> <div class="custom-slider"> <input type="range" id="maxColors" min="3" max="1000" value="100" step="1"> <div class="slider-track"></div> <span id="maxColorsValue">100</span> </div> </div> <button class="draw-btn" id="sendDraw" disabled data-translate="Draw Now ✧">Draw Now ✧</button> </div> <div class="kawaii-footer"> <span class="credit-text" data-translate="Made with ♥ by Anonimbiri & Gartic-Developers">Made with ♥ by Anonimbiri & Gartic-Developers</span> </div> </div> </div> <div class="kawaii-notifications" id="kawaiiNotifications"></div> `; document.body.insertAdjacentHTML('beforeend', kawaiiHTML); } cacheElements() { this.elements = { kawaiiCheat: document.getElementById('kawaiiCheat'), kawaiiHeader: document.getElementById('kawaiiHeader'), minimizeBtn: document.getElementById('minimizeBtn'), tabButtons: document.querySelectorAll('.kawaii-tab'), tabContents: document.querySelectorAll('.kawaii-content'), autoGuessCheckbox: document.getElementById('autoGuess'), speedContainer: document.getElementById('speedContainer'), guessSpeed: document.getElementById('guessSpeed'), speedValue: document.getElementById('speedValue'), customWordsCheckbox: document.getElementById('customWords'), wordListContainer: document.getElementById('wordListContainer'), wordListDropzone: document.getElementById('wordListDropzone'), wordListInput: document.getElementById('wordList'), guessPattern: document.getElementById('guessPattern'), hitList: document.getElementById('hitList'), imageDropzone: document.getElementById('imageDropzone'), imageUpload: document.getElementById('imageUpload'), imagePreview: document.getElementById('imagePreview'), previewImg: document.getElementById('previewImg'), cancelImage: document.getElementById('cancelImage'), drawSpeed: document.getElementById('drawSpeed'), drawSpeedValue: document.getElementById('drawSpeedValue'), maxColors: document.getElementById('maxColors'), maxColorsValue: document.getElementById('maxColorsValue'), sendDraw: document.getElementById('sendDraw'), notifications: document.getElementById('kawaiiNotifications') }; } addStyles() { 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; } .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: 1px dashed 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; min-height: 0; flex-grow: 1; overflow: hidden; 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: 1px dashed 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; width: 100%; box-sizing: border-box; } .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; } .kawaii-notifications { position: fixed; top: 20px; right: 20px; display: flex; flex-direction: column; gap: 10px; z-index: 2000; pointer-events: none; } .kawaii-notification { background: var(--panel-bg); border: 2px solid var(--panel-border); border-radius: 12px; padding: 12px 18px; color: var(--text-color); font-family: 'M PLUS Rounded 1c', sans-serif; font-size: 14px; font-weight: 700; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); display: flex; align-items: center; gap: 10px; max-width: 300px; opacity: 0; transform: translateX(100%); transition: opacity 0.3s ease, transform 0.3s ease; pointer-events: auto; } .kawaii-notification.show { opacity: 1; transform: translateX(0); } .kawaii-notification-icon { font-size: 20px; color: var(--primary-dark); animation: bounce 1s infinite ease-in-out; } @keyframes bounce { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-5px); } } .kawaii-notification-close { background: transparent; border: 1px dashed var(--primary-dark); border-radius: 6px; width: 20px; height: 20px; color: var(--primary-dark); font-size: 12px; line-height: 18px; text-align: center; cursor: pointer; transition: all 0.3s ease; } .kawaii-notification-close:hover { background: var(--primary-dark); color: var(--element-active-text); transform: scale(1.1); } `; document.head.appendChild(style); this.updateLanguage(); [this.elements.guessSpeed, this.elements.drawSpeed, this.elements.maxColors].forEach(this.updateSliderTrack.bind(this)); } updateLanguage() { document.querySelectorAll('[data-translate]').forEach(element => { element.textContent = this.localize(element.getAttribute('data-translate')); }); document.querySelectorAll('[data-translate-placeholder]').forEach(element => { element.setAttribute('placeholder', this.localize(element.getAttribute('data-translate-placeholder'))); }); } 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}%`); } preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } bindEvents() { this.elements.kawaiiHeader.addEventListener('mousedown', this.startDragging.bind(this)); document.addEventListener('mousemove', this.drag.bind(this)); document.addEventListener('mouseup', this.stopDragging.bind(this)); this.elements.minimizeBtn.addEventListener('click', this.toggleMinimize.bind(this)); this.elements.tabButtons.forEach(btn => btn.addEventListener('click', this.switchTab.bind(this, btn))); document.querySelectorAll('.checkbox-container').forEach(container => { const checkbox = container.querySelector('input[type="checkbox"]'); const label = container.querySelector('label'); container.addEventListener('click', e => { if (e.target !== checkbox && e.target !== label) { checkbox.checked = !checkbox.checked; checkbox.dispatchEvent(new Event('change')); } }); label.addEventListener('click', e => e.stopPropagation()); }); this.elements.autoGuessCheckbox.addEventListener('change', this.toggleAutoGuess.bind(this)); this.elements.guessSpeed.addEventListener('input', this.updateGuessSpeed.bind(this)); this.elements.customWordsCheckbox.addEventListener('change', this.toggleCustomWords.bind(this)); this.elements.guessPattern.addEventListener('input', e => this.updateHitList(e.target.value.trim())); this.elements.hitList.addEventListener('click', this.handleHitListClick.bind(this)); ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { this.elements.wordListDropzone.addEventListener(eventName, this.preventDefaults, false); this.elements.imageDropzone.addEventListener(eventName, this.preventDefaults, false); }); this.elements.wordListDropzone.addEventListener('dragenter', () => this.elements.wordListDropzone.classList.add('drag-over')); this.elements.wordListDropzone.addEventListener('dragover', () => this.elements.wordListDropzone.classList.add('drag-over')); this.elements.wordListDropzone.addEventListener('dragleave', () => this.elements.wordListDropzone.classList.remove('drag-over')); this.elements.wordListDropzone.addEventListener('drop', this.handleWordListDrop.bind(this)); this.elements.wordListInput.addEventListener('change', this.handleWordListInput.bind(this)); this.elements.imageDropzone.addEventListener('dragenter', () => this.elements.imageDropzone.classList.add('drag-over')); this.elements.imageDropzone.addEventListener('dragover', () => this.elements.imageDropzone.classList.add('drag-over')); this.elements.imageDropzone.addEventListener('dragleave', () => this.elements.imageDropzone.classList.remove('drag-over')); this.elements.imageDropzone.addEventListener('drop', this.handleImageDrop.bind(this)); this.elements.imageUpload.addEventListener('change', this.handleImageInput.bind(this)); this.elements.cancelImage.addEventListener('click', this.cancelImagePreview.bind(this)); this.elements.drawSpeed.addEventListener('input', this.updateDrawSpeed.bind(this)); this.elements.maxColors.addEventListener('input', this.updateMaxColors.bind(this)); this.elements.sendDraw.addEventListener('click', this.startDrawing.bind(this)); } startDragging(e) { if (e.target !== this.elements.minimizeBtn) { this.state.initialX = e.clientX - this.state.xOffset; this.state.initialY = e.clientY - this.state.yOffset; this.state.isDragging = true; this.elements.kawaiiCheat.classList.add('dragging'); if (this.state.rafId) cancelAnimationFrame(this.state.rafId); } } drag(e) { if (!this.state.isDragging) return; e.preventDefault(); const newX = e.clientX - this.state.initialX; const newY = e.clientY - this.state.initialY; if (this.state.rafId) cancelAnimationFrame(this.state.rafId); this.state.rafId = requestAnimationFrame(() => { this.elements.kawaiiCheat.style.transform = `translate(${newX}px, ${newY}px)`; this.state.xOffset = newX; this.state.yOffset = newY; }); } stopDragging() { if (this.state.isDragging) { this.state.isDragging = false; this.elements.kawaiiCheat.classList.remove('dragging'); if (this.state.rafId) cancelAnimationFrame(this.state.rafId); } } toggleMinimize() { this.elements.kawaiiCheat.classList.toggle('minimized'); this.elements.minimizeBtn.textContent = this.elements.kawaiiCheat.classList.contains('minimized') ? '▲' : '▼'; } switchTab(btn) { this.elements.tabButtons.forEach(b => b.classList.remove('active')); this.elements.tabContents.forEach(c => c.style.display = 'none'); btn.classList.add('active'); document.getElementById(`${btn.dataset.tab}-tab`).style.display = 'flex'; } toggleAutoGuess(e) { this.elements.speedContainer.style.display = e.target.checked ? 'flex' : 'none'; if (!e.target.checked) this.stopAutoGuess(); else if (this.elements.guessPattern.value) this.startAutoGuess(); } updateGuessSpeed(e) { this.updateSliderTrack(e.target); this.elements.speedValue.textContent = e.target.value >= 1000 ? `${e.target.value / 1000}s` : `${e.target.value}ms`; if (this.elements.autoGuessCheckbox.checked && this.state.autoGuessInterval) { this.stopAutoGuess(); this.startAutoGuess(); } } toggleCustomWords(e) { this.elements.wordListContainer.style.display = e.target.checked ? 'block' : 'none'; this.updateHitList(this.elements.guessPattern.value.trim()); } handleWordListDrop(e) { this.elements.wordListDropzone.classList.remove('drag-over'); const file = e.dataTransfer.files[0]; if (file && file.type === 'text/plain') this.handleWordListFile(file); } handleWordListInput(e) { const file = e.target.files[0]; if (file) { this.handleWordListFile(file); e.target.value = ''; } } handleWordListFile(file) { const reader = new FileReader(); reader.onload = (event) => { this.wordList["Custom"] = event.target.result.split('\n').map(word => word.trim()).filter(word => word.length > 0); this.showNotification(this.localize("Loaded ${wordList['Custom'].length} words from ${file.name}", { "wordList['Custom'].length": this.wordList["Custom"].length, "file.name": file.name }), 4000); this.updateHitList(this.elements.guessPattern.value.trim()); }; reader.readAsText(file); } handleHitListClick(e) { if (e.target.tagName !== 'BUTTON' || e.target.classList.contains('tried')) return; const button = e.target; button.classList.add('tried'); if (!this.state.triedLabelAdded && this.elements.hitList.querySelectorAll('button.tried').length === 1) { const triedLabel = document.createElement('div'); triedLabel.classList.add('tried-label'); triedLabel.textContent = this.localize("Tried Words"); this.elements.hitList.appendChild(triedLabel); this.state.triedLabelAdded = true; } if (window.game && window.game._socket) { window.game._socket.emit(13, window.game._codigo, button.textContent); } this.elements.hitList.appendChild(button); } startAutoGuess() { if (!this.elements.autoGuessCheckbox.checked) return; this.stopAutoGuess(); const speed = parseInt(this.elements.guessSpeed.value); this.state.autoGuessInterval = setInterval(() => { const buttons = this.elements.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 (!this.state.triedLabelAdded && this.elements.hitList.querySelectorAll('button.tried').length === 1) { const triedLabel = document.createElement('div'); triedLabel.classList.add('tried-label'); triedLabel.textContent = this.localize("Tried Words"); this.elements.hitList.appendChild(triedLabel); this.state.triedLabelAdded = true; } this.elements.hitList.appendChild(buttons[0]); } }, speed); } stopAutoGuess() { if (this.state.autoGuessInterval) { clearInterval(this.state.autoGuessInterval); this.state.autoGuessInterval = null; } } updateHitList(pattern) { this.elements.hitList.innerHTML = ''; this.state.triedLabelAdded = false; const activeTheme = this.elements.customWordsCheckbox.checked || !window.game || !window.game._dadosSala || !window.game._dadosSala.tema ? "Custom" : window.game._dadosSala.tema; const activeList = this.wordList[activeTheme] || []; if (!pattern) { if (activeList.length === 0) { this.elements.hitList.innerHTML = `<div class="message">${this.localize(this.elements.customWordsCheckbox.checked ? "Upload a custom word list ✧" : "No words available ✧")}</div>`; } else { activeList.forEach(word => { const button = document.createElement('button'); button.textContent = word; this.elements.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) { this.elements.hitList.innerHTML = `<div class="message">${this.localize("No matches found ✧")}</div>`; } else { matches.forEach(word => { const button = document.createElement('button'); button.textContent = word; this.elements.hitList.appendChild(button); }); } } async fetchWordList(theme) { if (!this.wordList[theme] && this.wordListURLs[theme]) { try { const response = await fetch(this.wordListURLs[theme]); if (!response.ok) throw new Error(`Failed to fetch ${theme} word list`); const data = await response.json(); this.wordList[theme] = data.words || data; console.log(`Loaded ${this.wordList[theme].length} words for ${theme}`); } catch (error) { console.error(`Error fetching word list for ${theme}:`, error); this.wordList[theme] = []; } } } handleImageDrop(e) { this.elements.imageDropzone.classList.remove('drag-over'); const file = e.dataTransfer.files[0]; if (file && file.type.startsWith('image/')) this.handleImageFile(file); } handleImageInput(e) { const file = e.target.files[0]; if (file) { this.handleImageFile(file); e.target.value = ''; } } handleImageFile(file) { const reader = new FileReader(); reader.onload = (event) => { this.elements.previewImg.src = event.target.result; this.elements.imageDropzone.style.display = 'none'; this.elements.imagePreview.style.display = 'block'; this.elements.sendDraw.disabled = false; }; reader.readAsDataURL(file); } cancelImagePreview() { this.elements.previewImg.src = ''; this.elements.imageDropzone.style.display = 'flex'; this.elements.imagePreview.style.display = 'none'; this.elements.sendDraw.disabled = true; this.elements.imageUpload.value = ''; } updateDrawSpeed(e) { this.updateSliderTrack(e.target); this.elements.drawSpeedValue.textContent = e.target.value >= 1000 ? `${e.target.value / 1000}s` : `${e.target.value}ms`; } updateMaxColors(e) { this.updateSliderTrack(e.target); this.elements.maxColorsValue.textContent = e.target.value; } startDrawing() { if (this.elements.previewImg.src) { if (!window.game || !window.game.turn) { this.showNotification(this.localize("Not your turn or game not loaded! ✧"), 3000); return; } this.elements.sendDraw.disabled = true; this.isDrawingActive = true; this.processAndDrawImage(this.elements.previewImg.src); } } initializeGameCheck() { const checkGame = setInterval(() => { if (window.game && window.game._socket) { clearInterval(checkGame); const currentTheme = window.game._dadosSala.tema || "Custom"; if (currentTheme !== "Custom") { this.fetchWordList(currentTheme).then(() => this.updateHitList(this.elements.guessPattern.value.trim())); } window.game._socket.on(30, hint => { hint = String(hint).replace(/,/g, ''); this.elements.guessPattern.value = hint; this.updateHitList(hint); if (this.elements.autoGuessCheckbox.checked) this.startAutoGuess(); }); window.game._socket.on(19, () => { this.elements.guessPattern.value = ''; this.stopAutoGuess(); this.updateHitList(''); if (this.isDrawingActive) { this.isDrawingActive = false; this.elements.sendDraw.disabled = false; } }); window.game._socket.on(15, playerId => { if (playerId === window.game.me.id) { this.elements.guessPattern.value = ''; this.stopAutoGuess(); this.updateHitList(''); } }); let lastTheme = currentTheme; setInterval(() => { const newTheme = window.game._dadosSala.tema || "Custom"; if (newTheme !== lastTheme && newTheme !== "Custom") { lastTheme = newTheme; this.fetchWordList(newTheme).then(() => this.updateHitList(this.elements.guessPattern.value.trim())); } }, 1000); } }, 100); } processAndDrawImage(imageSrc) { if (!window.game || !window.game._socket || !window.game._desenho || !window.game.turn || !this.isDrawingActive) { this.showNotification(this.localize("Game not ready or not your turn! ✧"), 3000); this.elements.sendDraw.disabled = false; return; } const img = new Image(); img.crossOrigin = "Anonymous"; img.onload = async () => { const gameCanvas = window.game._desenho._canvas.canvas; const ctx = gameCanvas.getContext('2d'); if (!ctx) { this.showNotification(this.localize("Canvas context not available! ✧"), 3000); 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) { this.showNotification(this.localize("Temp canvas context failed! ✧"), 3000); return; } tempCanvas.width = canvasWidth; tempCanvas.height = canvasHeight; const scale = Math.min(canvasWidth / img.width, canvasHeight / img.height); const newWidth = Math.floor(img.width * scale); const newHeight = Math.floor(img.height * scale); const offsetX = Math.floor((canvasWidth - newWidth) / 2); const offsetY = Math.floor((canvasHeight - newHeight) / 2); tempCtx.drawImage(img, offsetX, offsetY, newWidth, newHeight); const imageData = tempCtx.getImageData(0, 0, canvasWidth, canvasHeight); const data = imageData.data; const drawSpeedValue = parseInt(this.elements.drawSpeed.value) || 300; const maxColorsValue = parseInt(this.elements.maxColors.value) || 20; const imgLeft = offsetX; const imgRight = offsetX + newWidth - 1; const imgTop = offsetY; const imgBottom = offsetY + newHeight - 1; // Arka plan rengini belirle const colorCounts = new Map(); const sampleStep = Math.max(1, Math.floor(newWidth / 50)); for (let x = imgLeft; x <= imgRight; x += sampleStep) { for (let y of [imgTop, imgBottom]) { const index = (y * canvasWidth + x) * 4; const key = `${data[index]},${data[index + 1]},${data[index + 2]}`; colorCounts.set(key, (colorCounts.get(key) || 0) + 1); } } let backgroundColor = [255, 255, 255]; 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(''); // Canvas'ı temizle if (!this.isDrawingActive || !window.game.turn) { this.stopDrawing(); return; } window.game._socket.emit(10, window.game._codigo, [4]); ctx.clearRect(0, 0, canvasWidth, canvasHeight); await new Promise(resolve => setTimeout(resolve, drawSpeedValue)); // Arka planı çiz if (!this.isDrawingActive || !window.game.turn) { this.stopDrawing(); return; } window.game._socket.emit(10, window.game._codigo, [5, bgHex]); window.game._socket.emit(10, window.game._codigo, [3, 0, 0, canvasWidth, canvasHeight]); ctx.fillStyle = `#${bgHex.slice(1)}`; ctx.fillRect(0, 0, canvasWidth, canvasHeight); await new Promise(resolve => setTimeout(resolve, drawSpeedValue)); // Renk kümelerini oluştur const colorClusters = new Map(); const finerSampleStep = Math.max(1, Math.floor(newWidth / 100)); for (let y = imgTop; y <= imgBottom; y += finerSampleStep) { for (let x = imgLeft; x <= imgRight; x += finerSampleStep) { const index = (y * canvasWidth + x) * 4; const key = `${data[index]},${data[index + 1]},${data[index + 2]}`; if (this.colorDistance([data[index], data[index + 1], data[index + 2]], backgroundColor) > 30) { 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('') })); const visited = new Set(); const getColorAt = (x, y) => { const index = (y * canvasWidth + x) * 4; return [data[index], data[index + 1], data[index + 2]]; }; // Bitişik pikselleri birleştir ve tek komutta gönder const traceAndGroupRegion = (startX, startY, targetColor) => { const stack = [[startX, startY]]; const regionCoords = []; const visitedInRegion = new Set(); while (stack.length > 0) { const [x, y] = stack.pop(); const key = `${x},${y}`; if ( visited.has(key) || visitedInRegion.has(key) || x < imgLeft || x > imgRight || y < imgTop || y > imgBottom ) { continue; } const pixelColor = getColorAt(x, y); if (this.colorDistance(pixelColor, targetColor) <= 10) { visitedInRegion.add(key); regionCoords.push([x, y]); stack.push([x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]); } } if (regionCoords.length < 5) return []; // Çok küçük bölgeleri çizme visitedInRegion.forEach(k => visited.add(k)); // Bölgeyi küçük ama birleşik dikdörtgenlere böl const fills = []; let currentRow = []; let lastY = null; regionCoords.sort((a, b) => a[1] - b[1] || a[0] - b[0]); // Y'ye göre sırala, sonra X for (const [x, y] of regionCoords) { if (lastY !== y) { if (currentRow.length > 0) { const minX = Math.min(...currentRow); const maxX = Math.max(...currentRow); fills.push([minX, lastY, maxX - minX + 1, 1]); currentRow = []; } lastY = y; } currentRow.push(x); } if (currentRow.length > 0) { const minX = Math.min(...currentRow); const maxX = Math.max(...currentRow); fills.push([minX, lastY, maxX - minX + 1, 1]); } return fills; }; // Çizim işlemi const drawRegions = async () => { for (let y = imgTop; y <= imgBottom; y += finerSampleStep) { if (!this.isDrawingActive || !window.game.turn) { this.stopDrawing(); return; } for (let x = imgLeft; x <= imgRight; x += finerSampleStep) { const key = `${x},${y}`; if (visited.has(key)) continue; const pixelColor = getColorAt(x, y); if (this.colorDistance(pixelColor, backgroundColor) <= 30) continue; const nearestColor = topColors.reduce((prev, curr) => this.colorDistance(pixelColor, prev.rgb) < this.colorDistance(pixelColor, curr.rgb) ? prev : curr ); const fills = traceAndGroupRegion(x, y, nearestColor.rgb); if (fills.length === 0) continue; if (!this.isDrawingActive || !window.game.turn) { this.stopDrawing(); return; } // Tek bir socket mesajında tüm bölgeyi gönder window.game._socket.emit(10, window.game._codigo, [5, nearestColor.hex]); ctx.fillStyle = `#${nearestColor.hex.slice(1)}`; const fillCommand = [3]; fills.forEach(([x, y, w, h]) => { fillCommand.push(x, y, w, h); ctx.fillRect(x, y, w, h); }); window.game._socket.emit(10, window.game._codigo, fillCommand); await new Promise(resolve => setTimeout(resolve, drawSpeedValue)); } } }; await drawRegions(); if (this.isDrawingActive) { this.showNotification(this.localize("Drawing completed! ✧"), 3000); } this.stopDrawing(); }; img.onerror = () => { this.showNotification(this.localize("Failed to load image! ✧"), 3000); this.stopDrawing(); }; img.src = imageSrc; } colorDistance(color1, color2) { const rMean = (color1[0] + color2[0]) / 2; const r = color1[0] - color2[0]; const g = color1[1] - color2[1]; const b = color1[2] - color2[2]; return Math.sqrt( (2 + rMean / 256) * r * r + 4 * g * g + (2 + (255 - rMean) / 256) * b * b ); } } KawaiiHelper.init(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址