Warning: fopen(/www/sites/update.greasyfork.icu/index/store/temp/ec5331a4ed708dc8b18fcfd292315f79.js): failed to open stream: No space left on device in /www/sites/update.greasyfork.icu/index/scriptControl.php on line 65
// ==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);
})();