Show NPC Time Til Next Level at Loader

Shows the time til the next level for NPCs at the attack loader page. Uses https://api.lzpt.io/loot to get the data. Also modifies tab title, disable by modifying IF_UPDATE_TAB_TITLE to false.

目前為 2024-12-05 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         Show NPC Time Til Next Level at Loader
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Shows the time til the next level for NPCs at the attack loader page. Uses https://api.lzpt.io/loot to get the data. Also modifies tab title, disable by modifying IF_UPDATE_TAB_TITLE to false.
// @author       Hesper [2924630]
// @include      https://www.torn.com/loader.php?sid=attack&user2ID=*
// @grant        GM.xmlHttpRequest
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // Displays time remaining until the next level in the tab title.
    // Set to false to disable
    const IF_UPDATE_TAB_TITLE = true;

    const validIDs = [10, 20, 21, 4, 19, 15, 17];

    // Function to fetch JSON data
    function fetchData() {
        return new Promise((resolve, reject) => {
            GM.xmlHttpRequest({
                method: 'GET',
                url: 'https://api.lzpt.io/loot',
                onload: function(response) {
                    if (response.status >= 200 && response.status < 300) {
                        try {
                            const data = JSON.parse(response.responseText);
                            resolve(data);
                        } catch (e) {
                            reject(e);
                        }
                    } else {
                        reject(new Error(`HTTP error! status: ${response.status}`));
                    }
                },
                onerror: function(err) {
                    reject(err);
                }
            });
        });
    }

    function calculateTimeRemaining(currentTime, hospOutTime) {
        const levelTimes = [0, 30, 90, 210, 450]; // Minutes after hosp_out for each level
        let currentLevel = 0;
        let timeRemaining = 0;

        if (currentTime < hospOutTime) {
            timeRemaining = hospOutTime - currentTime;
        } else {
            for (let level = 1; level <= 5; level++) {
                const targetTime = hospOutTime + levelTimes[level - 1] * 60; // Convert minutes to seconds
                timeRemaining = targetTime - currentTime;
                if (timeRemaining > 0) {
                    currentLevel = level;
                    break;
                }
            }
        }

        return { currentLevel, timeRemaining: timeRemaining > 0 ? timeRemaining : 0 };
    }

    function formatTime(seconds) {
        const minutes = Math.floor(seconds / 60);
        const hours = Math.floor(minutes / 60);
        const remainingMinutes = minutes % 60;
        const remainingSeconds = seconds % 60;
        if (hours === 0) {
            if (remainingMinutes === 0) {
                return `${remainingSeconds}s`;
            }
            return `${remainingMinutes}m ${remainingSeconds}s`;
        }
        return `${hours}h ${remainingMinutes}m ${remainingSeconds}s`;
    }

    function updateDiv(npc, currentTime) {

        const hospOutTime = npc.hosp_out;
        const { currentLevel, timeRemaining } = calculateTimeRemaining(currentTime, hospOutTime);
        let level = currentLevel;

        const targetDiv = document.querySelector('.titleContainer___QrlWP');
        if (targetDiv) {
            let newDiv = document.querySelector('#npc-level-info');
            if (!newDiv) {
                newDiv = document.createElement('div');
                newDiv.id = 'npc-level-info';
                newDiv.style.marginTop = '15px';
                newDiv.style.marginRight = '10px';
                newDiv.style.textAlign = 'right';
                newDiv.style.fontSize = '1em';
                newDiv.style.lineHeight = '1.2em';

                targetDiv.parentNode.insertBefore(newDiv, targetDiv.nextSibling);
            }
            if (level < 5) {
                newDiv.innerHTML = `Currently <b>Level ${level}</b><br />Time til Level ${level + 1}: <b>${formatTime(timeRemaining)}</b>`;
                if (IF_UPDATE_TAB_TITLE) document.title = `${formatTime(timeRemaining)} til Level ${level + 1} - ${npc.name}`;
            } else {
                newDiv.innerHTML = `Currently <b>Level ${level}</b>`;
                if (IF_UPDATE_TAB_TITLE) document.title = `Level ${level} -  ${npc.name}`;
            }
        }
    }

    async function startUpdating(user2ID) {
        // Check if the user2ID is valid
        const npcData = await fetchData();
        const npc = npcData.npcs[user2ID];
        if (!npc) return;

        // Start updating the timer every second
        setInterval(() => {
            const currentTime = Math.floor(Date.now() / 1000);
            updateDiv(npc, currentTime);
        }, 1000);
    }

    window.addEventListener('load', () => {
        // check if the user2ID is valid
        const user2ID = parseInt(new URLSearchParams(window.location.search).get('user2ID'), 10);
        if (validIDs.includes(user2ID)) {
            startUpdating(user2ID);
        }
    });
})();