// ==UserScript== // @name Powerline Spectator Mode // @author Rumini - Discord: rumini // @description Powerline.io Spectator Mode // @version 1.0 // @match *://powerline.io/* // @icon https://i.imgur.com/9k4SFr0.png // @license MIT // @grant none // @namespace https://greasyfork.org/users/1356205 // @downloadURL https://update.greasyfork.icu/scripts/504709/Powerline%20Spectator%20Mode.user.js // @updateURL https://update.greasyfork.icu/scripts/504709/Powerline%20Spectator%20Mode.meta.js // ==/UserScript== if (window.location.href === 'https://powerline.io/') { window.location.href = 'https://powerline.io/maindev.html'; } (function () { 'use strict'; let currentSpectateIndex = 0; let switcherButton; let overlayToggleButton; let isSpectating = false; let infoDiv; let updateInterval; let lastKnownLocalPlayerID = 0; let overlayVisible = true; let forceOverlayVisible = false; function waitForGame(callback) { if (typeof network !== 'undefined' && typeof entities !== 'undefined' && typeof Snake !== 'undefined') { callback(); } else { setTimeout(() => waitForGame(callback), 100); } } function createSpectateSwitcherUI() { const container = document.createElement('div'); container.id = 'spectate-switcher-container'; container.style.cssText = ` position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); z-index: 1000; opacity: 0; transition: opacity 0.3s ease-in-out; display: flex; align-items: center; gap: 10px; `; switcherButton = document.createElement('button'); switcherButton.id = 'spectate-switcher'; switcherButton.style.cssText = ` background-color: rgba(0, 58, 58, 0.4); color: #05ffff; border: 2px solid rgba(5, 255, 255, 0.5); border-radius: 10px; padding: 10px 20px; font-family: 'Arial Black', sans-serif; font-size: 16px; cursor: pointer; backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(5px); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; `; switcherButton.textContent = 'Switch Spectate'; switcherButton.addEventListener('click', switchSpectatePlayer); switcherButton.addEventListener('mouseover', () => { switcherButton.style.backgroundColor = 'rgba(0, 88, 88, 0.5)'; switcherButton.style.transform = 'scale(1.05)'; switcherButton.style.boxShadow = '0 0 15px rgba(5, 255, 255, 0.2)'; }); switcherButton.addEventListener('mouseout', () => { switcherButton.style.backgroundColor = 'rgba(0, 58, 58, 0.3)'; switcherButton.style.transform = 'scale(1)'; switcherButton.style.boxShadow = 'none'; }); overlayToggleButton = document.createElement('div'); overlayToggleButton.id = 'overlay-toggle-button'; overlayToggleButton.style.cssText = ` width: 50px; height: 50px; border-radius: 50%; background-color: rgba(0, 58, 58, 0.4); border: 2px solid rgba(5, 255, 255, 0.5); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease; `; overlayToggleButton.innerHTML = ` `; overlayToggleButton.addEventListener('click', toggleOverlay); overlayToggleButton.addEventListener('mouseover', () => { overlayToggleButton.style.backgroundColor = 'rgba(0, 88, 88, 0.5)'; overlayToggleButton.style.transform = 'scale(1.1)'; overlayToggleButton.style.boxShadow = '0 0 15px rgba(5, 255, 255, 0.2)'; }); overlayToggleButton.addEventListener('mouseout', () => { overlayToggleButton.style.backgroundColor = 'rgba(0, 58, 58, 0.3)'; overlayToggleButton.style.transform = 'scale(1)'; overlayToggleButton.style.boxShadow = 'none'; }); infoDiv = document.createElement('div'); infoDiv.id = 'spectate-info'; infoDiv.style.cssText = ` position: fixed; bottom: 200px; right: 20px; z-index: 1000; background-color: rgba(0, 58, 58, 0.3); border: 2px solid rgba(5, 255, 255, 0.8); border-radius: 10px; padding: 0 20px 20px 20px; font-family: 'Arial', sans-serif; font-size: 14px; color: #05ffff; backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); box-shadow: 0 0 20px rgba(5, 255, 255, 0.5); transition: opacity 0.3s ease, box-shadow 1s ease; max-width: 400px; word-wrap: break-word; opacity: 0; `; container.appendChild(switcherButton); container.appendChild(overlayToggleButton); document.body.appendChild(container); document.body.appendChild(infoDiv); setInterval(updateButtonVisibility, 100); setInterval(updateInfoBoxGlow, 50); document.addEventListener('keydown', function (event) { if (event.code === 'Space') { if (isSpectating || localPlayerID === 0) { isSpectating = false; clearInterval(updateInterval); infoDiv.style.opacity = '0'; } updateButtonVisibility(); } }); setOverlayVisibility(true); } function updateButtonVisibility() { const container = document.getElementById('spectate-switcher-container'); if (localPlayerID !== 0 && lastKnownLocalPlayerID === 0) { isSpectating = false; clearInterval(updateInterval); infoDiv.style.opacity = '0'; forceOverlayVisible = false; } if (localPlayerID === 0 && lastKnownLocalPlayerID !== 0) { forceOverlayVisible = true; setOverlayVisibility(true); } lastKnownLocalPlayerID = localPlayerID; if (localPlayerID === 0 || isSpectating) { container.style.opacity = '1'; infoDiv.style.opacity = isSpectating ? '1' : '0'; } else { container.style.opacity = '0'; infoDiv.style.opacity = '0'; } } function setOverlayVisibility(visible) { const overlay = document.getElementById('overlay'); if (overlay) { overlayVisible = visible; overlay.style.opacity = visible ? '1' : '0'; } } function toggleOverlay() { if (forceOverlayVisible) { forceOverlayVisible = false; } setOverlayVisibility(!overlayVisible); } function updateInfoBoxGlow() { const glowIntensity = Math.sin(Date.now() * 0.0005) * 5 + 15; infoDiv.style.boxShadow = `0 0 ${glowIntensity}px rgba(5, 255, 255, 0.7)`; } function switchSpectatePlayer() { const keys = Object.keys(entities); if (keys.length === 0) return; let found = false; for (let i = 0; i < keys.length; i++) { currentSpectateIndex = (currentSpectateIndex + 1) % keys.length; const entity = entities[keys[currentSpectateIndex]]; if (entity instanceof Snake && entity !== localPlayer) { localPlayer = entity; camera.target = localPlayer; updateInfoDiv(entity); clearInterval(updateInterval); updateInterval = setInterval(() => updateInfoDiv(entity), 100); isSpectating = true; found = true; break; } } if (!found) { hud.addSpecialMessage("No other snakes are close enough to spectate."); } updateButtonVisibility(); } function updateInfoDiv(entity) { if (!entity || entity.id === 0) { infoDiv.style.opacity = '0'; return; } const entityInfo = `

Spectating: ${entity.nick || ''}

ID: ${entity.id}
Current Length: ${entity.curLength.toFixed(2)}
Total Length: ${entity.curLengthDst.toFixed(2)}
Last Speed: ${entity.lastSpeed.toFixed(2)}
Client X: ${entity.x.toFixed(2)}
Client Y: ${entity.y.toFixed(2)}
Server X: ${entity.lastServerX.toFixed(2)}
Server Y: ${entity.lastServerY.toFixed(2)}
`; infoDiv.innerHTML = entityInfo; infoDiv.style.opacity = '1'; } waitForGame(createSpectateSwitcherUI); })();