您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Hornet from Hollow Knight Silksong follows your character around in Drawaria with authentic sounds and dynamic animations!
// ==UserScript== // @name Drawaria x Hollow Knight Silksong - Hornet Follower // @namespace http://tampermonkey.net/ // @version 1.0 // @description Hornet from Hollow Knight Silksong follows your character around in Drawaria with authentic sounds and dynamic animations! // @author YouTubeDrawaria // @match https://drawaria.online/* // @grant none // @license MIT // @icon https://ih1.redbubble.net/image.2501187840.3535/pp,504x498-pad,600x600,f8f8f8.u1.jpg // ==/UserScript== (function() { 'use strict'; /* ---------- PREVENCIÓN DE MÚLTIPLES INSTANCIAS ---------- */ if (window.hornetCompanionLoaded) { console.log('🕷️ Hornet Companion already loaded, skipping...'); return; } window.hornetCompanionLoaded = true; /* ---------- CONFIGURACIÓN SEGURA DE HORNET ---------- */ const HORNET_CONFIG = { imageUrl: 'https://i.ibb.co/jZgRrT4V/ezgif-1b3e16ce996509.gif', collisionImageUrl: 'https://i.ibb.co/cKs6VfhW/hollow-knight-silksong-doodle.gif', size: 240, speed: 1.8, followDistance: 60, collisionDistance: 50, // Distancia para detectar colisión soundInterval: 5000, bounceAmplitude: 5, bounceSpeed: 0.1, maxRetries: 3 }; const HORNET_SOUNDS = [ 'https://www.myinstants.com/media/sounds/hollow-knight-hornet-voice-3-2.mp3', 'https://www.myinstants.com/media/sounds/hollow-knight-hornet-voice-4-2.mp3', 'https://www.myinstants.com/media/sounds/hollow-knight-hornet-voice-9.mp3', 'https://www.myinstants.com/media/sounds/hollow-knight-hornet-voice-11.mp3', 'https://www.myinstants.com/media/sounds/hornet_edino.mp3', 'https://www.myinstants.com/media/sounds/hollow-knight-hornet-voice-2-3.mp3', 'https://www.myinstants.com/media/sounds/hollow-knight-hornet-voice-10.mp3' ]; /* ---------- VARIABLES GLOBALES SEGURAS ---------- */ let hornetElement = null; let targetPlayer = null; let animationFrame = null; let soundTimeout = null; let hornetPosition = { x: 100, y: 100 }; let bounceOffset = 0; let isActive = false; let retryCount = 0; let lastKnownPosition = { x: 100, y: 100 }; let isColliding = false; // Nueva variable para estado de colisión let currentAnimation = 'following'; // 'following' o 'colliding' /* ---------- FUNCIONES DE AUDIO SEGURAS ---------- */ function safePreloadSounds() { try { HORNET_SOUNDS.forEach((url, index) => { setTimeout(() => { try { const audio = new Audio(); audio.preload = 'metadata'; audio.src = url; audio.volume = 0.2; } catch (e) { console.warn(`Failed to preload sound ${index}:`, e); } }, index * 200); }); } catch (error) { console.error('Error in sound preloading:', error); } } function safePlayHornetSound() { try { const randomSound = HORNET_SOUNDS[Math.floor(Math.random() * HORNET_SOUNDS.length)]; const audio = new Audio(randomSound); audio.volume = 0.2; const playPromise = audio.play(); if (playPromise !== undefined) { playPromise.catch(e => { console.log('Audio play prevented by browser:', e.name); }); } } catch (error) { console.error('Error playing Hornet sound:', error); } } function scheduleNextSound() { if (soundTimeout) { clearTimeout(soundTimeout); soundTimeout = null; } if (!isActive) return; try { const randomDelay = HORNET_CONFIG.soundInterval + (Math.random() * 6000 - 3000); soundTimeout = setTimeout(() => { if (isActive && targetPlayer) { safePlayHornetSound(); scheduleNextSound(); } }, Math.max(5000, randomDelay)); } catch (error) { console.error('Error scheduling sound:', error); } } /* ---------- FUNCIONES DE DETECCIÓN SEGURAS ---------- */ function safeGetCurrentPlayerCoords() { try { const canvas = document.getElementById('canvas'); if (!canvas) return null; const selfAvatar = document.querySelector('.spawnedavatar[data-playerid]:not([style*="display: none"])'); if (!selfAvatar) return null; const canvasRect = canvas.getBoundingClientRect(); const avatarRect = selfAvatar.getBoundingClientRect(); if (!canvasRect.width || !canvasRect.height || !avatarRect.width || !avatarRect.height) { return null; } const coords = { x: Math.round((avatarRect.left - canvasRect.left) + (avatarRect.width / 2)), y: Math.round((avatarRect.top - canvasRect.top) + (avatarRect.height / 2)), width: avatarRect.width, height: avatarRect.height, element: selfAvatar }; if (coords.x < 0 || coords.y < 0 || coords.x > canvasRect.width || coords.y > canvasRect.height) { return lastKnownPosition.x ? lastKnownPosition : null; } lastKnownPosition = { x: coords.x, y: coords.y }; retryCount = 0; return coords; } catch (error) { console.error('Error getting player coordinates:', error); return lastKnownPosition.x ? lastKnownPosition : null; } } function safeGetPlayerCoords(playerId) { try { if (!playerId) return null; const canvas = document.getElementById('canvas'); if (!canvas) return null; const avatar = document.querySelector(`.spawnedavatar[data-playerid="${playerId}"]:not([style*="display: none"])`); if (!avatar) return null; const canvasRect = canvas.getBoundingClientRect(); const avatarRect = avatar.getBoundingClientRect(); if (!canvasRect.width || !canvasRect.height || !avatarRect.width || !avatarRect.height) { return null; } const coords = { x: Math.round((avatarRect.left - canvasRect.left) + (avatarRect.width / 2)), y: Math.round((avatarRect.top - canvasRect.top) + (avatarRect.height / 2)), width: avatarRect.width, height: avatarRect.height, element: avatar }; if (coords.x < 0 || coords.y < 0 || coords.x > canvasRect.width || coords.y > canvasRect.height) { return null; } return coords; } catch (error) { console.error('Error getting target player coordinates:', error); return null; } } /* ---------- FUNCIONES DE COLISIÓN Y ANIMACIÓN ---------- */ function checkCollision(hornetX, hornetY, playerCoords) { if (!playerCoords) return false; const canvas = document.getElementById('canvas'); if (!canvas) return false; const canvasRect = canvas.getBoundingClientRect(); // Convertir posición de Hornet a coordenadas del canvas const hornetCanvasX = hornetX - canvasRect.left; const hornetCanvasY = hornetY - canvasRect.top; // Calcular distancia entre Hornet y el jugador const dx = Math.abs(hornetCanvasX - playerCoords.x); const dy = Math.abs(hornetCanvasY - playerCoords.y); const distance = Math.sqrt(dx * dx + dy * dy); return distance <= HORNET_CONFIG.collisionDistance; } function updateHornetAnimation(newState) { if (!hornetElement || currentAnimation === newState) return; try { currentAnimation = newState; if (newState === 'colliding') { hornetElement.style.backgroundImage = `url('${HORNET_CONFIG.collisionImageUrl}')`; console.log('🕷️ Hornet collision animation activated!'); } else { hornetElement.style.backgroundImage = `url('${HORNET_CONFIG.imageUrl}')`; console.log('🕷️ Hornet following animation activated!'); } } catch (error) { console.error('Error updating Hornet animation:', error); } } /* ---------- FUNCIONES DE HORNET MEJORADAS ---------- */ function createHornetElement() { try { if (hornetElement) { if (hornetElement.parentNode) { hornetElement.parentNode.removeChild(hornetElement); } hornetElement = null; } hornetElement = document.createElement('div'); hornetElement.id = 'hornet-companion'; hornetElement.style.cssText = ` position: fixed; width: ${HORNET_CONFIG.size}px; height: ${HORNET_CONFIG.size}px; background-image: url('${HORNET_CONFIG.imageUrl}'); background-size: contain; background-repeat: no-repeat; background-position: center; pointer-events: none; z-index: 9999; transition: background-image 0.3s ease; image-rendering: pixelated; transform-origin: center; display: none; will-change: transform, left, top; `; if (document.body) { document.body.appendChild(hornetElement); console.log('🕷️ Hornet element created successfully!'); } else { throw new Error('Document body not available'); } // Inicializar estado de animación currentAnimation = 'following'; isColliding = false; return hornetElement; } catch (error) { console.error('Error creating Hornet element:', error); return null; } } function safeUpdateHornetPosition() { try { if (!hornetElement || !targetPlayer || !isActive) return; const playerCoords = targetPlayer.type === 'self' ? safeGetCurrentPlayerCoords() : safeGetPlayerCoords(targetPlayer.id); if (!playerCoords) { retryCount++; if (retryCount > HORNET_CONFIG.maxRetries) { hornetElement.style.display = 'none'; } return; } retryCount = 0; hornetElement.style.display = 'block'; // Calcular posición objetivo const targetX = Math.max(0, playerCoords.x - HORNET_CONFIG.followDistance); const targetY = Math.max(0, playerCoords.y - HORNET_CONFIG.followDistance); // Movimiento suave con límites const dx = targetX - hornetPosition.x; const dy = targetY - hornetPosition.y; hornetPosition.x += dx * (HORNET_CONFIG.speed / 100); hornetPosition.y += dy * (HORNET_CONFIG.speed / 100); // Efecto de rebote controlado bounceOffset += HORNET_CONFIG.bounceSpeed; const bounce = Math.sin(bounceOffset) * HORNET_CONFIG.bounceAmplitude; // Obtener canvas para posición relativa const canvas = document.getElementById('canvas'); if (!canvas) return; const canvasRect = canvas.getBoundingClientRect(); // Calcular posición final con límites de pantalla const finalX = Math.max(0, Math.min(canvasRect.left + hornetPosition.x, window.innerWidth - HORNET_CONFIG.size)); const finalY = Math.max(0, Math.min(canvasRect.top + hornetPosition.y + bounce, window.innerHeight - HORNET_CONFIG.size)); // **NUEVA FUNCIONALIDAD: Detectar colisión y cambiar animación** const wasColliding = isColliding; isColliding = checkCollision(finalX, finalY, playerCoords); // Cambiar animación según el estado de colisión if (isColliding && !wasColliding) { updateHornetAnimation('colliding'); console.log('🕷️ Hornet is now colliding with player!'); } else if (!isColliding && wasColliding) { updateHornetAnimation('following'); console.log('🕷️ Hornet resumed following player!'); } // Aplicar posición de forma segura hornetElement.style.left = `${finalX}px`; hornetElement.style.top = `${finalY}px`; // Voltear sprite según dirección (solo cuando no está colisionando) if (!isColliding) { const isMovingLeft = dx < -0.5; hornetElement.style.transform = `scaleX(${isMovingLeft ? -1 : 1})`; } else { // Cuando está colisionando, mantener orientación normal hornetElement.style.transform = `scaleX(1)`; } } catch (error) { console.error('Error updating Hornet position:', error); if (hornetElement) { hornetElement.style.display = 'none'; } } } function startHornetFollowing(playerData) { try { if (isActive) stopHornetFollowing(); targetPlayer = playerData; isActive = true; retryCount = 0; isColliding = false; // Reset estado de colisión currentAnimation = 'following'; // Reset animación // Crear elemento de Hornet if (!createHornetElement()) { throw new Error('Failed to create Hornet element'); } // Inicializar posición segura const initialCoords = playerData.type === 'self' ? safeGetCurrentPlayerCoords() : safeGetPlayerCoords(playerData.id); if (initialCoords) { hornetPosition.x = initialCoords.x; hornetPosition.y = initialCoords.y; } // Función de animación segura function safeAnimate() { if (!isActive) return; try { safeUpdateHornetPosition(); animationFrame = requestAnimationFrame(safeAnimate); } catch (error) { console.error('Animation error:', error); stopHornetFollowing(); } } // Iniciar animación después de un breve delay setTimeout(() => { if (isActive) safeAnimate(); }, 500); // Iniciar sonidos después de un delay mayor setTimeout(() => { if (isActive) scheduleNextSound(); }, 2000); console.log(`🕷️ Hornet is now safely following ${playerData.name || 'player ' + playerData.id}!`); } catch (error) { console.error('Error starting Hornet following:', error); stopHornetFollowing(); alert('❌ Error: Could not summon Hornet. Check console for details.'); } } function stopHornetFollowing() { try { console.log('🕷️ Stopping Hornet following...'); isActive = false; targetPlayer = null; retryCount = 0; isColliding = false; // Reset estado de colisión currentAnimation = 'following'; // Reset animación // Limpiar animación if (animationFrame) { cancelAnimationFrame(animationFrame); animationFrame = null; } // Limpiar sonidos if (soundTimeout) { clearTimeout(soundTimeout); soundTimeout = null; } // Ocultar elemento if (hornetElement) { hornetElement.style.display = 'none'; } console.log('🕷️ Hornet has stopped following safely.'); } catch (error) { console.error('Error stopping Hornet:', error); } } /* ---------- INTERFAZ DE USUARIO MEJORADA ---------- */ function createSafeControlPanel() { try { // Verificar si ya existe un panel const existingPanel = document.getElementById('hornet-control-panel'); if (existingPanel) { existingPanel.remove(); } const panel = document.createElement('div'); panel.id = 'hornet-control-panel'; panel.style.cssText = ` position: fixed; top: 20px; right: 20px; z-index: 9998; background: linear-gradient(135deg, #2d1b69, #11998e); color: white; padding: 20px; border-radius: 15px; font-family: 'Segoe UI', Arial, sans-serif; font-size: 14px; box-shadow: 0 8px 25px rgba(0,0,0,0.6); border: 2px solid #8e44ad; min-width: 280px; max-width: 320px; backdrop-filter: blur(10px); `; const title = document.createElement('div'); title.innerHTML = '🕷️ HORNET FOLLOWER 🕷️'; title.style.cssText = ` font-weight: bold; font-size: 16px; text-align: center; margin-bottom: 15px; background: linear-gradient(45deg, #e74c3c, #8e44ad); -webkit-background-clip: text; -webkit-text-fill-color: transparent; text-shadow: 0 0 10px rgba(231, 76, 60, 0.5); `; panel.appendChild(title); // **NUEVA SECCIÓN: Estado de Animación** const animationStatus = document.createElement('div'); animationStatus.id = 'hornet-animation-status'; animationStatus.style.cssText = ` margin-bottom: 15px; padding: 8px; background: rgba(0,0,0,0.4); border-radius: 8px; text-align: center; font-size: 12px; border: 1px solid #8e44ad; `; animationStatus.innerHTML = '🎭 Animation: <span style="color: #95a5a6;">Waiting...</span>'; panel.appendChild(animationStatus); // Selector de objetivo const targetSection = document.createElement('div'); targetSection.style.cssText = `margin-bottom: 15px;`; const targetLabel = document.createElement('div'); targetLabel.textContent = '🎯 Follow Target:'; targetLabel.style.cssText = `margin-bottom: 8px; font-weight: bold; color: #ecf0f1;`; targetSection.appendChild(targetLabel); const targetSelect = document.createElement('select'); targetSelect.style.cssText = ` width: 100%; padding: 8px; border-radius: 8px; border: 2px solid #8e44ad; background: rgba(20, 20, 40, 0.9); color: #ecf0f1; font-size: 13px; outline: none; `; // Opción para seguir a uno mismo const selfOption = document.createElement('option'); selfOption.value = 'self'; selfOption.textContent = '👤 Follow Myself'; targetSelect.appendChild(selfOption); targetSection.appendChild(targetSelect); panel.appendChild(targetSection); // Botones de control const buttonsDiv = document.createElement('div'); buttonsDiv.style.cssText = `display: flex; gap: 10px; flex-direction: column;`; const startBtn = document.createElement('button'); startBtn.textContent = '🕷️ SUMMON HORNET'; startBtn.style.cssText = ` padding: 12px; border-radius: 8px; border: none; background: linear-gradient(45deg, #e74c3c, #8e44ad); color: white; font-weight: bold; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(231, 76, 60, 0.4); outline: none; `; const stopBtn = document.createElement('button'); stopBtn.textContent = '❌ DISMISS HORNET'; stopBtn.style.cssText = ` padding: 12px; border-radius: 8px; border: none; background: linear-gradient(45deg, #34495e, #2c3e50); color: white; font-weight: bold; cursor: not-allowed; transition: all 0.3s ease; box-shadow: 0 4px 15px rgba(52, 73, 94, 0.4); opacity: 0.6; outline: none; `; stopBtn.disabled = true; buttonsDiv.appendChild(startBtn); buttonsDiv.appendChild(stopBtn); panel.appendChild(buttonsDiv); // Estado const statusDiv = document.createElement('div'); statusDiv.style.cssText = ` margin-top: 15px; padding: 10px; background: rgba(0,0,0,0.3); border-radius: 8px; font-size: 12px; text-align: center; color: #bdc3c7; `; statusDiv.textContent = '🕷️ Hornet is waiting...'; panel.appendChild(statusDiv); // Event listeners seguros startBtn.addEventListener('click', () => { try { const selectedTarget = targetSelect.value; let playerData; if (selectedTarget === 'self') { playerData = { type: 'self', name: 'You', id: null }; } else { const selectedOption = targetSelect.options[targetSelect.selectedIndex]; const playerName = selectedOption ? selectedOption.textContent.replace('🎯 ', '') : 'Unknown'; playerData = { type: 'other', name: playerName, id: selectedTarget }; } startHornetFollowing(playerData); startBtn.disabled = true; startBtn.style.opacity = '0.6'; startBtn.style.cursor = 'not-allowed'; stopBtn.disabled = false; stopBtn.style.opacity = '1'; stopBtn.style.cursor = 'pointer'; statusDiv.textContent = `🕷️ Hornet is following ${playerData.name}!`; statusDiv.style.color = '#2ecc71'; } catch (error) { console.error('Error in start button:', error); alert('❌ Error starting Hornet. Please try again.'); } }); stopBtn.addEventListener('click', () => { try { stopHornetFollowing(); startBtn.disabled = false; startBtn.style.opacity = '1'; startBtn.style.cursor = 'pointer'; stopBtn.disabled = true; stopBtn.style.opacity = '0.6'; stopBtn.style.cursor = 'not-allowed'; statusDiv.textContent = '🕷️ Hornet has been dismissed.'; statusDiv.style.color = '#e67e22'; // Reset estado de animación en interfaz const animStatus = document.getElementById('hornet-animation-status'); if (animStatus) { animStatus.innerHTML = '🎭 Animation: <span style="color: #95a5a6;">Waiting...</span>'; } } catch (error) { console.error('Error in stop button:', error); } }); if (document.body) { document.body.appendChild(panel); console.log('🕷️ Control panel created successfully!'); } return { targetSelect, statusDiv, animationStatus }; } catch (error) { console.error('Error creating control panel:', error); return null; } } /* ---------- ACTUALIZACIÓN DE INTERFAZ EN TIEMPO REAL ---------- */ function updateAnimationStatusInUI() { try { const animStatus = document.getElementById('hornet-animation-status'); if (!animStatus) return; if (!isActive) { animStatus.innerHTML = '🎭 Animation: <span style="color: #95a5a6;">Waiting...</span>'; } else if (isColliding) { animStatus.innerHTML = '🎭 Animation: <span style="color: #e74c3c;">💥 Colliding</span>'; } else { animStatus.innerHTML = '🎭 Animation: <span style="color: #2ecc71;">🏃 Following</span>'; } } catch (error) { console.error('Error updating animation status UI:', error); } } // Modificar safeUpdateHornetPosition para incluir actualización de UI const originalSafeUpdateHornetPosition = safeUpdateHornetPosition; safeUpdateHornetPosition = function() { originalSafeUpdateHornetPosition(); updateAnimationStatusInUI(); }; /* ---------- SISTEMA SEGURO DE GESTIÓN DE JUGADORES ---------- */ function safeRefreshPlayerList(targetSelect) { if (!targetSelect) return; try { const currentSelection = targetSelect.value; // Preservar opción "Follow Myself" while (targetSelect.children.length > 1) { targetSelect.removeChild(targetSelect.lastChild); } // Buscar jugadores de forma segura const playerRows = document.querySelectorAll('.playerlist-row[data-playerid]'); let playersAdded = 0; playerRows.forEach(row => { try { if (row.dataset.self === 'true') return; if (row.dataset.playerid === '0') return; if (!row.dataset.playerid) return; const nameElement = row.querySelector('.playerlist-name a'); const name = nameElement ? nameElement.textContent.trim() : `Player ${row.dataset.playerid}`; if (name && row.dataset.playerid) { const opt = document.createElement('option'); opt.value = row.dataset.playerid; opt.textContent = `🎯 ${name}`; targetSelect.appendChild(opt); playersAdded++; } } catch (error) { console.warn('Error processing player row:', error); } }); // Restaurar selección si es posible if (currentSelection && targetSelect.querySelector(`option[value="${currentSelection}"]`)) { targetSelect.value = currentSelection; } console.log(`🎯 Refreshed player list: ${playersAdded} players found`); } catch (error) { console.error('Error refreshing player list:', error); } } /* ---------- INICIALIZACIÓN SEGURA ---------- */ function safeInitialize() { try { console.log('🕷️ Initializing Hornet Companion (Safe Mode)...'); // Verificar entorno if (!document.body) { throw new Error('Document body not ready'); } // Precargar sonidos de forma segura setTimeout(safePreloadSounds, 1000); // Crear panel de control const controlElements = createSafeControlPanel(); if (!controlElements) { throw new Error('Failed to create control panel'); } const { targetSelect, statusDiv } = controlElements; // Configurar observador de jugadores con throttling let observerTimeout = null; const throttledRefresh = () => { if (observerTimeout) clearTimeout(observerTimeout); observerTimeout = setTimeout(() => { safeRefreshPlayerList(targetSelect); }, 500); }; // Observar lista de jugadores const playerListElement = document.getElementById('playerlist'); if (playerListElement) { try { const observer = new MutationObserver(throttledRefresh); observer.observe(playerListElement, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-playerid'] }); console.log('👁️ Player list observer configured'); } catch (observerError) { console.warn('Could not set up player list observer:', observerError); } } // Actualizar lista inicial setTimeout(() => safeRefreshPlayerList(targetSelect), 1500); console.log('🕷️ Hornet Companion initialized successfully!'); } catch (error) { console.error('Critical error during initialization:', error); alert('❌ Hornet Companion failed to load. Check console for details.'); } } /* ---------- CLEANUP Y PREVENCIÓN DE MEMORY LEAKS ---------- */ function cleanup() { try { console.log('🧹 Cleaning up Hornet Companion...'); stopHornetFollowing(); if (hornetElement && hornetElement.parentNode) { hornetElement.parentNode.removeChild(hornetElement); hornetElement = null; } const panel = document.getElementById('hornet-control-panel'); if (panel && panel.parentNode) { panel.parentNode.removeChild(panel); } window.hornetCompanionLoaded = false; } catch (error) { console.error('Error during cleanup:', error); } } /* ---------- EVENT LISTENERS GLOBALES ---------- */ window.addEventListener('beforeunload', cleanup); window.addEventListener('error', (event) => { if (event.message && event.message.includes('hornet')) { console.error('Hornet-related error detected:', event); stopHornetFollowing(); } }); /* ---------- INICIALIZACIÓN CON RETRY ---------- */ function initializeWithRetry(attempts = 0) { const maxAttempts = 3; if (attempts >= maxAttempts) { console.error('❌ Max initialization attempts reached'); return; } try { // Verificar si los elementos necesarios están disponibles const canvas = document.getElementById('canvas'); const playerList = document.getElementById('playerlist'); if (!canvas || !playerList) { console.log(`⏳ Waiting for game elements... (attempt ${attempts + 1}/${maxAttempts})`); setTimeout(() => initializeWithRetry(attempts + 1), 2000); return; } safeInitialize(); } catch (error) { console.error(`Initialization attempt ${attempts + 1} failed:`, error); setTimeout(() => initializeWithRetry(attempts + 1), 2000); } } // Iniciar cuando el DOM esté listo if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { setTimeout(() => initializeWithRetry(), 1000); }); } else { setTimeout(() => initializeWithRetry(), 1000); } console.log('🕷️ Hornet Companion script loaded - awaiting initialization...'); })();