Powerline.io Follow Mouse Cursor "Hacks"

Move snake toward mouse using separate timers for WASD based on angle, togglable with space key, display precise mouse angle and visual guides, with a settings menu and shift-based angle locking.

// ==UserScript==
// @name         Powerline.io Follow Mouse Cursor "Hacks"
// @namespace    http://tampermonkey.net/
// @version      1.91
// @author Heptatron
// @description  Move snake toward mouse using separate timers for WASD based on angle, togglable with space key, display precise mouse angle and visual guides, with a settings menu and shift-based angle locking.
// @match        *://powerline.io/*
// @license         MIT
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    let frequency = 13; // presses per second
    let baseInterval = 1000 / frequency; // 50ms for 20Hz
    let angleStepIncrement = 10;
    let angleLockValue = 90;
    let isShiftKeyPressed = false;

    let mouseX = window.innerWidth / 2;
    let mouseY = window.innerHeight / 2;
    let currentAngle = 0;

    const keyTimers = {
        W: { last: 0, delay: Infinity },
        A: { last: 0, delay: Infinity },
        S: { last: 0, delay: Infinity },
        D: { last: 0, delay: Infinity }
    };

    let isActive = false; // Flag to track whether the functionality is active or not
    let angleCanvas;
    let ctx;
    let angleDisplay;
    let settingsMenu;
    let frequencyInput;
    let angleStepInput;
    let angleLockInput;

    function createSettingsMenu() {
        settingsMenu = document.createElement('div');
        settingsMenu.style.position = 'fixed';
        settingsMenu.style.top = '10px';
        settingsMenu.style.left = '10px';
        settingsMenu.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
        settingsMenu.style.color = 'white';
        settingsMenu.style.padding = '10px';
        settingsMenu.style.borderRadius = '5px';
        settingsMenu.style.zIndex = '2000';
        settingsMenu.style.fontFamily = 'sans-serif';
        settingsMenu.style.fontSize = '12px';

        const freqLabel = document.createElement('label');
        freqLabel.textContent = 'Frequency: ';
        frequencyInput = document.createElement('input');
        frequencyInput.type = 'number';
        frequencyInput.value = frequency;
        frequencyInput.style.width = '50px';
        frequencyInput.addEventListener('change', updateFrequency);

        const angleStepLabel = document.createElement('label');
        angleStepLabel.textContent = 'Angle Step (°): ';
        angleStepInput = document.createElement('input');
        angleStepInput.type = 'number';
        angleStepInput.value = angleStepIncrement;
        angleStepInput.style.width = '50px';
        angleStepInput.addEventListener('change', updateAngleStep);

        const angleLockLabel = document.createElement('label');
        angleLockLabel.textContent = 'Angle Lock (°): ';
        angleLockInput = document.createElement('input');
        angleLockInput.type = 'number';
        angleLockInput.value = angleLockValue;
        angleLockInput.style.width = '50px';
        angleLockInput.addEventListener('change', updateAngleLockValue);

        settingsMenu.appendChild(freqLabel);
        settingsMenu.appendChild(frequencyInput);
        settingsMenu.appendChild(document.createElement('br'));
        settingsMenu.appendChild(angleStepLabel);
        settingsMenu.appendChild(angleStepInput);
        settingsMenu.appendChild(document.createElement('br'));
        settingsMenu.appendChild(angleLockLabel);
        settingsMenu.appendChild(angleLockInput);

        document.body.appendChild(settingsMenu);
    }

    function updateFrequency() {
        const newFrequency = parseInt(frequencyInput.value, 10);
        if (!isNaN(newFrequency) && newFrequency > 0) {
            frequency = newFrequency;
            baseInterval = 1000 / frequency;
        } else {
            frequencyInput.value = frequency;
        }
    }

    function updateAngleStep() {
        const newAngleStep = parseInt(angleStepInput.value, 10);
        if (!isNaN(newAngleStep) && newAngleStep >= 0) {
            angleStepIncrement = newAngleStep;
        } else {
            angleStepInput.value = angleStepIncrement;
        }
    }

    function updateAngleLockValue() {
        const newAngleLock = parseInt(angleLockInput.value, 10);
        if (!isNaN(newAngleLock) && newAngleLock >= 0) {
            angleLockValue = newAngleLock;
        } else {
            angleLockInput.value = angleLockValue;
        }
    }

    // --- Angle Display Functions ---
    function createAngleDisplay() {
        angleDisplay = document.createElement('div');
        angleDisplay.style.position = 'fixed';
        angleDisplay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
        angleDisplay.style.color = 'white';
        angleDisplay.style.padding = '5px';
        angleDisplay.style.borderRadius = '5px';
        angleDisplay.style.zIndex = '1000';
        angleDisplay.style.fontFamily = 'sans-serif';
        angleDisplay.style.fontSize = '12px';
        document.body.appendChild(angleDisplay);
    }

    function updateAngleDisplay() {
        if (angleDisplay) {
            const degrees = currentAngle * 180 / Math.PI;
            angleDisplay.textContent = `${degrees.toFixed(1)}°`;
            angleDisplay.style.left = mouseX + 10 + 'px';
            angleDisplay.style.top = mouseY + 10 + 'px';
        }
    }

    // --- Angle Canvas Functions ---
    function createAngleCanvas() {
        angleCanvas = document.createElement('canvas');
        angleCanvas.style.position = 'fixed';
        angleCanvas.style.top = '0';
        angleCanvas.style.left = '0';
        angleCanvas.style.width = '100vw';
        angleCanvas.style.height = '100vh';
        angleCanvas.style.pointerEvents = 'none';
        angleCanvas.style.zIndex = '999';
        document.body.appendChild(angleCanvas);
        ctx = angleCanvas.getContext('2d');
        resizeCanvas();
        window.addEventListener('resize', resizeCanvas);
    }

    function resizeCanvas() {
        if (angleCanvas) {
            angleCanvas.width = window.innerWidth;
            angleCanvas.height = window.innerHeight;
            drawAngleLines();
        }
    }

    function drawAngleLines() {
        if (!ctx) return;

        ctx.clearRect(0, 0, angleCanvas.width, angleCanvas.height);
        ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
        ctx.lineWidth = 1;
        const centerX = angleCanvas.width / 2;
        const centerY = angleCanvas.height / 2;
        const maxDistance = Math.sqrt(centerX * centerX + centerY * centerY);

        // Function to draw a line at a given angle
        function drawLine(angleRad) {
            const endX = centerX + maxDistance * Math.cos(angleRad);
            const endY = centerY + maxDistance * Math.sin(angleRad);

            // Calculate the starting point on the opposite side
            const startX = centerX - (endX - centerX);
            const startY = centerY - (endY - centerY);

            ctx.beginPath();
            ctx.moveTo(startX, startY);
            ctx.lineTo(endX, endY);
            ctx.stroke();
        }

        drawLine(0);
        drawLine(Math.PI / 4);
        drawLine(Math.PI / 2);
        drawLine(-Math.PI / 4);
    }

    document.addEventListener('mousemove', (e) => {
        mouseX = e.clientX;
        mouseY = e.clientY;
        updateAngle();
        if (angleDisplay && isActive) {
            updateAngleDisplay();
        }
    });

    function updateAngle() {
        const centerX = window.innerWidth / 2;
        const centerY = window.innerHeight / 2;
        let dx = mouseX - centerX;
        let dy = mouseY - centerY;
        let rawAngleDegrees = Math.atan2(dy, dx) * 180 / Math.PI;

        if (isShiftKeyPressed && angleLockValue > 0) {
            rawAngleDegrees = Math.round(rawAngleDegrees / angleLockValue) * angleLockValue;
        } else if (angleStepIncrement > 0) {
            rawAngleDegrees = Math.round(rawAngleDegrees / angleStepIncrement) * angleStepIncrement;
        }

        currentAngle = rawAngleDegrees * Math.PI / 180;

        const originalDistance = Math.sqrt(dx * dx + dy * dy) || 1;
        dx = originalDistance * Math.cos(currentAngle);
        dy = originalDistance * Math.sin(currentAngle);
        mouseX = centerX + dx;
        mouseY = centerY + dy;
    }

    document.addEventListener('keydown', (e) => {
        if (e.key === ' ' || e.key === 'Spacebar') { // Toggle active state
            isActive = !isActive;
            if (isActive && !angleCanvas) {
                createAngleCanvas();
                drawAngleLines();
            } else if (!isActive && angleCanvas) {
                document.body.removeChild(angleCanvas);
                angleCanvas = null;
                ctx = null;
                window.removeEventListener('resize', resizeCanvas);
            }
            if (isActive && !angleDisplay) {
                createAngleDisplay();
                updateAngleDisplay();
            } else if (isActive && angleDisplay) {
                updateAngleDisplay();
            } else if (!isActive && angleDisplay) {
                document.body.removeChild(angleDisplay);
                angleDisplay = null;
            }
        } else if (e.key === 'Shift') {
            isShiftKeyPressed = true;
            updateAngle();
            if (angleDisplay && isActive) {
                updateAngleDisplay();
            }
        }
    });

    document.addEventListener('keyup', (e) => {
    if (e.key === 'Shift') {
        isShiftKeyPressed = false;


        updateAngle();

        if (angleDisplay && isActive) {
            updateAngleDisplay();
        }
    }
});


    function sendKey(key) {
        const keyCode = key.charCodeAt(0);
        const down = new KeyboardEvent("keydown", { bubbles: true });
        Object.defineProperty(down, 'keyCode', { get: () => keyCode });
        Object.defineProperty(down, 'which', { get: () => keyCode });
        Object.defineProperty(down, 'key', { get: () => key });
        Object.defineProperty(down, 'code', { get: () => 'Key' + key });
        document.dispatchEvent(down);

        const up = new KeyboardEvent("keyup", { bubbles: true });
        Object.defineProperty(up, 'keyCode', { get: () => keyCode });
        Object.defineProperty(up, 'which', { get: () => keyCode });
        Object.defineProperty(up, 'key', { get: () => key });
        Object.defineProperty(up, 'code', { get: () => 'Key' + key });
        document.dispatchEvent(up);
    }

    function updateDelays() {
        const centerX = window.innerWidth / 2;
        const centerY = window.innerHeight / 2;

        const dx = mouseX - centerX;
        const dy = mouseY - centerY;

        const magnitude = Math.sqrt(dx * dx + dy * dy) || 1;
        const unitX = dx / magnitude;
        const unitY = dy / magnitude;


        keyTimers.W.delay = unitY < 0 ? baseInterval / Math.abs(unitY) : Infinity;
        keyTimers.S.delay = unitY > 0 ? baseInterval / Math.abs(unitY) : Infinity;


        keyTimers.A.delay = unitX < 0 ? baseInterval / Math.abs(unitX) : Infinity;
        keyTimers.D.delay = unitX > 0 ? baseInterval / Math.abs(unitX) : Infinity;
    }

    setInterval(() => {
        if (isActive) {
            updateDelays();

            const now = Date.now();
            for (const key in keyTimers) {
                const timer = keyTimers[key];
                if (now - timer.last >= timer.delay) {
                    timer.last = now;
                    sendKey(key);
                }
            }
        }
    }, 1);

    createSettingsMenu();

    updateAngle();
})();