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);

})();