Milkywayidle Labyrinth Timer Flasher

Flashes Labyrinth button and timer when about to fail.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Для установки этого скрипта вам необходимо установить расширение, такое как Tampermonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         Milkywayidle Labyrinth Timer Flasher
// @namespace    https://www.milkywayidle.com/
// @version      1.1
// @description  Flashes Labyrinth button and timer when about to fail.
// @author       SilkyPanda
// @match        *://*.milkywayidle.com/*
// @icon         https://www.milkywayidle.com/favicon.svg
// @grant        none
// @license      MIT
// @run-at       document-start
// ==/UserScript==


(function() {
    'use strict';

    const FLASH_THRESHOLD_SEC = 20; 
    const COMBAT_ROOM_DURATION_MS = 120000; 
    
    let timeRemainingMs = Infinity;
    let isCombatRoom = false;
    let combatEndTimeLocal = 0;
    let isLabyrinthActive = false;

    // Inject Custom CSS
    const style = document.createElement('style');
    style.innerHTML = `
        @keyframes flashTimerRed {
            0%   { color: #ff3333; text-shadow: 0 0 8px rgba(255, 51, 51, 0.8); }
            50%  { color: inherit; text-shadow: none; }
            100% { color: #ff3333; text-shadow: 0 0 8px rgba(255, 51, 51, 0.8); }
        }
        .timer-warning-flash {
            animation: flashTimerRed 1s infinite !important;
            font-weight: bold !important;
        }

        @keyframes flashNavSubtle {
            0%   { background-color: rgba(220, 38, 38, 0.35); }
            50%  { background-color: transparent; }
            100% { background-color: rgba(220, 38, 38, 0.35); }
        }
        .nav-warning-flash {
            animation: flashNavSubtle 1s infinite !important;
            border-radius: 8px;
        }
    `;
    window.addEventListener('DOMContentLoaded', () => {
        document.head.appendChild(style);
    });

    // Stealth WebSocket Proxy
    const OriginalWebSocket = window.WebSocket;
    window.WebSocket = new Proxy(OriginalWebSocket, {
        construct(target, args) {
            const ws = new target(...args);
            
            ws.addEventListener('message', function(event) {
                try {
                    const data = JSON.parse(event.data);
                    
                    if (data.type === 'labyrinth_room_progress') {
                        isLabyrinthActive = true;
                        isCombatRoom = false;
                        timeRemainingMs = data.timeRemainingMs;
                    }
                    else if (data.type === 'new_battle') {
                        isLabyrinthActive = true;
                        isCombatRoom = true;
                        combatEndTimeLocal = Date.now() + COMBAT_ROOM_DURATION_MS;
                        timeRemainingMs = COMBAT_ROOM_DURATION_MS;
                    }
                    else if (data.type === 'actions_updated') {
                        if (isLabyrinthActive) {
                            isLabyrinthActive = false;
                            isCombatRoom = false;
                            timeRemainingMs = Infinity; 
                        }
                    }
                } catch (e) {}
            });
            
            return ws;
        }
    });

    // UI Updater Loop
    setInterval(() => {
        if (isLabyrinthActive && isCombatRoom) {
            timeRemainingMs = combatEndTimeLocal - Date.now();
            if (timeRemainingMs < 0) timeRemainingMs = 0; 
        }

        const shouldFlash = isLabyrinthActive && timeRemainingMs <= (FLASH_THRESHOLD_SEC * 1000);

        let labButton = null;
        const spans = document.querySelectorAll('span[class*="NavigationBar_label"]');
        for (let span of spans) {
            if (span.textContent.trim() === 'Labyrinth') {
                labButton = span.closest('[class*="NavigationBar_navigationLink"]');
                break;
            }
        }

        const combatTimer = document.querySelector('[class*="BattlePanel_timeRemaining"]');
        const skillTimer = document.querySelector('[class*="LabyrinthPanel_timerDisplay"]');

        if (labButton) {
            if (shouldFlash) labButton.classList.add('nav-warning-flash');
            else labButton.classList.remove('nav-warning-flash');
        }

        if (combatTimer) {
            if (shouldFlash && isCombatRoom) combatTimer.classList.add('timer-warning-flash');
            else combatTimer.classList.remove('timer-warning-flash');
        }

        if (skillTimer) {
            if (shouldFlash && !isCombatRoom) skillTimer.classList.add('timer-warning-flash');
            else skillTimer.classList.remove('timer-warning-flash');
        }

    }, 500);

})();