KIRA NEXUS

Airstrip Prediction + Maximum Layer Priority + UI Recovery

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

You will need to install an extension such as Tampermonkey to install this script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         KIRA NEXUS
// @namespace    Gemini.Torn
// @version      1.1.0
// @description  Airstrip Prediction + Maximum Layer Priority + UI Recovery
// @author       Kira
// @license      MIT
// @match        *://www.torn.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @run-at       document-end
// ==/UserScript==

(function() {
    'use strict';

    // Airstrip Flight Times (Total Seconds)
    const FLIGHT_SECONDS = {
        "Mexico": 1080, "Cayman Islands": 1500, "Canada": 1740,
        "Hawaii": 5640, "United Kingdom": 6660, "Switzerland": 7380,
        "Argentina": 7020, "Japan": 9480, "China": 10140,
        "UAE": 11400, "South Africa": 12480
    };

    const AIRSTRIP_DATA = {
        "Mexico": 600000, "Cayman Islands": 500000, "Canada": 550000,
        "Hawaii": 700000, "United Kingdom": 600000, "Argentina": 650000,
        "Switzerland": 1200000, "Japan": 800000, "China": 1100000,
        "UAE": 1000000, "South Africa": 1300000
    };

    const DEFAULTS = { apiKey: "", dayName: "United Kingdom", sleepName: "China", profitMode: "market", traderRate: 98 };
    let config = JSON.parse(GM_getValue("kira_config", JSON.stringify(DEFAULTS)));
    let stats = JSON.parse(GM_getValue("kira_stats", `{"date":"${new Date().toLocaleDateString()}", "dailyProfit":0}`));
    let isMinimized = GM_getValue("kira_minimized", false);
    
    const savedPos = JSON.parse(GM_getValue("boxPos", '{"top":"150px", "left":"10px"}'));
    const savedSize = JSON.parse(GM_getValue("boxSize", '{"width":"260px", "height":"auto"}'));
    
    let cachedTimeLeft = 0;
    let lastSync = Date.now();
    let currentLoc = "Syncing...";

    // MAXIMIZED Z-INDEX AND LAYER FIXES
    GM_addStyle(`
        #kira-tracker { 
            position: fixed !important; 
            top: ${savedPos.top}; left: ${savedPos.left}; 
            width: ${isMinimized ? '140px' : savedSize.width}; 
            min-width: ${isMinimized ? '140px' : '220px'}; 
            background: #121212; color: #e0e0e0; padding: ${isMinimized ? '10px 15px' : '20px'}; 
            border-radius: ${isMinimized ? '30px' : '20px'}; 
            font-family: 'Inter', -apple-system, sans-serif; 
            z-index: 999999999 !important; /* Maximum safe z-index */
            border: 1px solid rgba(255, 255, 255, 0.15); 
            box-shadow: 0 10px 40px rgba(0, 0, 0, 0.8);
            overflow: hidden; display: flex; flex-direction: column;
            opacity: 1 !important;
            touch-action: none;
            pointer-events: auto !important;
            -webkit-transform: translate3d(0,0,0); /* Force Hardware Acceleration for layering */
        }
        #top-bar { display: flex; align-items: center; margin-bottom: ${isMinimized ? '0' : '18px'}; cursor: grab; user-select: none; padding: 5px 0; }
        #status-dot { 
            width: 10px; height: 10px; border-radius: 50%; 
            background: #00ff9f; margin-right: 12px; 
            box-shadow: 0 0 12px #00ff9f;
            animation: kira-pulse 2s infinite ease-in-out;
        }
        @keyframes kira-pulse {
            0% { opacity: 1; transform: scale(1); box-shadow: 0 0 12px #00ff9f; }
            50% { opacity: 0.7; transform: scale(0.95); box-shadow: 0 0 18px #00ff9f; }
            100% { opacity: 1; transform: scale(1); box-shadow: 0 0 12px #00ff9f; }
        }
        #title-text { flex-grow: 1; font-weight: 700; font-size: 13px; letter-spacing: 1px; color: #fff; text-transform: uppercase; display: ${isMinimized ? 'none' : 'block'}; }
        .nav-icons { display: flex; gap: 8px; }
        .nav-btn { cursor: pointer; color: #666; font-size: 18px; transition: color 0.2s; padding: 5px; font-weight: bold; }
        #tracker-body { display: ${isMinimized ? 'none' : 'block'}; }
        #mini-timer { display: ${isMinimized ? 'block' : 'none'}; font-size: 14px; font-weight: 700; color: #fff; margin-right: 10px; }
        .stat-group { margin-bottom: 15px; }
        .stat-label { font-size: 9px; text-transform: uppercase; color: #888; letter-spacing: 1px; margin-bottom: 4px; display: block; }
        .stat-value { font-size: 18px; font-weight: 600; color: #fff; }
        .stat-sub { font-size: 10px; color: #555; margin-left: 8px; cursor: pointer; text-transform: uppercase; padding: 5px; }
        #loc-display { color: #00d4ff; font-size: 12px; font-weight: 800; }
        #profit-display { color: #00ff9f; font-size: 20px; }
        .action-btn { 
            width: 100%; font-size: 10px; margin-top: 8px; cursor: pointer; 
            background: #1a1a1a; color: #aaa; border: 1px solid #333; 
            padding: 12px; border-radius: 10px; font-weight: 700; text-transform: uppercase;
        }
        #settings-menu { display: none; margin-top: 15px; padding-top: 15px; border-top: 1px solid #333; }
        .kira-input { background: #000; color: #fff; border: 1px solid #444; width: 100%; margin-bottom: 10px; font-size: 16px; padding: 10px; border-radius: 8px; }
    `);

    async function syncAPI() {
        if (!config.apiKey) return;
        try {
            const res = await fetch(`https://api.torn.com/user/?selections=travel&key=${config.apiKey}`);
            const data = await res.json();
            if (data && data.travel) {
                cachedTimeLeft = data.travel.time_left;
                currentLoc = data.travel.destination || "Torn City";
                lastSync = Date.now();
                GM_setValue("last_travel_state", {time: cachedTimeLeft, stamp: lastSync, loc: currentLoc});
            }
        } catch (e) {}
    }

    function setupUI() {
        if (document.getElementById('kira-tracker')) return;
        const box = document.createElement('div');
        box.id = 'kira-tracker';
        const getOptions = (sel) => Object.keys(AIRSTRIP_DATA).map(l => `<option value="${l}" ${l===sel?'selected':''}>${l}</option>`).join('');

        box.innerHTML = `
            <div id="top-bar">
                <div id="status-dot"></div>
                <div id="mini-timer">00:00:00</div>
                <div id="title-text">Kira Nexus</div>
                <div class="nav-icons">
                    <div id="toggle-mini" class="nav-btn">${isMinimized ? '+' : '−'}</div>
                    <div id="open-settings" class="nav-btn">⚙</div>
                </div>
            </div>
            <div id="tracker-body">
                <div class="stat-group"><span class="stat-label">Timer</span><div id="time-display" class="stat-value">00:00:00</div><span id="manual-sync" class="stat-sub">Sync</span></div>
                <div class="stat-group"><span class="stat-label">Arrival Window</span><div id="land-display" style="font-size: 14px; color: #bfef45;">--:--:--</div></div>
                <div class="stat-group" style="background: #1a1a1a; padding: 10px; border-radius: 10px; border-left: 3px solid #00d4ff;"><span class="stat-label" style="color: #00d4ff;">Current Sector</span><div id="loc-display">SYNCING...</div></div>
                <div class="stat-group"><span class="stat-label" id="profit-mode-label">Session Profit</span><div id="profit-display" class="stat-value">$0.00M</div></div>
                <button id="add-profit" class="action-btn">Log Transaction</button>
                <button id="mode-swap" class="action-btn">Switch Route</button>
            </div>
            <div id="settings-menu">
                <span class="stat-label">API Key</span><input id="set-api" class="kira-input" value="${config.apiKey}">
                <span class="stat-label">Standard</span><select id="set-day" class="kira-input">${getOptions(config.dayName)}</select>
                <span class="stat-label">Extended</span><select id="set-sleep" class="kira-input">${getOptions(config.sleepName)}</select>
                <span class="stat-label">Valuation</span>
                <select id="set-method" class="kira-input">
                    <option value="market" ${config.profitMode === 'market' ? 'selected' : ''}>Market (-5%)</option>
                    <option value="trader" ${config.profitMode === 'trader' ? 'selected' : ''}>Trader (%)</option>
                </select>
                <div id="trader-rate-wrap"><span class="stat-label">Trader Ratio %</span><input id="set-rate" type="number" class="kira-input" value="${config.traderRate}"></div>
                <button id="save-settings" class="action-btn" style="background: #fff; color: #000;">Save & Apply</button>
                <button id="reset-ui" class="action-btn" style="background: #ff4444; color: #fff; margin-top: 20px;">Emergency UI Reset</button>
            </div>
        `;
        document.body.appendChild(box);

        // Movement logic
        const topBar = document.getElementById('top-bar');
        topBar.addEventListener('pointerdown', (e) => {
            if (e.target.closest('.nav-btn')) return;
            box.setPointerCapture(e.pointerId);
            let shiftX = e.clientX - box.getBoundingClientRect().left, shiftY = e.clientY - box.getBoundingClientRect().top;
            const onMove = (ev) => {
                let newX = Math.max(0, Math.min(window.innerWidth - box.offsetWidth, ev.clientX - shiftX));
                let newY = Math.max(0, Math.min(window.innerHeight - box.offsetHeight, ev.clientY - shiftY));
                box.style.left = newX + 'px'; box.style.top = newY + 'px'; 
            };
            const onUp = () => {
                window.removeEventListener('pointermove', onMove);
                GM_setValue("boxPos", JSON.stringify({top: box.style.top, left: box.style.left}));
            };
            window.addEventListener('pointermove', onMove);
            window.addEventListener('pointerup', onUp, {once: true});
        });

        // UI Controls
        document.getElementById('toggle-mini').onclick = () => { isMinimized = !isMinimized; GM_setValue("kira_minimized", isMinimized); location.reload(); };
        document.getElementById('open-settings').onclick = () => { const m = document.getElementById('settings-menu'); m.style.display = (m.style.display === 'block') ? 'none' : 'block'; };
        document.getElementById('reset-ui').onclick = () => { GM_setValue("boxPos", '{"top":"150px", "left":"10px"}'); location.reload(); };
        document.getElementById('save-settings').onclick = () => {
            config.apiKey = document.getElementById('set-api').value;
            config.dayName = document.getElementById('set-day').value;
            config.sleepName = document.getElementById('set-sleep').value;
            config.profitMode = document.getElementById('set-method').value;
            config.traderRate = parseFloat(document.getElementById('set-rate').value) || 98;
            GM_setValue("kira_config", JSON.stringify(config)); location.reload();
        };

        document.getElementById('add-profit').onclick = async () => {
            if (!config.apiKey) return alert("API Key Required");
            try {
                const res = await fetch(`https://api.torn.com/user/?selections=basic&key=${config.apiKey}`);
                const data = await res.json();
                if (data.level < 15) return alert("International Deals are locked until Level 15.");
                const dest = GM_getValue("isSleep", false) ? config.sleepName : config.dayName;
                let finalProfit = config.profitMode === 'market' ? (AIRSTRIP_DATA[dest] || 0) * 0.95 : (AIRSTRIP_DATA[dest] || 0) * (config.traderRate / 100);
                stats.dailyProfit += finalProfit;
                GM_setValue("kira_stats", JSON.stringify(stats));
            } catch (e) { alert("API Error."); }
        };

        document.getElementById('mode-swap').onclick = () => { GM_setValue("isSleep", !GM_getValue("isSleep", false)); render(); };
        document.getElementById('manual-sync').onclick = () => syncAPI();
    }

    function render() {
        if (!document.getElementById('kira-tracker')) setupUI();
        const globalState = GM_getValue("last_travel_state", {time: 0, stamp: Date.now(), loc: "SYNCING..."});
        const isFlying = globalState.time > 0;
        const currentRoute = GM_getValue("isSleep", false) ? config.sleepName : config.dayName;
        
        let remaining = isFlying ? Math.max(0, globalState.time - Math.floor((Date.now() - globalState.stamp) / 1000)) : (FLIGHT_SECONDS[currentRoute] || 0);
        let arrivalTimestamp = isFlying ? globalState.stamp + (globalState.time * 1000) : Date.now() + (remaining * 1000);

        const pad = (n) => n.toString().padStart(2, '0');
        const timeStr = `${pad(Math.floor(remaining / 3600))}:${pad(Math.floor((remaining % 3600) / 60))}:${pad(remaining % 60)}`;
        
        if (document.getElementById('time-display')) document.getElementById('time-display').innerText = timeStr;
        if (document.getElementById('mini-timer')) document.getElementById('mini-timer').innerText = timeStr;
        if (document.getElementById('land-display')) document.getElementById('land-display').innerText = new Date(arrivalTimestamp).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: true });
        if (document.getElementById('loc-display')) document.getElementById('loc-display').innerText = globalState.loc.toUpperCase();
        if (document.getElementById('profit-display')) document.getElementById('profit-display').innerText = `$${(stats.dailyProfit / 1000000).toFixed(2)}M`;
        if (document.getElementById('mode-swap')) document.getElementById('mode-swap').innerText = `ROUTE: ${currentRoute}`.toUpperCase();
    }

    // High-frequency observer to fight Torn's dynamic UI refreshes
    const observer = new MutationObserver(() => { if (!document.getElementById('kira-tracker')) setupUI(); });
    observer.observe(document.body, { childList: true, subtree: true });

    setupUI(); syncAPI();
    setInterval(syncAPI, 30000); setInterval(render, 1000);
})();