Virus Timer (Enhanced)

Displays movable and toggleable virus research timer.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        Virus Timer (Enhanced)
// @namespace   TornPDA
// @version     6.1
// @description Displays movable and toggleable virus research timer.
// @author      Pint-Shot-Riot
// @match       https://www.torn.com/*
// @grant       none
// @license     MIT
// ==/UserScript==

(function() {
    'use strict';

    const pdaKey = "###PDA-APIKEY###";
    const defaultPos = { top: "60px", left: "10px" };
    let virusUntil = 0;
    let isVisible = localStorage.getItem('pda_virus_visible') !== 'false';

    const styleSheet = document.createElement("style");
    styleSheet.innerText = `
        .pda-virus-status-li {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            margin: 0 2px;
        }
        .pda-virus-status-icon {
            display: flex;
            align-items: center;
            justify-content: center;
            width: 24px;
            height: 24px;
            cursor: pointer;
            font-size: 16px;
            opacity: 0.75;
            transition: opacity 0.15s, transform 0.15s;
            text-decoration: none;
            filter: grayscale(1);
            user-select: none;
        }
        .pda-virus-status-icon:hover {
            opacity: 1;
            transform: scale(1.15);
        }
        .pda-virus-active {
            filter: grayscale(0) !important;
            opacity: 1 !important;
        }
        #pda-virus-badge {
            position: fixed !important;
            z-index: 2147483647 !important;
            background: rgba(34, 34, 34, 0.5) !important;
            color: rgba(136, 176, 49, 0.8) !important;
            font-family: "Segoe UI", Arial, sans-serif !important;
            font-weight: bold !important;
            text-transform: uppercase !important;
            box-shadow: 2px 2px 5px rgba(0,0,0,0.3);
            border: 1px solid rgba(68, 68, 68, 0.5) !important;
            border-left: 3px solid #88b031 !important;
            padding: 8px 14px !important;
            font-size: 13px !important;
            border-radius: 2px;
            white-space: nowrap !important;
            touch-action: none;
            user-select: none;
            display: none;
        }
        .pda-drag-ready {
            transform: scale(1.1) !important;
            background: rgba(136, 176, 49, 0.4) !important;
            border: 1px solid #88b031 !important;
        }
        .pda-pulse {
            animation: pda-glow 2.5s infinite ease-in-out;
        }
        .pda-warning-pulse {
            animation: pda-warning-glow 1.5s infinite ease-in-out !important;
            border-left-color: #ff3b3b !important;
            color: #ff3b3b !important;
        }
        @keyframes pda-glow {
            0% { border-color: rgba(68, 68, 68, 0.5); }
            50% { border-color: rgba(136, 176, 49, 0.8); }
            100% { border-color: rgba(68, 68, 68, 0.5); }
        }
        @keyframes pda-warning-glow {
            0% { border-color: rgba(255, 59, 59, 0.3); }
            50% { border-color: rgba(255, 59, 59, 1); }
            100% { border-color: rgba(255, 59, 59, 0.3); }
        }
    `;
    document.head.appendChild(styleSheet);

    function isDesktop() {
        return window.innerWidth > 768;
    }

    function getApiKey() {
        if (!pdaKey.includes("PDA-APIKEY")) return pdaKey;
        return localStorage.getItem('pda_virus_manual_key');
    }

    function checkKeyPrompt() {
        if (isDesktop() && (!getApiKey() || getApiKey().includes("PDA-APIKEY"))) {
            const newKey = prompt("Virus Timer: Please enter a LIMITED ACCESS (or higher) API Key:");
            if (newKey) {
                localStorage.setItem('pda_virus_manual_key', newKey.trim());
                updateData();
            }
        }
    }

    function syncUI() {
        const badge = document.getElementById('pda-virus-badge');
        const icon = document.getElementById('pda-virus-status-btn');
        if (badge) {
            const targetDisplay = isVisible ? 'block' : 'none';
            if (badge.style.display !== targetDisplay) {
                badge.style.setProperty('display', targetDisplay, 'important');
            }
            badge.classList.toggle('pda-pulse', isVisible);
        }
        if (icon) {
            icon.classList.toggle('pda-virus-active', isVisible);
        }
    }

    function handleToggle() {
        isVisible = !isVisible;
        localStorage.setItem('pda_virus_visible', isVisible);
        syncUI();
    }

    function resetPosition() {
        const badge = document.getElementById('pda-virus-badge');
        localStorage.removeItem('pda_virus_pos');
        if (badge) {
            Object.assign(badge.style, { top: defaultPos.top, left: defaultPos.left });
        }
    }

    function createUI() {
        const statusUl = document.querySelector('ul[class*="status-icons"]');
        let created = false;
        
        if (statusUl && !document.getElementById('pda-virus-status-btn')) {
            const li = document.createElement('li');
            li.className = 'pda-virus-status-li';
            const a = document.createElement('a');
            a.id = 'pda-virus-status-btn';
            a.className = 'pda-virus-status-icon';
            a.innerHTML = '🦠';
            
            let pressTimer;
            const startPress = () => {
                pressTimer = setTimeout(() => {
                    resetPosition();
                    pressTimer = null;
                }, 1500);
            };
            const endPress = (e) => {
                if (pressTimer) {
                    clearTimeout(pressTimer);
                    handleToggle();
                }
                e.preventDefault();
            };

            a.addEventListener('mousedown', startPress);
            a.addEventListener('mouseup', endPress);
            a.addEventListener('touchstart', (e) => { e.preventDefault(); startPress(); });
            a.addEventListener('touchend', (e) => { e.preventDefault(); endPress(e); });
            
            li.appendChild(a);
            statusUl.appendChild(li);
            created = true;
        }

        if (!document.getElementById('pda-virus-badge')) {
            const badge = document.createElement('div');
            badge.id = 'pda-virus-badge';
            const pos = JSON.parse(localStorage.getItem('pda_virus_pos') || JSON.stringify(defaultPos));
            Object.assign(badge.style, { top: pos.top, left: pos.left });
            badge.onclick = (e) => {
                e.stopPropagation();
                if (!badge.classList.contains('pda-drag-ready')) handleToggle();
            };
            document.body.appendChild(badge);
            setupDrag(badge);
            created = true;
        }

        if (created) syncUI();
    }

    function setupDrag(el) {
        let pressTimer, isDragging = false, dragEnabled = false;
        let startX, startY, initialX, initialY;

        const onStart = (e) => {
            const b = e.type.includes('touch') ? e.touches[0] : e;
            startX = b.clientX; startY = b.clientY;
            const rect = el.getBoundingClientRect();
            initialX = b.clientX - rect.left;
            initialY = b.clientY - rect.top;
            pressTimer = setTimeout(() => {
                dragEnabled = true;
                el.classList.add('pda-drag-ready');
            }, 600);
        };

        const onMove = (e) => {
            const b = e.type.includes('touch') ? e.touches[0] : e;
            if (!dragEnabled) {
                if (Math.abs(b.clientX - startX) > 10 || Math.abs(b.clientY - startY) > 10) clearTimeout(pressTimer);
                return;
            }
            if (e.cancelable) e.preventDefault();
            isDragging = true;
            el.style.left = (b.clientX - initialX) + "px";
            el.style.top = (b.clientY - initialY) + "px";
        };

        const onEnd = () => {
            clearTimeout(pressTimer);
            if (dragEnabled && isDragging) {
                localStorage.setItem('pda_virus_pos', JSON.stringify({ top: el.style.top, left: el.style.left }));
                setTimeout(() => el.classList.remove('pda-drag-ready'), 100);
                isDragging = false;
            } else {
                el.classList.remove('pda-drag-ready');
            }
            dragEnabled = false;
        };

        el.addEventListener('touchstart', onStart, {passive: false});
        window.addEventListener('touchmove', onMove, {passive: false});
        window.addEventListener('touchend', onEnd);
        el.addEventListener('mousedown', onStart);
        window.addEventListener('mousemove', onMove);
        window.addEventListener('mouseup', onEnd);
    }

    async function updateData() {
        const key = getApiKey();
        if (!key || key.includes("PDA-APIKEY")) return;
        try {
            const res = await fetch(`https://api.torn.com/v2/user/virus?key=${key}`);
            const data = await res.json();
            virusUntil = data.virus?.until || 0;
        } catch (e) {}
    }

    function render() {
        const b = document.getElementById('pda-virus-badge');
        if (!b || !isVisible) return;
        
        const now = Math.floor(Date.now() / 1000);
        let text = "VIRUS: IDLE";
        let isWarning = false;

        if (virusUntil > 0) {
            if (virusUntil > now) {
                const s = virusUntil - now;
                isWarning = (s <= 1200);
                const d = Math.floor(s / 86400);
                const h = Math.floor((s % 86400) / 3600);
                const m = Math.floor((s % 3600) / 60);
                text = `VIRUS: ${d > 0 ? d + 'D ' : ''}${h}H ${m}M`;
            } else {
                text = "VIRUS COMPLETE";
                isWarning = true;
            }
        }
        
        if (b.innerText !== text) b.innerText = text;
        b.classList.toggle('pda-warning-pulse', isWarning);
    }

    checkKeyPrompt();
    updateData();
    createUI(); 
    setInterval(updateData, 30000);
    setInterval(render, 1000);
    setInterval(createUI, 2000);
})();