您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
RED KING ♔!定制与舒适度排名第一的客户端。
// ==UserScript== // @name ♔ Red King ♔ // @name:en ♔ Red King ♔ // @name:es ♔ Red King ♔ // @name:fr ♔ Red King ♔ // @name:de ♔ Red King ♔ // @name:it ♔ Red King ♔ // @name:pt ♔ Red King ♔ // @name:ru ♔ Красный Король ♔ // @name:zh-CN ♔ 红王 ♔ // @name:ja ♔ レッドキング ♔ // @name:ko ♔ 레드 킹 ♔ // @name:ar ♔ الملك الأحمر ♔ // @name:hi ♔ रेड किंग ♔ // @name:tr ♔ Kırmızı Kral ♔ // @name:pl ♔ Czerwony Król ♔ // @name:nl ♔ Rode Koning ♔ // @name:uk ♔ Червоний Король ♔ // @name:ca ♔ Rei Vermell ♔ // @name:sv ♔ Röda Kungen ♔ // @name:th ♔ ราชาแดง ♔ // @name:el ♔ Κόκκινος Βασιλιάς ♔ // @name:hu ♔ Vörös Király ♔ // @name:cs ♔ Červený Král ♔ // @name:ro ♔ Regele Roșu ♔ // @name:fi ♔ Punainen Kuningas ♔ // @name:no ♔ Røde Kongen ♔ // @name:da ♔ Røde Konge ♔ // @name:sk ♔ Červený Kráľ ♔ // @name:bg ♔ Червеният крал ♔ // @namespace https://lichess.org/@/IamGi4nx // @version 2.4.1.4 // @description ¡RED KING ♔! El cliente número 1 en personalización y comodidad. // @description:en RED KING ♔! The #1 client for customization and comfort. // @description:es ¡RED KING ♔! El cliente número uno en personalización y comodidad. // @description:fr RED KING ♔ ! Le client n°1 pour la personnalisation et le confort. // @description:de RED KING ♔! Der #1-Client für Anpassung und Komfort. // @description:it RED KING ♔! Il client numero 1 per personalizzazione e comfort. // @description:pt RED KING ♔! O cliente nº1 em personalização e conforto. // @description:ru RED KING ♔! Клиент №1 по персонализации и удобству. // @description:zh-CN RED KING ♔!定制与舒适度排名第一的客户端。 // @description:ja RED KING ♔!カスタマイズ性と快適さでNo.1のクライアント。 // @description:ko RED KING ♔! 맞춤화와 편안함에서 최고의 클라이언트입니다. // @description:ar RED KING ♔! العميل رقم 1 في التخصيص والراحة. // @description:hi RED KING ♔! कस्टमाइज़ेशन और आराम के लिए नंबर 1 क्लाइंट। // @description:tr RED KING ♔! Özelleştirme ve konfor konusunda 1 numaralı istemci. // @description:pl RED KING ♔! Numer 1 w personalizacji i komforcie. // @description:nl RED KING ♔! De #1 klant voor maatwerk en comfort. // @description:uk RED KING ♔! Клієнт №1 з персоналізації та комфорту. // @description:qu RED KING ♔! Ñawpaqmi kikinmanta ruwasqa hina kuska ima hina kanki! // @description:ca RED KING ♔! El client número 1 en personalització i comoditat. // @description:sv RED KING ♔! Den bästa klienten för anpassning och komfort. // @description:th RED KING ♔! ไคลเอนต์อันดับ 1 ด้านการปรับแต่งและความสะดวกสบาย // @description:el RED KING ♔! Ο νούμερο 1 πελάτης για προσαρμογή και άνεση. // @description:hu RED KING ♔! Az első számú kliens testreszabáshoz és kényelemhez. // @description:cs RED KING ♔! Klient číslo 1 pro přizpůsobení a pohodlí. // @description:ro RED KING ♔! Clientul nr.1 pentru personalizare și confort. // @description:fi RED KING ♔! Ykköskäyttäjä mukautettavuudessa ja mukavuudessa. // @description:no RED KING ♔! Den #1 klienten for tilpasning og komfort. // @description:da RED KING ♔! Den bedste klient til tilpasning og komfort. // @description:sk RED KING ♔! Klient číslo 1 pre prispôsobenie a pohodlie. // @description:bg RED KING ♔! Клиент №1 за персонализиране и комфорт. // @author IamGi4nx // @match https://lichess.org/* // @match https://*.lichess.org/* // @icon https://openclipart.org/image/2000px/275290 // @license Copyright (c) 2025 IamGi4nx. All rights reserved. // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_openInTab // @grant GM_notification // @run-at document-end // ==/UserScript== (function() { 'use strict'; // === ESTILOS ROJIZOS PERSONALIZADOS === GM_addStyle(` #redKingPanel { background: linear-gradient(145deg, #1a0a0a 0%, #2b0e0e 100%) !important; border: 2px solid #b22222 !important; border-radius: 15px !important; box-shadow: 0 15px 35px rgba(0,0,0,0.9), 0 0 20px rgba(178,34,34,0.5) !important; overflow: hidden !important; backdrop-filter: blur(10px) !important; } #redKingPanel #panelHeader { background: linear-gradient(135deg, #8B0000 0%, #DC143C 100%) !important; color: #FFD7D7 !important; font-weight: bold !important; } #redKingPanel .tab-btn { flex: 1; padding: 10px 6px; text-align: center; font-size: 11px; cursor: pointer; background: #3a1f1f !important; color: #ddd !important; transition: all 0.3s ease; } #redKingPanel .tab-btn.active { background: #b22222 !important; color: #FFD700 !important; } #redKingPanel .action-btn, #redKingPanel .social-btn { background: linear-gradient(135deg, #a83232 0%, #7a0e0e 100%) !important; color: #fff !important; border: none !important; border-radius: 6px !important; padding: 8px 12px !important; font-size: 12px !important; cursor: pointer !important; box-shadow: 0 4px 10px rgba(0,0,0,0.3) !important; transition: all 0.25s ease; } #redKingPanel .action-btn:hover, #redKingPanel .social-btn:hover { background: linear-gradient(135deg, #c0392b 0%, #922b21 100%) !important; transform: translateY(-2px); } .toggle-switch input:checked + .toggle-slider { background-color: #b22222 !important; } #redKingTopNotification { background: linear-gradient(90deg, #a10c0c 0%, #b22222 100%) !important; color: #fff !important; text-shadow: 0 1px 2px rgba(0,0,0,0.5) !important; } #redKingMusicPlayer { background: linear-gradient(145deg, #2a0e0e 0%, #1a0a0a 100%) !important; border: 2px solid #b22222 !important; border-radius: 10px !important; } #redKingMusicPlayer button { background: #b22222 !important; color: #fff !important; border: none !important; border-radius: 4px !important; padding: 4px 8px !important; cursor: pointer !important; } #redKingMusicPlayer button:hover { background: #c0392b !important; } .sprite-upload-btn { background: #8B0000 !important; } .sprite-reset-btn { background: #a83232 !important; } #redKingPanel h4, #redKingPanel h5 { color: #ffaaaa !important; } #redKingPanel input[type="color"] { border: 2px solid #b22222 !important; } `); console.log('🔴♔ Red King v2.4.1 iniciando...'); // Configuración por defecto mejorada const defaultConfig = { theme: 'red-king', soundEnabled: false, autoRotate: false, highlightMoves: false, showStats: false, autoAnalysis: false, customSounds: false, boardEffects: false, chatVisible: true, boardMoveable: false, // Nuevas opciones CORREGIDAS musicPlayer: { enabled: false, youtubeUrl: '', volume: 0.5, autoplay: false }, customSprites: { enabled: false, pieces: { 'white-king': '', 'white-queen': '', 'white-rook': '', 'white-bishop': '', 'white-knight': '', 'white-pawn': '', 'black-king': '', 'black-queen': '', 'black-rook': '', 'black-bishop': '', 'black-knight': '', 'black-pawn': '' } }, customColors: { primary: '#8B0000', secondary: '#DC143C', accent: '#FFD700', background: '#1a1a1a', text: '#ffffff', chatText: '#ffffff' }, buttonColors: { enabled: false, buttonPrimary: '#8B0000', buttonSecondary: '#DC143C', hoverPrimary: '#DC143C', hoverSecondary: '#8B0000', textColor: '#ffffff' }, shortcuts: { flipBoard: 'f', resign: 'r', takeback: 't', analysis: 'a', panel: 'p', toggleChat: 'c' }, userLinks: { discord: 'https://discord.gg/2mqdDJAZdq', github: 'https://github.com/gi4nxdepelover' } }; // Variables globales let config = GM_getValue('redKingConfig241', defaultConfig); let panelVisible = true; let currentTab = 'general'; let boardMoved = false; let originalBoardPosition = null; let boardDragListeners = []; let musicPlayerInstance = null; let customSpritesApplied = false; let youtubeAPIReady = false; // === SISTEMA DE AVISOS SUPERIORES === function showTopNotification(message, type = 'success', duration = 3000) { const notification = document.createElement('div'); notification.id = 'redKingTopNotification'; const typeStyles = { success: { bg: '#4CAF50', icon: '✅' }, error: { bg: '#f44336', icon: '❌' }, warning: { bg: '#ff9800', icon: '⚠️' }, info: { bg: '#2196F3', icon: 'ℹ️' }, saved: { bg: '#8BC34A', icon: '💾' } }; const style = typeStyles[type] || typeStyles.success; notification.style.cssText = ` position: fixed !important; top: 0 !important; left: 0 !important; right: 0 !important; background: ${style.bg} !important; color: white !important; padding: 12px 20px !important; text-align: center !important; z-index: 999999999 !important; font-family: 'Segoe UI', sans-serif !important; font-size: 14px !important; font-weight: 600 !important; box-shadow: 0 2px 10px rgba(0,0,0,0.3) !important; transform: translateY(-100%) !important; transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1) !important; `; notification.innerHTML = ` <span style="margin-right: 10px; font-size: 16px;">${style.icon}</span> ${message} `; document.body.appendChild(notification); // Animación de entrada setTimeout(() => { notification.style.transform = 'translateY(0)'; }, 50); // Animación de salida setTimeout(() => { notification.style.transform = 'translateY(-100%)'; setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 400); }, duration); } // === REPRODUCTOR DE MÚSICA YOUTUBE CORREGIDO === class YouTubeMusicPlayer { constructor() { this.player = null; this.playerReady = false; this.currentVideoId = ''; this.isPlaying = false; this.volume = 0.5; this.playerContainer = null; this.intervalId = null; } async initPlayer(videoUrl) { try { const videoId = this.extractVideoId(videoUrl); if (!videoId) { showTopNotification('❌ URL de YouTube inválido', 'error'); return false; } console.log('🎵 Inicializando reproductor con video ID:', videoId); this.currentVideoId = videoId; // Crear contenedor del reproductor this.createPlayerContainer(); // Cargar API de YouTube si no está cargada await this.loadYouTubeAPI(); // Esperar a que la API esté lista await this.waitForYouTubeAPI(); // Crear reproductor this.player = new YT.Player('redKingYouTubePlayer', { height: '0', width: '0', videoId: videoId, playerVars: { 'autoplay': config.musicPlayer.autoplay ? 1 : 0, 'controls': 0, 'disablekb': 1, 'fs': 0, 'modestbranding': 1, 'rel': 0, 'loop': 1, 'playlist': videoId }, events: { 'onReady': this.onPlayerReady.bind(this), 'onStateChange': this.onPlayerStateChange.bind(this), 'onError': this.onPlayerError.bind(this) } }); return true; } catch (error) { console.error('Error inicializando reproductor:', error); showTopNotification('❌ Error al inicializar reproductor de música', 'error'); return false; } } extractVideoId(url) { const regexes = [ /(?:youtube\.com\/watch\?v=)([^&\n?#]+)/, /(?:youtube\.com\/embed\/)([^&\n?#]+)/, /(?:youtu\.be\/)([^&\n?#]+)/, /(?:youtube\.com\/v\/)([^&\n?#]+)/, /(?:youtube\.com\/.*v=)([^&\n?#]+)/ ]; for (const regex of regexes) { const match = url.match(regex); if (match && match[1]) return match[1]; } return null; } async loadYouTubeAPI() { return new Promise((resolve) => { if (window.YT && window.YT.Player) { console.log('🎵 API de YouTube ya cargada'); youtubeAPIReady = true; resolve(); return; } console.log('🎵 Cargando API de YouTube...'); // Crear callback global window.onYouTubeIframeAPIReady = function() { console.log('🎵 API de YouTube lista'); youtubeAPIReady = true; resolve(); }; if (!document.querySelector('script[src*="youtube.com/iframe_api"]')) { const script = document.createElement('script'); script.src = 'https://www.youtube.com/iframe_api'; script.async = true; document.head.appendChild(script); } }); } async waitForYouTubeAPI() { return new Promise((resolve) => { if (window.YT && window.YT.Player) { resolve(); return; } const checkAPI = () => { if (window.YT && window.YT.Player) { resolve(); } else { setTimeout(checkAPI, 100); } }; checkAPI(); }); } createPlayerContainer() { if (this.playerContainer) { this.playerContainer.remove(); } this.playerContainer = document.createElement('div'); this.playerContainer.id = 'redKingMusicPlayer'; this.playerContainer.style.cssText = ` position: fixed !important; bottom: 20px !important; left: 20px !important; width: 320px !important; height: auto !important; background: linear-gradient(145deg, #1a1a1a 0%, #2d2d2d 100%) !important; border: 2px solid #8B0000 !important; border-radius: 10px !important; padding: 15px !important; color: white !important; font-family: 'Segoe UI', sans-serif !important; z-index: 999998 !important; box-shadow: 0 10px 25px rgba(0,0,0,0.7) !important; backdrop-filter: blur(10px) !important; `; this.playerContainer.innerHTML = ` <div style="margin-bottom: 10px; font-size: 14px; color: #FFD700; font-weight: bold;"> 🎵 Reproductor de Música YouTube </div> <div id="redKingYouTubePlayer"></div> <div id="musicControls" style="margin-top: 15px;"> <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;"> <button id="playPauseBtn" class="music-btn">▶️</button> <button id="prevBtn" class="music-btn">⏮️</button> <button id="nextBtn" class="music-btn">⏭️</button> <button id="stopBtn" class="music-btn">⏹️</button> </div> <div style="display: flex; align-items: center; margin-bottom: 10px;"> <span style="font-size: 12px; margin-right: 10px;">🔊</span> <input type="range" id="volumeSlider" min="0" max="100" value="50" style="flex: 1; margin-right: 10px;"> <span id="volumeValue" style="font-size: 12px; color: #FFD700; width: 35px;">50%</span> </div> <div style="display: flex; align-items: center; margin-bottom: 10px;"> <span style="font-size: 12px; margin-right: 10px;">⏳</span> <input type="range" id="progressSlider" min="0" max="100" value="0" style="flex: 1; margin-right: 10px;"> <span id="timeDisplay" style="font-size: 12px; color: #ccc; width: 80px;">0:00 / 0:00</span> </div> <div style="text-align: center;"> <button id="closeMusicPlayer" style="background: #ff4444; border: none; color: white; padding: 6px 12px; border-radius: 4px; font-size: 12px; cursor: pointer;">❌ Cerrar</button> </div> </div> `; document.body.appendChild(this.playerContainer); this.setupMusicControls(); } setupMusicControls() { const playPauseBtn = document.getElementById('playPauseBtn'); const prevBtn = document.getElementById('prevBtn'); const nextBtn = document.getElementById('nextBtn'); const stopBtn = document.getElementById('stopBtn'); const volumeSlider = document.getElementById('volumeSlider'); const progressSlider = document.getElementById('progressSlider'); const closeMusicPlayer = document.getElementById('closeMusicPlayer'); playPauseBtn?.addEventListener('click', () => this.togglePlayPause()); prevBtn?.addEventListener('click', () => this.seekRelative(-10)); nextBtn?.addEventListener('click', () => this.seekRelative(10)); stopBtn?.addEventListener('click', () => this.stop()); volumeSlider?.addEventListener('input', (e) => { const volume = e.target.value / 100; this.setVolume(volume); document.getElementById('volumeValue').textContent = e.target.value + '%'; }); progressSlider?.addEventListener('change', (e) => { if (this.player && this.playerReady) { const duration = this.player.getDuration(); const newTime = (e.target.value / 100) * duration; this.player.seekTo(newTime, true); } }); closeMusicPlayer?.addEventListener('click', () => this.destroy()); // Actualizar progreso cada segundo this.startProgressUpdate(); } startProgressUpdate() { if (this.intervalId) { clearInterval(this.intervalId); } this.intervalId = setInterval(() => this.updateProgress(), 1000); } onPlayerReady(event) { console.log('🎵 Reproductor listo'); this.playerReady = true; this.player.setVolume(this.volume * 100); showTopNotification('🎵 Reproductor de música listo', 'success'); } onPlayerStateChange(event) { const playPauseBtn = document.getElementById('playPauseBtn'); if (event.data === YT.PlayerState.PLAYING) { this.isPlaying = true; if (playPauseBtn) playPauseBtn.textContent = '⏸️'; } else if (event.data === YT.PlayerState.PAUSED || event.data === YT.PlayerState.ENDED) { this.isPlaying = false; if (playPauseBtn) playPauseBtn.textContent = '▶️'; } } onPlayerError(event) { console.error('Error en reproductor de YouTube:', event.data); showTopNotification('❌ Error en el reproductor de música', 'error'); } togglePlayPause() { if (!this.player || !this.playerReady) return; if (this.isPlaying) { this.player.pauseVideo(); } else { this.player.playVideo(); } } seekRelative(seconds) { if (!this.player || !this.playerReady) return; const currentTime = this.player.getCurrentTime(); const newTime = Math.max(0, currentTime + seconds); this.player.seekTo(newTime, true); } setVolume(volume) { this.volume = Math.max(0, Math.min(1, volume)); if (this.player && this.playerReady) { this.player.setVolume(this.volume * 100); } } stop() { if (this.player && this.playerReady) { this.player.stopVideo(); } } updateProgress() { if (!this.player || !this.playerReady || !document.getElementById('progressSlider')) return; try { const currentTime = this.player.getCurrentTime(); const duration = this.player.getDuration(); if (duration > 0) { const progress = (currentTime / duration) * 100; document.getElementById('progressSlider').value = progress; const timeDisplay = document.getElementById('timeDisplay'); if (timeDisplay) { timeDisplay.textContent = `${this.formatTime(currentTime)} / ${this.formatTime(duration)}`; } } } catch (error) { // Silenciar errores de actualización } } formatTime(seconds) { const minutes = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${minutes}:${secs.toString().padStart(2, '0')}`; } destroy() { if (this.intervalId) { clearInterval(this.intervalId); this.intervalId = null; } if (this.player) { try { this.player.destroy(); } catch (error) { console.warn('Error destroying player:', error); } this.player = null; } if (this.playerContainer) { this.playerContainer.remove(); this.playerContainer = null; } this.playerReady = false; this.isPlaying = false; showTopNotification('🎵 Reproductor de música cerrado', 'info'); } } // === SISTEMA DE SPRITES PERSONALIZADOS CORREGIDO === class CustomSpritesManager { constructor() { this.customStyleSheet = null; this.applied = false; } initCustomSprites() { console.log('🎨 Inicializando sprites personalizados...'); if (!this.customStyleSheet) { this.customStyleSheet = document.createElement('style'); this.customStyleSheet.id = 'redKingCustomSprites'; document.head.appendChild(this.customStyleSheet); } this.applyCustomSprites(); this.applied = true; } applyCustomSprites() { if (!config.customSprites.enabled) { this.removeCustomSprites(); return; } console.log('🎨 Aplicando sprites personalizados...'); let cssRules = ''; // Selectores CSS más específicos y compatibles con Lichess const pieceSelectors = { 'white-king': 'piece.white.king, .cg-board piece[data-color="white"][data-role="king"]', 'white-queen': 'piece.white.queen, .cg-board piece[data-color="white"][data-role="queen"]', 'white-rook': 'piece.white.rook, .cg-board piece[data-color="white"][data-role="rook"]', 'white-bishop': 'piece.white.bishop, .cg-board piece[data-color="white"][data-role="bishop"]', 'white-knight': 'piece.white.knight, .cg-board piece[data-color="white"][data-role="knight"]', 'white-pawn': 'piece.white.pawn, .cg-board piece[data-color="white"][data-role="pawn"]', 'black-king': 'piece.black.king, .cg-board piece[data-color="black"][data-role="king"]', 'black-queen': 'piece.black.queen, .cg-board piece[data-color="black"][data-role="queen"]', 'black-rook': 'piece.black.rook, .cg-board piece[data-color="black"][data-role="rook"]', 'black-bishop': 'piece.black.bishop, .cg-board piece[data-color="black"][data-role="bishop"]', 'black-knight': 'piece.black.knight, .cg-board piece[data-color="black"][data-role="knight"]', 'black-pawn': 'piece.black.pawn, .cg-board piece[data-color="black"][data-role="pawn"]' }; let hasCustomSprites = false; for (const [piece, selector] of Object.entries(pieceSelectors)) { const customSprite = config.customSprites.pieces[piece]; if (customSprite && customSprite.trim()) { cssRules += ` ${selector} { background-image: url('${customSprite}') !important; background-size: cover !important; background-repeat: no-repeat !important; background-position: center !important; } `; hasCustomSprites = true; console.log('🎨 Sprite aplicado para:', piece); } } if (this.customStyleSheet) { this.customStyleSheet.textContent = cssRules; console.log('🎨 CSS de sprites actualizado:', hasCustomSprites ? 'Con sprites' : 'Sin sprites'); } } removeCustomSprites() { console.log('🎨 Removiendo sprites personalizados...'); if (this.customStyleSheet) { this.customStyleSheet.textContent = ''; } } uploadSprite(pieceType) { const input = document.createElement('input'); input.type = 'file'; input.accept = 'image/*'; input.addEventListener('change', (event) => { const file = event.target.files[0]; if (!file) return; console.log('🎨 Subiendo sprite para:', pieceType); if (!file.type.startsWith('image/')) { showTopNotification('❌ Por favor selecciona un archivo de imagen', 'error'); return; } // Verificar tamaño del archivo (máximo 5MB) if (file.size > 5 * 1024 * 1024) { showTopNotification('❌ La imagen es muy grande (máximo 5MB)', 'error'); return; } const reader = new FileReader(); reader.onload = (e) => { const dataUrl = e.target.result; config.customSprites.pieces[pieceType] = dataUrl; GM_setValue('redKingConfig241', config); console.log('🎨 Sprite guardado para:', pieceType); // Actualizar vista previa const preview = document.getElementById(`preview_${pieceType.replace('-', '_')}`); if (preview) { preview.src = dataUrl; preview.style.display = 'block'; } // Aplicar sprites inmediatamente this.applyCustomSprites(); showTopNotification(`✅ Sprite de ${pieceType.replace('-', ' ')} guardado`, 'saved'); }; reader.onerror = () => { showTopNotification('❌ Error al leer el archivo', 'error'); }; reader.readAsDataURL(file); }); input.click(); } resetSprite(pieceType) { config.customSprites.pieces[pieceType] = ''; GM_setValue('redKingConfig241', config); const preview = document.getElementById(`preview_${pieceType.replace('-', '_')}`); if (preview) { preview.style.display = 'none'; preview.src = ''; } this.applyCustomSprites(); showTopNotification(`🔄 Sprite de ${pieceType.replace('-', ' ')} restablecido`, 'info'); } } // Instancias globales const spritesManager = new CustomSpritesManager(); // === FUNCIÓN DE VOLTEAR TABLERO SÚPER MEJORADA === function flipBoardAction() { console.log('🔄 Iniciando función de voltear tablero...'); try { let success = false; // MÉTODO 1: Detectar botón de flip de Lichess const flipButtons = [ '.game .game__buttons .fbt', '.analyse__tools .fbt', 'button.fbt', '.flip', 'button[title*="flip"]', 'button[title*="Flip"]', '.game__menu button[data-icon="B"]', '.lpv__fbt' ]; console.log('🔍 Buscando botones de flip...'); for (const selector of flipButtons) { const button = document.querySelector(selector); if (button && button.offsetParent !== null) { console.log('✅ Encontrado botón flip:', selector); button.click(); success = true; showTopNotification('🔄 Tablero volteado', 'success'); break; } } // MÉTODO 2: Si no encontró botón, usar eventos de teclado if (!success) { console.log('🎹 Intentando con eventos de teclado...'); // Enfocar el tablero primero const boardElement = document.querySelector('.cg-wrap, .game, .analyse'); if (boardElement) { boardElement.focus(); } // Crear y disparar evento keydown más completo const keyEvent = new KeyboardEvent('keydown', { key: 'f', code: 'KeyF', keyCode: 70, which: 70, charCode: 0, bubbles: true, cancelable: true, composed: true, view: window, detail: 0 }); // Disparar en múltiples elementos const targets = [ document, document.body, document.querySelector('.cg-wrap'), document.querySelector('.main-wrap'), document.querySelector('.game'), document.querySelector('.analyse') ].filter(el => el); targets.forEach(target => { try { target.dispatchEvent(keyEvent); } catch (e) { console.warn('Error dispatching to:', target, e); } }); // También keyup const keyUpEvent = new KeyboardEvent('keyup', { key: 'f', code: 'KeyF', keyCode: 70, which: 70, bubbles: true, cancelable: true }); targets.forEach(target => { try { target.dispatchEvent(keyUpEvent); } catch (e) { console.warn('Error dispatching keyup to:', target, e); } }); showTopNotification('🔄 Comando de volteo enviado', 'info'); success = true; } // MÉTODO 3: Como último recurso, CSS flip if (!success) { console.log('🎨 Intentando flip CSS...'); const board = document.querySelector('.cg-board, .cg-wrap'); if (board) { const currentTransform = board.style.transform || ''; if (currentTransform.includes('rotate(180deg)')) { board.style.transform = currentTransform.replace('rotate(180deg)', '').trim(); showTopNotification('🔄 Tablero normal (CSS)', 'success'); } else { board.style.transform = (currentTransform + ' rotate(180deg)').trim(); showTopNotification('🔄 Tablero volteado (CSS)', 'success'); } success = true; } } if (!success) { console.warn('⚠️ No se pudo voltear el tablero'); showTopNotification('⚠️ No se pudo voltear automáticamente - Intenta presionar "F"', 'warning'); } } catch (error) { console.error('❌ Error crítico al voltear tablero:', error); showTopNotification('❌ Error al voltear tablero', 'error'); } } // Crear panel principal mejorado function createPanel() { console.log('Creando panel Red King v2.4.1...'); if (document.getElementById('redKingPanel')) { document.getElementById('redKingPanel').remove(); } const panel = document.createElement('div'); panel.id = 'redKingPanel'; panel.style.cssText = ` position: fixed !important; top: 20px !important; right: 20px !important; width: 380px !important; max-height: 700px !important; background: linear-gradient(145deg, #1a1a1a 0%, #2d2d2d 100%) !important; border: 2px solid #8B0000 !important; border-radius: 15px !important; padding: 0 !important; color: white !important; font-family: 'Segoe UI', -apple-system, BlinkMacSystemFont, sans-serif !important; box-shadow: 0 15px 35px rgba(0,0,0,0.9), 0 0 20px rgba(139,0,0,0.3) !important; z-index: 999999 !important; overflow: hidden !important; backdrop-filter: blur(10px) !important; transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1) !important; `; panel.innerHTML = createPanelHTML(); document.body.appendChild(panel); setupPanelEvents(); makePanelDraggable(panel); showTopNotification('♔ Red King v2.4.1 activado! - Gracias por descargar RedKing <3', 'success', 3000); return panel; } // HTML del panel con nuevas opciones function createPanelHTML() { return ` <div id="panelHeader" style=" background: linear-gradient(135deg, #8B0000 0%, #DC143C 100%); padding: 15px; text-align: center; font-size: 18px; color: #FFD700; cursor: move; user-select: none; border-radius: 15px 15px 0 0; position: relative; "> <span style="font-weight: bold;">♔ Red King v2.4.1</span> <div style="position: absolute; top: 50%; right: 15px; transform: translateY(-50%); cursor: pointer; font-size: 16px;" id="minimizeBtn">−</div> </div> <div id="panelContent" style="padding: 0;"> <div id="tabNavigation" style=" display: flex; background: #2a2a2a; border-bottom: 1px solid #444; "> <div class="tab-btn active" data-tab="general">⚙️ General</div> <div class="tab-btn" data-tab="board">🏁 Tablero</div> <div class="tab-btn" data-tab="music">🎵 Música</div> <div class="tab-btn" data-tab="sprites">🎨 Sprites</div> <div class="tab-btn" data-tab="theme">🌈 Tema</div> </div> <div id="tabContent" style="padding: 20px; max-height: 480px; overflow-y: auto;"> ${createTabContent()} </div> </div> <div style=" background: #2a2a2a; padding: 12px; text-align: center; font-size: 11px; color: #888; border-radius: 0 0 15px 15px; border-top: 1px solid #444; "> <div style="margin-bottom: 8px;"> <button id="restoreNormal" style=" background: linear-gradient(135deg, #ff6b6b 0%, #ee5a24 100%); border: none; color: white; padding: 8px 16px; border-radius: 6px; cursor: pointer; font-size: 11px; font-weight: 500; margin-bottom: 8px; ">🔄 Restaurar Normalidad</button> </div> <div style="margin-bottom: 5px;"> <span id="closePanel" style="cursor: pointer; color: #DC143C; margin-right: 15px;">❌ Cerrar</span> <span id="resetConfig" style="cursor: pointer; color: #FFD700;">🔄 Reset Config</span> </div> <div>v2.4.1 - Hecho por Gi4nx con ❤️</div> </div> `; } // Contenido de pestañas mejorado function createTabContent() { return ` <!-- Pestaña General --> <div id="tab-general" class="tab-content" style="display: block;"> <div class="option-group"> <h4 style="color: #FFD700; margin: 0 0 15px 0; font-size: 14px;">🎮 Opciones Básicas</h4> ${createToggleOption('soundEnabled', 'Sonidos mejorados', '🔊')} ${createToggleOption('highlightMoves', 'Resaltar movimientos', '✨')} ${createToggleOption('boardEffects', 'Efectos de tablero', '💫')} </div> <div class="option-group" style="margin-top: 20px;"> <h4 style="color: #FFD700; margin: 0 0 15px 0; font-size: 14px;">⚡ Acciones Rápidas</h4> <div style="display: grid; gap: 8px;"> <button id="flipBoard" class="action-btn">🔄 Girar Tablero</button> <button id="quickAnalysis" class="action-btn">📊 Análisis Rápido</button> <button id="exportPGN" class="action-btn">📄 Exportar PGN</button> </div> </div> <div class="option-group" style="margin-top: 15px;"> <h4 style="color: #FFD700; margin: 0 0 10px 0; font-size: 12px;">⌨️ Atajos</h4> <div style="font-size: 11px; color: #ccc; line-height: 1.4;"> <kbd>F</kbd> Girar • <kbd>A</kbd> Análisis • <kbd>P</kbd> Panel • <kbd>C</kbd> Chat </div> </div> </div> <!-- Pestaña Tablero --> <div id="tab-board" class="tab-content" style="display: none;"> <div class="option-group"> <h4 style="color: #FFD700; margin: 0 0 15px 0; font-size: 14px;">🏁 Controles de Tablero</h4> <div style="margin-bottom: 15px; padding: 10px; background: rgba(255,215,0,0.1); border-radius: 6px; border: 1px solid #FFD700;"> <div style="font-size: 12px; color: #FFD700; margin-bottom: 8px;">📐 Estado del Tablero:</div> <div id="boardStatus" style="font-size: 11px; color: #ccc;">Normal (no movible)</div> </div> <div style="display: grid; gap: 8px;"> <button id="toggleBoardMove" class="action-btn">📐 Activar/Desactivar Mover</button> <button id="resetBoardPosition" class="action-btn">🎯 Restaurar Posición</button> <button id="toggleChat" class="action-btn">💬 Toggle Chat</button> <button id="hideChat" class="action-btn">👁️ Ocultar Chat</button> <button id="showChat" class="action-btn">👁️🗨️ Mostrar Chat</button> </div> </div> <div class="option-group" style="margin-top: 20px;"> <h4 style="color: #FFD700; margin: 0 0 15px 0; font-size: 14px;">🎨 Colores de Texto</h4> ${createColorPicker('customColors.text', '📄 Texto General')} ${createColorPicker('customColors.chatText', '💬 Texto Chat')} </div> <div class="option-group" style="margin-top: 15px;"> <button id="applyBoardTheme" class="action-btn">✨ Aplicar Cambios</button> </div> </div> <!-- Pestaña Música --> <div id="tab-music" class="tab-content" style="display: none;"> <div class="option-group"> <h4 style="color: #FFD700; margin: 0 0 15px 0; font-size: 14px;">🎵 Reproductor de Música<br> (¡OBSOLETO! Por favor leer el aviso mas abajo.)</h4> ${createToggleOption('musicPlayer.enabled', 'Activar reproductor', '🎵')} <div style="margin: 15px 0;"> <label style="display: block; margin-bottom: 5px; font-size: 12px; color: #FFD700;">🔗 URL de YouTube:</label> <input type="text" id="youtubeUrl" value="${config.musicPlayer.youtubeUrl}" placeholder="https://www.youtube.com/watch?v=..." style=" width: 100%; padding: 8px; border: 1px solid #FFD700; border-radius: 4px; background: #333; color: white; font-size: 12px; "> </div> <div style="margin: 15px 0;"> <label style="display: block; margin-bottom: 5px; font-size: 12px; color: #FFD700;">🔊 Volumen:</label> <input type="range" id="musicVolume" min="0" max="100" value="${Math.round(config.musicPlayer.volume * 100)}" style="width: 100%;"> <div style="text-align: center; font-size: 11px; color: #ccc; margin-top: 5px;" id="volumeDisplay">${Math.round(config.musicPlayer.volume * 100)}%</div> </div> ${createToggleOption('musicPlayer.autoplay', 'Reproducir automáticamente', '▶️')} <div style="margin-top: 15px; display: grid; gap: 8px;"> <button id="startMusicPlayer" class="action-btn">🎵 Iniciar Reproductor</button> <button id="stopMusicPlayer" class="action-btn">⏹️ Detener Reproductor</button> </div> </div> <div class="option-group" style="margin-top: 20px;"> <h4 style="color: #FFD700; margin: 0 0 10px 0; font-size: 14px;">ℹ️ Instrucciones</h4> <div style="font-size: 11px; color: #ccc; line-height: 1.4;"> 1. Pega el enlace de YouTube<br> 2. Ajusta el volumen deseado<br> 3. Activa el reproductor y presiona "Iniciar"<br> 4. Usa los controles para pausar, adelantar, etc.<br> <br> ADVERTENCIA, La sección de musica esta completamente obsoleta. No funciona y tiene varios errores, La nueva actualización que resuelva este problema "2.4.2" no llegará muy pronto. Mientras lo solucionamos puedes probar otras opciones. Gracias por leer<br> - Gi4nx </div> </div> </div> <!-- Pestaña Sprites --> <div id="tab-sprites" class="tab-content" style="display: none;"> <div class="option-group"> <h4 style="color: #FFD700; margin: 0 0 15px 0; font-size: 14px;">🎨 Sprites Personalizados</h4> ${createToggleOption('customSprites.enabled', 'Activar sprites personalizados', '🎨')} <div style="margin-top: 15px;"> <h5 style="color: #FFD700; margin: 0 0 10px 0; font-size: 13px;">⚪ Piezas Blancas</h5> ${createSpriteUploader('white-king', '♔ Rey')} ${createSpriteUploader('white-queen', '♕ Reina')} ${createSpriteUploader('white-rook', '♖ Torre')} ${createSpriteUploader('white-bishop', '♗ Alfil')} ${createSpriteUploader('white-knight', '♘ Caballo')} ${createSpriteUploader('white-pawn', '♙ Peón')} </div> <div style="margin-top: 15px;"> <h5 style="color: #FFD700; margin: 0 0 10px 0; font-size: 13px;">⚫ Piezas Negras</h5> ${createSpriteUploader('black-king', '♚ Rey')} ${createSpriteUploader('black-queen', '♛ Reina')} ${createSpriteUploader('black-rook', '♜ Torre')} ${createSpriteUploader('black-bishop', '♝ Alfil')} ${createSpriteUploader('black-knight', '♞ Caballo')} ${createSpriteUploader('black-pawn', '♟ Peón')} </div> <div style="margin-top: 15px; display: grid; gap: 8px;"> <button id="applySprites" class="action-btn">✨ Aplicar Sprites</button> <button id="resetAllSprites" class="action-btn">🔄 Restablecer Todos</button> </div> </div> <div class="option-group" style="margin-top: 20px;"> <h4 style="color: #FFD700; margin: 0 0 10px 0; font-size: 14px;">ℹ️ Instrucciones</h4> <div style="font-size: 11px; color: #ccc; line-height: 1.4;"> • Sube archivos PNG, JPG o GIF<br> • Recomendado: imágenes cuadradas<br> • Los cambios se aplican al instante<br> • Ejemplo: Rey = Patata frita 🍟 </div> </div> </div> <!-- Pestaña Tema --> <div id="tab-theme" class="tab-content" style="display: none;"> <div class="option-group"> <h4 style="color: #FFD700; margin: 0 0 15px 0; font-size: 14px;">🎨 Personalización</h4> ${createToggleOption('buttonColors.enabled', 'Botones rojos', '🎯')} </div> <div class="option-group" style="margin-top: 20px;"> <h4 style="color: #FFD700; margin: 0 0 15px 0; font-size: 14px;">🌈 Colores de Botones</h4> ${createColorPicker('buttonColors.buttonPrimary', '🔴 Primario')} ${createColorPicker('buttonColors.buttonSecondary', '🟥 Secundario')} ${createColorPicker('buttonColors.hoverPrimary', '🔺 Hover 1')} ${createColorPicker('buttonColors.hoverSecondary', '🔻 Hover 2')} </div> <div style="margin-top: 15px;"> <button id="applyTheme" class="action-btn">✨ Aplicar Tema</button> <button id="exportConfig" class="action-btn" style="margin-top: 8px;">💾 Exportar Config</button> </div> <div class="option-group" style="margin-top: 15px;"> <h4 style="color: #FFD700; margin: 0 0 15px 0; font-size: 14px;">🌐 Social</h4> <div style="display: grid; gap: 8px;"> <button id="joinDiscord" class="social-btn discord-btn">💬 Discord</button> <button id="visitGitHub" class="social-btn github-btn">🐱 GitHub</button> </div> </div> </div> `; } // Crear uploader de sprites function createSpriteUploader(pieceType, pieceName) { const currentSprite = config.customSprites.pieces[pieceType]; const previewId = `preview_${pieceType.replace('-', '_')}`; return ` <div style="display: flex; justify-content: space-between; align-items: center; margin: 8px 0; padding: 8px; background: rgba(255,255,255,0.05); border-radius: 4px;"> <span style="font-size: 12px; flex: 1;">${pieceName}:</span> <div style="display: flex; align-items: center; gap: 8px;"> <img id="${previewId}" src="${currentSprite}" style="width: 24px; height: 24px; display: ${currentSprite ? 'block' : 'none'}; border: 1px solid #FFD700; border-radius: 2px;"> <button class="sprite-upload-btn" data-piece="${pieceType}" style="background: #4CAF50; border: none; color: white; padding: 4px 8px; border-radius: 3px; font-size: 10px; cursor: pointer;">📁</button> <button class="sprite-reset-btn" data-piece="${pieceType}" style="background: #f44336; border: none; color: white; padding: 4px 8px; border-radius: 3px; font-size: 10px; cursor: pointer;">🗑️</button> </div> </div> `; } // Crear opción toggle function createToggleOption(configPath, label, icon) { const value = getNestedConfig(configPath); const id = configPath.replace(/\./g, '_'); return ` <div style="margin: 12px 0; display: flex; justify-content: space-between; align-items: center;"> <span style="font-size: 13px;"> <span style="margin-right: 8px;">${icon}</span>${label} </span> <label class="toggle-switch"> <input type="checkbox" id="${id}" ${value ? 'checked' : ''}> <span class="toggle-slider"></span> </label> </div> `; } // Crear selector de color function createColorPicker(configPath, label) { const value = getNestedConfig(configPath); const id = configPath.replace(/\./g, '_'); return ` <div style="display: flex; justify-content: space-between; align-items: center; margin: 10px 0; font-size: 12px;"> <span style="flex: 1;">${label}:</span> <input type="color" id="${id}" value="${value}" style=" width: 40px; height: 30px; border-radius: 6px; border: 2px solid #FFD700; cursor: pointer; background: none; margin-left: 10px; "> </div> `; } // Funciones de utilidad function getNestedConfig(path) { return path.split('.').reduce((obj, key) => obj && obj[key], config); } function setNestedConfig(path, value) { const keys = path.split('.'); const lastKey = keys.pop(); const target = keys.reduce((obj, key) => obj[key], config); target[lastKey] = value; } // === FUNCIONES DE TABLERO (mantenidas del código original que funciona) === // Toggle movimiento de tablero - MANTENER COMO ESTÁ function toggleBoardMoveable() { console.log('🔧 Toggle board moveable iniciado, boardMoved:', boardMoved); const boardSelectors = [ '.cg-wrap', '.board-wrap', 'main.game .cg-wrap', '.game .cg-wrap', '.analyse .cg-wrap', '.lpv .cg-wrap' ]; let board = null; for (const selector of boardSelectors) { board = document.querySelector(selector); if (board) { console.log('✅ Tablero encontrado con selector:', selector); break; } } if (!board) { showTopNotification('❌ No se encontró el tablero', 'error'); console.error('❌ No se encontró ningún tablero con los selectores disponibles'); return; } if (!boardMoved) { console.log('🔧 Activando modo arrastrable...'); // Guardar posición original usando getBoundingClientRect() para mayor precisión const rect = board.getBoundingClientRect(); const computedStyle = window.getComputedStyle(board); originalBoardPosition = { // Estilos directos del elemento position: board.style.position, left: board.style.left, top: board.style.top, right: board.style.right, bottom: board.style.bottom, transform: board.style.transform, zIndex: board.style.zIndex, cursor: board.style.cursor, // Posición computada como respaldo computedPosition: computedStyle.position, computedLeft: computedStyle.left, computedTop: computedStyle.top, computedRight: computedStyle.right, computedBottom: computedStyle.bottom, // Posición absoluta en viewport rect: { left: rect.left, top: rect.top, width: rect.width, height: rect.height } }; console.log('💾 Posición original guardada:', originalBoardPosition); // Aplicar estilos para hacer el tablero movible const currentPosition = computedStyle.position; if (currentPosition === 'static') { board.style.position = 'relative'; } else { // Si ya tiene position, convertir a absolute para mayor control board.style.position = 'absolute'; board.style.left = rect.left + 'px'; board.style.top = rect.top + 'px'; } board.style.cursor = 'grab'; board.style.zIndex = '1000'; console.log('✅ Estilos aplicados - tablero debe mantenerse visible'); makeBoardDraggable(board); boardMoved = true; config.boardMoveable = true; GM_setValue('redKingConfig241', config); showTopNotification('📐 Tablero ahora es movible', 'success'); updateBoardStatus(); console.log('✅ Modo arrastrable activado exitosamente'); } else { console.log('🔧 Desactivando modo arrastrable...'); // Desactivar modo arrastrable removeBoardDragListeners(); // Restaurar posición original con mayor precisión if (originalBoardPosition) { console.log('🔄 Restaurando posición original...'); // Restaurar estilos directos board.style.position = originalBoardPosition.position; board.style.left = originalBoardPosition.left; board.style.top = originalBoardPosition.top; board.style.right = originalBoardPosition.right; board.style.bottom = originalBoardPosition.bottom; board.style.transform = originalBoardPosition.transform; board.style.zIndex = originalBoardPosition.zIndex; board.style.cursor = originalBoardPosition.cursor; // Si los estilos directos están vacíos, limpiar completamente if (!originalBoardPosition.position) { board.style.removeProperty('position'); } if (!originalBoardPosition.left) { board.style.removeProperty('left'); } if (!originalBoardPosition.top) { board.style.removeProperty('top'); } if (!originalBoardPosition.transform) { board.style.removeProperty('transform'); } if (!originalBoardPosition.zIndex) { board.style.removeProperty('z-index'); } if (!originalBoardPosition.cursor) { board.style.removeProperty('cursor'); } } else { console.log('⚠️ No hay posición original guardada, usando reset básico'); // Fallback: limpiar todos los estilos de posicionamiento ['position', 'left', 'top', 'right', 'bottom', 'transform', 'z-index', 'cursor'].forEach(prop => { board.style.removeProperty(prop); }); } boardMoved = false; config.boardMoveable = false; GM_setValue('redKingConfig241', config); showTopNotification('🎯 Tablero fijo (no movible)', 'info'); updateBoardStatus(); console.log('✅ Modo arrastrable desactivado exitosamente'); } } // Hacer tablero arrastrable - MANTENER COMO ESTÁ function makeBoardDraggable(board) { console.log('🔧 Configurando tablero arrastrable...'); let isDragging = false; let startX, startY, initialLeft, initialTop; const startDrag = (e) => { if (e.button !== 0) return; if (e.target.classList.contains('cg-board') || e.target.classList.contains('cg-wrap') || e.target.tagName === 'CG-CONTAINER') { console.log('🎯 Iniciando arrastre del tablero...'); isDragging = true; startX = e.clientX; startY = e.clientY; const computedStyle = window.getComputedStyle(board); initialLeft = parseInt(computedStyle.left) || 0; initialTop = parseInt(computedStyle.top) || 0; console.log('📍 Posición inicial para arrastre:', { initialLeft, initialTop }); board.style.cursor = 'grabbing'; board.style.userSelect = 'none'; e.preventDefault(); e.stopPropagation(); } }; const drag = (e) => { if (!isDragging) return; e.preventDefault(); e.stopPropagation(); const deltaX = e.clientX - startX; const deltaY = e.clientY - startY; const newLeft = initialLeft + deltaX; const newTop = initialTop + deltaY; const margin = 50; const maxLeft = window.innerWidth - board.offsetWidth - margin; const maxTop = window.innerHeight - board.offsetHeight - margin; const boundedLeft = Math.max(-margin, Math.min(newLeft, maxLeft)); const boundedTop = Math.max(-margin, Math.min(newTop, maxTop)); board.style.left = boundedLeft + 'px'; board.style.top = boundedTop + 'px'; if (board.style.position !== 'absolute') { board.style.position = 'absolute'; } }; const stopDrag = (e) => { if (!isDragging) return; console.log('🎯 Finalizando arrastre del tablero...'); isDragging = false; board.style.cursor = 'grab'; board.style.userSelect = ''; }; board.addEventListener('mousedown', startDrag); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', stopDrag); boardDragListeners = [ { element: board, event: 'mousedown', listener: startDrag }, { element: document, event: 'mousemove', listener: drag }, { element: document, event: 'mouseup', listener: stopDrag } ]; console.log('✅ Tablero arrastrable configurado exitosamente'); } // Remover listeners del tablero function removeBoardDragListeners() { console.log('🧹 Removiendo listeners de arrastre...'); boardDragListeners.forEach(({ element, event, listener }) => { try { element.removeEventListener(event, listener); } catch (error) { console.warn('⚠️ Error removiendo listener:', error); } }); boardDragListeners = []; console.log('✅ Listeners removidos'); } // Solo resetear posición del tablero function resetBoardPositionOnly() { const boardSelectors = [ '.cg-wrap', '.board-wrap', 'main.game .cg-wrap', '.game .cg-wrap', '.analyse .cg-wrap', '.lpv .cg-wrap' ]; let board = null; for (const selector of boardSelectors) { board = document.querySelector(selector); if (board) break; } if (!board) { showTopNotification('❌ No se encontró el tablero', 'error'); return; } if (originalBoardPosition) { if (boardMoved) { const rect = originalBoardPosition.rect; board.style.left = rect.left + 'px'; board.style.top = rect.top + 'px'; } else { board.style.left = originalBoardPosition.left; board.style.top = originalBoardPosition.top; board.style.transform = originalBoardPosition.transform; } } else { const rect = board.getBoundingClientRect(); const centerX = (window.innerWidth - rect.width) / 2; const centerY = (window.innerHeight - rect.height) / 2; if (boardMoved) { board.style.left = centerX + 'px'; board.style.top = centerY + 'px'; } } showTopNotification('🎯 Posición del tablero restaurada', 'success'); } // Actualizar estado del tablero en la UI function updateBoardStatus() { const statusElement = document.getElementById('boardStatus'); if (statusElement) { if (boardMoved) { statusElement.innerHTML = '<span style="color: #4CAF50;">📐 Movible (No funcional)</span> - Arrastra desde áreas vacías del tablero'; statusElement.style.fontSize = '10px'; } else { statusElement.innerHTML = '<span style="color: #ccc;">🔒 Normal (no movible, No funcional)</span>'; statusElement.style.fontSize = '11px'; } } } // Configurar eventos del panel function setupPanelEvents() { console.log('Configurando eventos v2.4.1...'); // Navegación por pestañas document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', (e) => { const targetTab = e.target.dataset.tab; switchTab(targetTab); }); }); // Toggle switches document.querySelectorAll('input[type="checkbox"]').forEach(toggle => { toggle.addEventListener('change', (e) => { const configPath = e.target.id.replace(/_/g, '.'); const value = e.target.checked; setNestedConfig(configPath, value); GM_setValue('redKingConfig241', config); handleConfigChange(configPath, value); showAdvancedNotification(`${getOptionName(configPath)} ${value ? 'activado' : 'desactivado'}`, 'info', 1500); }); }); // Color pickers document.querySelectorAll('input[type="color"]').forEach(picker => { picker.addEventListener('change', (e) => { const configPath = e.target.id.replace(/_/g, '.'); const value = e.target.value; setNestedConfig(configPath, value); GM_setValue('redKingConfig241', config); if (configPath.includes('buttonColors')) { updateButtonStyles(); } else if (configPath.includes('customColors')) { applyTextColors(); } showAdvancedNotification('Color actualizado ✨', 'success', 1000); }); }); setupActionButtons(); setupMusicPlayerEvents(); setupSpritesEvents(); setupPanelControls(); } // Configurar eventos del reproductor de música function setupMusicPlayerEvents() { const youtubeUrlInput = document.getElementById('youtubeUrl'); if (youtubeUrlInput) { youtubeUrlInput.addEventListener('change', (e) => { config.musicPlayer.youtubeUrl = e.target.value; GM_setValue('redKingConfig241', config); showTopNotification('🔗 URL de YouTube guardada', 'saved'); }); } const musicVolumeSlider = document.getElementById('musicVolume'); const volumeDisplay = document.getElementById('volumeDisplay'); if (musicVolumeSlider && volumeDisplay) { musicVolumeSlider.addEventListener('input', (e) => { const volume = parseInt(e.target.value) / 100; config.musicPlayer.volume = volume; volumeDisplay.textContent = e.target.value + '%'; GM_setValue('redKingConfig241', config); if (musicPlayerInstance) { musicPlayerInstance.setVolume(volume); } }); } const startMusicBtn = document.getElementById('startMusicPlayer'); if (startMusicBtn) { startMusicBtn.addEventListener('click', startMusicPlayer); } const stopMusicBtn = document.getElementById('stopMusicPlayer'); if (stopMusicBtn) { stopMusicBtn.addEventListener('click', stopMusicPlayer); } } // Configurar eventos de sprites function setupSpritesEvents() { // Botones de upload document.querySelectorAll('.sprite-upload-btn').forEach(btn => { btn.addEventListener('click', (e) => { const pieceType = e.target.dataset.piece; spritesManager.uploadSprite(pieceType); }); }); // Botones de reset individual document.querySelectorAll('.sprite-reset-btn').forEach(btn => { btn.addEventListener('click', (e) => { const pieceType = e.target.dataset.piece; spritesManager.resetSprite(pieceType); }); }); // Aplicar sprites const applySpritesBtn = document.getElementById('applySprites'); if (applySpritesBtn) { applySpritesBtn.addEventListener('click', () => { spritesManager.applyCustomSprites(); showTopNotification('✨ Sprites aplicados manualmente', 'success'); }); } // Resetear todos los sprites const resetAllSpritesBtn = document.getElementById('resetAllSprites'); if (resetAllSpritesBtn) { resetAllSpritesBtn.addEventListener('click', () => { if (confirm('¿Restablecer todos los sprites personalizados?')) { Object.keys(config.customSprites.pieces).forEach(piece => { config.customSprites.pieces[piece] = ''; }); GM_setValue('redKingConfig241', config); document.querySelectorAll('[id^="preview_"]').forEach(preview => { preview.style.display = 'none'; }); spritesManager.removeCustomSprites(); showTopNotification('🔄 Todos los sprites restablecidos', 'info'); } }); } } // Funciones del reproductor de música CORREGIDAS async function startMusicPlayer() { const youtubeUrl = config.musicPlayer.youtubeUrl.trim(); if (!youtubeUrl) { showTopNotification('❌ Ingresa una URL de YouTube válida', 'error'); return; } if (!config.musicPlayer.enabled) { showTopNotification('❌ Activa el reproductor de música primero', 'warning'); return; } try { showTopNotification('🎵 Iniciando reproductor...', 'info'); if (musicPlayerInstance) { musicPlayerInstance.destroy(); musicPlayerInstance = null; } musicPlayerInstance = new YouTubeMusicPlayer(); const success = await musicPlayerInstance.initPlayer(youtubeUrl); if (!success) { musicPlayerInstance = null; } } catch (error) { console.error('Error iniciando reproductor:', error); showTopNotification('❌ Error al iniciar reproductor', 'error'); musicPlayerInstance = null; } } function stopMusicPlayer() { if (musicPlayerInstance) { musicPlayerInstance.destroy(); musicPlayerInstance = null; } else { showTopNotification('ℹ️ No hay reproductor activo', 'info'); } } // Configurar botones de acción function setupActionButtons() { // Botón girar tablero CORREGIDO const flipBoard = document.getElementById('flipBoard'); if (flipBoard) { flipBoard.addEventListener('click', flipBoardAction); } // Botón análisis rápido const quickAnalysis = document.getElementById('quickAnalysis'); if (quickAnalysis) { quickAnalysis.addEventListener('click', openAnalysis); } // Botón exportar PGN const exportPGN = document.getElementById('exportPGN'); if (exportPGN) { exportPGN.addEventListener('click', exportPGNAction); } // Botón mover tablero const toggleBoardMove = document.getElementById('toggleBoardMove'); if (toggleBoardMove) { toggleBoardMove.addEventListener('click', toggleBoardMoveable); } // Reset posición del tablero const resetBoardPosition = document.getElementById('resetBoardPosition'); if (resetBoardPosition) { resetBoardPosition.addEventListener('click', resetBoardPositionOnly); } // Controles de chat const toggleChat = document.getElementById('toggleChat'); if (toggleChat) { toggleChat.addEventListener('click', toggleChatVisibility); } const hideChat = document.getElementById('hideChat'); if (hideChat) { hideChat.addEventListener('click', () => hideChatFunction()); } const showChat = document.getElementById('showChat'); if (showChat) { showChat.addEventListener('click', () => showChatFunction()); } // Aplicar tema tablero const applyBoardTheme = document.getElementById('applyBoardTheme'); if (applyBoardTheme) { applyBoardTheme.addEventListener('click', () => { applyTextColors(); showTopNotification('✨ Cambios de tablero aplicados', 'success'); }); } // Aplicar tema general const applyTheme = document.getElementById('applyTheme'); if (applyTheme) { applyTheme.addEventListener('click', () => { applyCustomTheme(); showTopNotification('✨ Tema aplicado', 'success'); }); } // Exportar configuración const exportConfig = document.getElementById('exportConfig'); if (exportConfig) { exportConfig.addEventListener('click', exportConfiguration); } // Botones sociales const joinDiscord = document.getElementById('joinDiscord'); if (joinDiscord) { joinDiscord.addEventListener('click', () => { GM_openInTab(config.userLinks.discord, false); showTopNotification('💬 Abriendo Discord...', 'info'); }); } const visitGitHub = document.getElementById('visitGitHub'); if (visitGitHub) { visitGitHub.addEventListener('click', () => { GM_openInTab(config.userLinks.github, false); showTopNotification('🐱 Abriendo GitHub...', 'info'); }); } } // Configurar controles del panel function setupPanelControls() { const minimizeBtn = document.getElementById('minimizeBtn'); if (minimizeBtn) { minimizeBtn.addEventListener('click', togglePanelSize); } const closePanel = document.getElementById('closePanel'); if (closePanel) { closePanel.addEventListener('click', () => { const panel = document.getElementById('redKingPanel'); if (panel) { panel.style.transform = 'scale(0)'; setTimeout(() => panel.remove(), 300); } }); } const resetConfig = document.getElementById('resetConfig'); if (resetConfig) { resetConfig.addEventListener('click', resetConfiguration); } const restoreNormal = document.getElementById('restoreNormal'); if (restoreNormal) { restoreNormal.addEventListener('click', restoreToNormal); } } // Cambiar pestañas function switchTab(tabName) { document.querySelectorAll('.tab-btn').forEach(btn => { btn.classList.remove('active'); btn.style.cssText = ` flex: 1; padding: 12px 6px; text-align: center; cursor: pointer; font-size: 10px; border-right: 1px solid #444; transition: all 0.3s; background: #3a3a3a; color: #ccc; `; }); const activeBtn = document.querySelector(`.tab-btn[data-tab="${tabName}"]`); if (activeBtn) { activeBtn.classList.add('active'); activeBtn.style.cssText = ` flex: 1; padding: 12px 6px; text-align: center; cursor: pointer; font-size: 10px; border-right: 1px solid #444; transition: all 0.3s; background: #8B0000; color: #FFD700; `; } document.querySelectorAll('.tab-content').forEach(content => { content.style.display = 'none'; }); const activeContent = document.getElementById(`tab-${tabName}`); if (activeContent) { activeContent.style.display = 'block'; } currentTab = tabName; if (tabName === 'board') { updateBoardStatus(); } } // Manejar cambios de configuración function handleConfigChange(configPath, value) { switch (configPath) { case 'buttonColors.enabled': updateButtonStyles(); break; case 'boardEffects': toggleBoardEffects(value); break; case 'highlightMoves': toggleHighlightMoves(value); break; case 'soundEnabled': toggleSounds(value); break; case 'customSprites.enabled': if (value) { spritesManager.initCustomSprites(); } else { spritesManager.removeCustomSprites(); } break; case 'musicPlayer.enabled': if (!value && musicPlayerInstance) { stopMusicPlayer(); } break; } } // Obtener nombre legible de la opción function getOptionName(configPath) { const names = { 'soundEnabled': 'Sonidos', 'highlightMoves': 'Resaltado', 'boardEffects': 'Efectos', 'buttonColors.enabled': 'Botones rojos', 'musicPlayer.enabled': 'Reproductor de música', 'musicPlayer.autoplay': 'Reproducción automática', 'customSprites.enabled': 'Sprites personalizados' }; return names[configPath] || configPath; } // === FUNCIONES DE ACCIÓN === // Análisis rápido function openAnalysis() { try { const gameId = extractGameId(); const currentUrl = window.location.href; if (gameId && gameId.length >= 8) { const analysisUrl = `https://lichess.org/analysis/${gameId}`; GM_openInTab(analysisUrl, false); showTopNotification('📊 Abriendo análisis de partida', 'success'); } else if (currentUrl.includes('/game/')) { const gameMatch = currentUrl.match(/\/game\/([a-zA-Z0-9]{8,})/); if (gameMatch) { const analysisUrl = `https://lichess.org/analysis/${gameMatch[1]}`; GM_openInTab(analysisUrl, false); showTopNotification('📊 Abriendo análisis', 'success'); } else { GM_openInTab('https://lichess.org/analysis', false); showTopNotification('📊 Abriendo tablero de análisis', 'info'); } } else { GM_openInTab('https://lichess.org/analysis', false); showTopNotification('📊 Abriendo tablero de análisis', 'info'); } } catch (error) { console.error('Error al abrir análisis:', error); showTopNotification('❌ Error al abrir análisis', 'error'); } } // Exportar PGN function exportPGNAction() { try { const gameId = extractGameId(); if (gameId && gameId.length >= 8) { const pgnUrl = `https://lichess.org/game/export/${gameId}.pgn`; fetch(pgnUrl) .then(response => response.text()) .then(pgnData => { const blob = new Blob([pgnData], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `lichess_${gameId}.pgn`; link.style.display = 'none'; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); showTopNotification('📄 PGN descargado', 'success'); }) .catch(() => { const link = document.createElement('a'); link.href = pgnUrl; link.download = `lichess_${gameId}.pgn`; link.click(); showTopNotification('📄 Descargando PGN...', 'success'); }); } else { showTopNotification('❌ No se encontró ID de partida válido', 'error'); } } catch (error) { console.error('Error al exportar PGN:', error); showTopNotification('❌ Error al exportar PGN', 'error'); } } // === FUNCIONES DE CHAT === function toggleChatVisibility() { const chatSelectors = ['.mchat', '.chat', '.game__chat', '#chat', '.lpv__chat', '.chat-wrap']; let chatFound = false; for (const selector of chatSelectors) { const chatElement = document.querySelector(selector); if (chatElement) { if (chatElement.style.display === 'none') { chatElement.style.display = ''; config.chatVisible = true; showTopNotification('👁️🗨️ Chat mostrado', 'success'); } else { chatElement.style.display = 'none'; config.chatVisible = false; showTopNotification('👁️ Chat oculto', 'info'); } GM_setValue('redKingConfig241', config); chatFound = true; break; } } if (!chatFound) { showTopNotification('❌ No se encontró el chat', 'error'); } } function hideChatFunction() { const chatSelectors = ['.mchat', '.chat', '.game__chat', '#chat', '.lpv__chat', '.chat-wrap']; let hidden = false; for (const selector of chatSelectors) { const chatElement = document.querySelector(selector); if (chatElement) { chatElement.style.display = 'none'; config.chatVisible = false; GM_setValue('redKingConfig241', config); hidden = true; } } if (hidden) { showTopNotification('👁️ Chat oculto', 'info'); } else { showTopNotification('❌ No se encontró el chat', 'error'); } } function showChatFunction() { const chatSelectors = ['.mchat', '.chat', '.game__chat', '#chat', '.lpv__chat', '.chat-wrap']; let shown = false; for (const selector of chatSelectors) { const chatElement = document.querySelector(selector); if (chatElement) { chatElement.style.display = ''; config.chatVisible = true; GM_setValue('redKingConfig241', config); shown = true; } } if (shown) { showTopNotification('👁️🗨️ Chat mostrado', 'success'); } else { showTopNotification('❌ No se encontró el chat', 'error'); } } // Extraer ID de partida function extractGameId() { const url = window.location.href; const pathname = window.location.pathname; const patterns = [ /\/([a-zA-Z0-9]{8})(?:\/|$|\?|#)/, /\/([a-zA-Z0-9]{12})(?:\/|$|\?|#)/, /\/game\/export\/([a-zA-Z0-9]{8,})/, /\/analysis\/([a-zA-Z0-9]{8,})/ ]; for (const pattern of patterns) { const match = pathname.match(pattern) || url.match(pattern); if (match && match[1]) { return match[1]; } } return null; } // === FUNCIONES DE TEMA === function toggleBoardEffects(enabled) { const effectsStyle = document.getElementById('redKingBoardEffectsStyle') || document.createElement('style'); effectsStyle.id = 'redKingBoardEffectsStyle'; if (enabled) { effectsStyle.textContent = ` .cg-board square.last-move { background-color: rgba(220, 20, 60, 0.3) !important; box-shadow: inset 0 0 10px rgba(220, 20, 60, 0.5) !important; } .cg-board square.selected { background-color: rgba(255, 215, 0, 0.5) !important; box-shadow: inset 0 0 15px rgba(255, 215, 0, 0.7) !important; } .cg-board square.move-dest { background: radial-gradient(circle, rgba(255,215,0,0.3) 0%, transparent 60%) !important; } .cg-board square.check { background-color: rgba(220, 20, 60, 0.6) !important; animation: redKingCheckPulse 1s ease-in-out infinite alternate !important; } @keyframes redKingCheckPulse { 0% { background-color: rgba(220, 20, 60, 0.6) !important; } 100% { background-color: rgba(220, 20, 60, 0.8) !important; } } `; } else { effectsStyle.textContent = ''; } if (!effectsStyle.parentNode) { document.head.appendChild(effectsStyle); } } function toggleHighlightMoves(enabled) { const highlightStyle = document.getElementById('redKingHighlightStyle') || document.createElement('style'); highlightStyle.id = 'redKingHighlightStyle'; if (enabled) { highlightStyle.textContent = ` .cg-board square.move-dest, .cg-board square.premove-dest { background: radial-gradient(circle, rgba(255,215,0,0.4) 20%, transparent 50%) !important; border: 2px solid rgba(255,215,0,0.6) !important; } .cg-board square.oc.move-dest { background: radial-gradient(circle, rgba(220,20,60,0.4) 20%, transparent 50%) !important; border: 2px solid rgba(220,20,60,0.6) !important; } `; } else { highlightStyle.textContent = ''; } if (!highlightStyle.parentNode) { document.head.appendChild(highlightStyle); } } function toggleSounds(enabled) { const soundStyle = document.getElementById('redKingSoundStyle') || document.createElement('style'); soundStyle.id = 'redKingSoundStyle'; if (enabled) { soundStyle.textContent = ` .sound-enabled { transition: all 0.2s ease !important; } `; } else { soundStyle.textContent = ''; } if (!soundStyle.parentNode) { document.head.appendChild(soundStyle); } } function applyTextColors() { const textColorStyle = document.getElementById('redKingTextColorStyle') || document.createElement('style'); textColorStyle.id = 'redKingTextColorStyle'; textColorStyle.textContent = ` .mchat, .mchat .messages, .chat, .game__chat { color: ${config.customColors.chatText} !important; } .mchat .message, .chat .message { color: ${config.customColors.chatText} !important; } `; if (!textColorStyle.parentNode) { document.head.appendChild(textColorStyle); } } function updateButtonStyles() { if (!config.buttonColors.enabled) { const existingStyle = document.getElementById('redKingButtonStyles'); if (existingStyle) existingStyle.remove(); return; } const buttonStyle = document.getElementById('redKingButtonStyles') || document.createElement('style'); buttonStyle.id = 'redKingButtonStyles'; buttonStyle.textContent = ` .button, .form3-submit, .lobby__box .button, .lobby__start .button, .game__menu .button, .analyse__tools .button, .board-wrap .button, button.button, input[type="submit"], .game .button { background: linear-gradient(135deg, ${config.buttonColors.buttonPrimary} 0%, ${config.buttonColors.buttonSecondary} 100%) !important; border: none !important; color: ${config.buttonColors.textColor} !important; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; box-shadow: 0 4px 15px rgba(0,0,0,0.3) !important; } .button:hover, .form3-submit:hover, .lobby__box .button:hover, .lobby__start .button:hover, .game__menu .button:hover, .analyse__tools .button:hover, .board-wrap .button:hover, button.button:hover, input[type="submit"]:hover, .game .button:hover { transform: translateY(-2px) !important; box-shadow: 0 6px 20px rgba(0,0,0,0.4) !important; background: linear-gradient(135deg, ${config.buttonColors.hoverPrimary} 0%, ${config.buttonColors.hoverSecondary} 100%) !important; } `; if (!buttonStyle.parentNode) { document.head.appendChild(buttonStyle); } } function applyCustomTheme() { toggleBoardEffects(config.boardEffects); toggleHighlightMoves(config.highlightMoves); toggleSounds(config.soundEnabled); applyTextColors(); updateButtonStyles(); if (config.customSprites.enabled) { spritesManager.initCustomSprites(); } } // === RESTAURAR NORMALIDAD === function restoreToNormal() { if (!confirm('¿Restaurar Lichess a su estado completamente normal?\n\nEsto desactivará TODOS los cambios de Red King incluyendo música y sprites.')) { return; } try { // 1. Detener reproductor de música if (musicPlayerInstance) { musicPlayerInstance.destroy(); musicPlayerInstance = null; } // 2. Desactivar sprites personalizados spritesManager.removeCustomSprites(); // 3. Desactivar movimiento de tablero if (boardMoved) { const boardSelectors = [ '.cg-wrap', '.board-wrap', 'main.game .cg-wrap', '.game .cg-wrap', '.analyse .cg-wrap', '.lpv .cg-wrap' ]; let board = null; for (const selector of boardSelectors) { board = document.querySelector(selector); if (board) break; } if (board && originalBoardPosition) { removeBoardDragListeners(); ['position', 'left', 'top', 'right', 'bottom', 'transform', 'z-index', 'cursor'].forEach(prop => { board.style.removeProperty(prop); }); } boardMoved = false; } // 4. Remover todos los estilos personalizados const stylesToRemove = [ 'redKingBoardEffectsStyle', 'redKingHighlightStyle', 'redKingSoundStyle', 'redKingTextColorStyle', 'redKingButtonStyles', 'redKingCustomSprites' ]; stylesToRemove.forEach(styleId => { const style = document.getElementById(styleId); if (style) style.remove(); }); // 5. Mostrar todo el chat const chatSelectors = ['.mchat', '.chat', '.game__chat', '#chat', '.lpv__chat', '.chat-wrap']; chatSelectors.forEach(selector => { const chatElement = document.querySelector(selector); if (chatElement) { chatElement.style.display = ''; } }); // 6. Resetear configuración completa config = JSON.parse(JSON.stringify(defaultConfig)); GM_setValue('redKingConfig241', config); // 7. Actualizar UI del panel document.querySelectorAll('input[type="checkbox"]').forEach(checkbox => { const configPath = checkbox.id.replace(/_/g, '.'); checkbox.checked = getNestedConfig(configPath); }); document.querySelectorAll('input[type="color"]').forEach(colorPicker => { const configPath = colorPicker.id.replace(/_/g, '.'); colorPicker.value = getNestedConfig(configPath); }); const youtubeUrlInput = document.getElementById('youtubeUrl'); if (youtubeUrlInput) youtubeUrlInput.value = ''; const musicVolumeSlider = document.getElementById('musicVolume'); if (musicVolumeSlider) musicVolumeSlider.value = 50; const volumeDisplay = document.getElementById('volumeDisplay'); if (volumeDisplay) volumeDisplay.textContent = '50%'; updateBoardStatus(); showTopNotification('🔄 ¡Lichess restaurado a normalidad completa!', 'success', 4000); } catch (error) { console.error('Error al restaurar normalidad:', error); showTopNotification('❌ Error al restaurar normalidad', 'error'); } } // === FUNCIONES UTILITARIAS === function togglePanelSize() { const panel = document.getElementById('redKingPanel'); const content = document.getElementById('panelContent'); const btn = document.getElementById('minimizeBtn'); if (panelVisible) { content.style.display = 'none'; panel.style.height = 'auto'; btn.textContent = '+'; panelVisible = false; } else { content.style.display = 'block'; btn.textContent = '−'; panelVisible = true; } } function makePanelDraggable(panel) { let isDragging = false; let startX, startY, startLeft, startTop; const header = document.getElementById('panelHeader'); if (!header) return; header.addEventListener('mousedown', startDrag); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', stopDrag); function startDrag(e) { if (e.target.id === 'minimizeBtn') return; isDragging = true; startX = e.clientX; startY = e.clientY; const rect = panel.getBoundingClientRect(); startLeft = rect.left; startTop = rect.top; header.style.cursor = 'grabbing'; panel.style.zIndex = '9999999'; panel.style.transition = 'none'; } function drag(e) { if (!isDragging) return; e.preventDefault(); const deltaX = e.clientX - startX; const deltaY = e.clientY - startY; let newLeft = startLeft + deltaX; let newTop = startTop + deltaY; const margin = 10; newLeft = Math.max(margin, Math.min(newLeft, window.innerWidth - panel.offsetWidth - margin)); newTop = Math.max(margin, Math.min(newTop, window.innerHeight - panel.offsetHeight - margin)); panel.style.left = newLeft + 'px'; panel.style.top = newTop + 'px'; panel.style.right = 'auto'; } function stopDrag() { if (!isDragging) return; isDragging = false; header.style.cursor = 'move'; panel.style.zIndex = '999999'; panel.style.transition = 'all 0.4s cubic-bezier(0.4, 0, 0.2, 1)'; } } function exportConfiguration() { try { const configData = JSON.stringify(config, null, 2); const blob = new Blob([configData], { type: 'application/json' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `red_king_v241_config_${new Date().toISOString().slice(0, 10)}.json`; link.click(); URL.revokeObjectURL(url); showTopNotification('💾 Configuración exportada', 'success'); } catch (error) { console.error('Error al exportar configuración:', error); showTopNotification('❌ Error al exportar', 'error'); } } function resetConfiguration() { if (confirm('¿Restaurar configuración por defecto?\n\nEsto eliminará todas las personalizaciones incluyendo sprites y música.')) { if (musicPlayerInstance) { musicPlayerInstance.destroy(); musicPlayerInstance = null; } spritesManager.removeCustomSprites(); config = JSON.parse(JSON.stringify(defaultConfig)); GM_setValue('redKingConfig241', config); showTopNotification('🔄 Configuración restaurada', 'success'); setTimeout(() => location.reload(), 1500); } } function showAdvancedNotification(message, type = 'info', duration = 3000) { const notification = document.createElement('div'); const typeStyles = { success: { bg: 'linear-gradient(135deg, #4CAF50 0%, #45a049 100%)', icon: '✅' }, error: { bg: 'linear-gradient(135deg, #f44336 0%, #d32f2f 100%)', icon: '❌' }, warning: { bg: 'linear-gradient(135deg, #ff9800 0%, #f57c00 100%)', icon: '⚠️' }, info: { bg: 'linear-gradient(135deg, #8B0000 0%, #DC143C 100%)', icon: 'ℹ️' } }; const style = typeStyles[type]; notification.style.cssText = ` position: fixed !important; top: 20px !important; left: 50% !important; transform: translateX(-50%) translateY(-100px) !important; background: ${style.bg} !important; color: white !important; padding: 12px 20px !important; border-radius: 8px !important; z-index: 10000000 !important; font-size: 13px !important; font-weight: 500 !important; box-shadow: 0 10px 30px rgba(0,0,0,0.5) !important; backdrop-filter: blur(10px) !important; max-width: 350px !important; text-align: center !important; animation: slideIn 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards !important; `; notification.innerHTML = ` <span style="margin-right: 8px; font-size: 14px;">${style.icon}</span> ${message} `; if (!document.getElementById('redKingNotificationAnimations')) { const animations = document.createElement('style'); animations.id = 'redKingNotificationAnimations'; animations.textContent = ` @keyframes slideIn { to { transform: translateX(-50%) translateY(0); } } @keyframes slideOut { to { transform: translateX(-50%) translateY(-100px); opacity: 0; } } `; document.head.appendChild(animations); } document.body.appendChild(notification); setTimeout(() => { notification.style.animation = 'slideOut 0.5s cubic-bezier(0.4, 0, 0.2, 1) forwards'; setTimeout(() => notification.remove(), 500); }, duration); } function setupKeyboardShortcuts() { document.addEventListener('keydown', (e) => { if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) { return; } const key = e.key.toLowerCase(); if (e.ctrlKey || e.altKey || e.metaKey) { return; } switch (key) { case 'f': e.preventDefault(); flipBoardAction(); break; case 'a': e.preventDefault(); openAnalysis(); break; case 'p': e.preventDefault(); togglePanel(); break; case 'c': e.preventDefault(); toggleChatVisibility(); break; } }); } function togglePanel() { const panel = document.getElementById('redKingPanel'); if (panel) { if (panel.style.display === 'none') { panel.style.display = 'block'; showTopNotification('♔ Panel mostrado', 'info'); } else { panel.style.display = 'none'; showTopNotification('♔ Panel oculto', 'info'); } } else { createPanel(); } } function addCustomStyles() { GM_addStyle(` .tab-btn { flex: 1; padding: 12px 6px; text-align: center; cursor: pointer; font-size: 10px; border-right: 1px solid #444; transition: all 0.3s; background: #3a3a3a; color: #ccc; } .tab-btn:last-child { border-right: none; } .action-btn, .music-btn { background: linear-gradient(135deg, #8B0000 0%, #DC143C 100%) !important; border: none !important; color: white !important; padding: 10px !important; border-radius: 6px !important; cursor: pointer !important; font-size: 12px !important; font-weight: 500 !important; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; width: 100% !important; box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important; } .music-btn { width: auto !important; padding: 8px 12px !important; font-size: 14px !important; margin: 2px !important; } .action-btn:hover, .music-btn:hover { transform: translateY(-1px) !important; box-shadow: 0 4px 12px rgba(0,0,0,0.4) !important; background: linear-gradient(135deg, #DC143C 0%, #8B0000 100%) !important; } .social-btn { background: linear-gradient(135deg, #4a4a4a 0%, #6a6a6a 100%) !important; border: none !important; color: white !important; padding: 12px 20px !important; border-radius: 6px !important; cursor: pointer !important; font-size: 13px !important; font-weight: 500 !important; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; width: 100% !important; box-shadow: 0 2px 8px rgba(0,0,0,0.3) !important; margin: 4px 0 !important; } .discord-btn:hover { background: linear-gradient(135deg, #5865F2 0%, #4752C4 100%) !important; transform: translateY(-1px) !important; } .github-btn:hover { background: linear-gradient(135deg, #333 0%, #24292e 100%) !important; transform: translateY(-1px) !important; } .toggle-switch { position: relative !important; display: inline-block !important; width: 44px !important; height: 22px !important; } .toggle-switch input { opacity: 0 !important; width: 0 !important; height: 0 !important; } .toggle-slider { position: absolute !important; cursor: pointer !important; top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important; background-color: #333 !important; transition: 0.3s !important; border-radius: 22px !important; } .toggle-slider:before { position: absolute !important; content: "" !important; height: 16px !important; width: 16px !important; left: 3px !important; bottom: 3px !important; background-color: white !important; transition: 0.3s !important; border-radius: 50% !important; } input:checked + .toggle-slider { background: linear-gradient(135deg, #8B0000 0%, #DC143C 100%) !important; } input:checked + .toggle-slider:before { transform: translateX(22px) !important; } #redKingPanel *::-webkit-scrollbar { width: 6px !important; } #redKingPanel *::-webkit-scrollbar-track { background: #2a2a2a !important; } #redKingPanel *::-webkit-scrollbar-thumb { background: linear-gradient(135deg, #8B0000 0%, #DC143C 100%) !important; border-radius: 3px !important; } kbd { background: #333 !important; border: 1px solid #555 !important; border-radius: 3px !important; color: #FFD700 !important; padding: 1px 4px !important; font-size: 10px !important; font-family: monospace !important; margin: 0 1px !important; } `); } // Función de inicialización principal function initialize() { console.log('🔴♔ Inicializando Red King v2.4.1...'); addCustomStyles(); setTimeout(() => { try { const panel = createPanel(); if (panel) { console.log('✅ Panel v2.4.1 creado exitosamente'); setTimeout(() => { setupKeyboardShortcuts(); // Aplicar tema si hay opciones activadas if (config.boardEffects || config.highlightMoves || config.buttonColors.enabled || config.customSprites.enabled) { applyCustomTheme(); } console.log('✅ Red King v2.4.1 completamente iniciado'); }, 500); } } catch (error) { console.error('Error al inicializar Red King v2.4.1:', error); showTopNotification('❌ Error al iniciar Red King', 'error'); } }, 1000); } // Manejar cambios de página let currentUrl = window.location.href; function handlePageChange() { if (window.location.href !== currentUrl) { currentUrl = window.location.href; console.log('Consolelog no disponible en tu país'); setTimeout(() => { if (config.boardEffects || config.highlightMoves || config.buttonColors.enabled || config.customSprites.enabled) { applyCustomTheme(); } }, 1000); } } // Observer para cambios en la página const observer = new MutationObserver(handlePageChange); observer.observe(document.body, { childList: true, subtree: true }); // Inicializar if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initialize); } else { initialize(); } // Backup de inicialización setTimeout(() => { if (!document.getElementById('redKingPanel')) { console.log('Backup: Re-inicializando Red King v2.4.1'); initialize(); } }, 5000); console.log('Red King v2.4.1 Iniciado'); })();