Torn Territory War Time Left

Shows time left until territory is captured given the current or bestcase attackers & defenders count right underneath war timeout ticker.

安裝腳本?
作者推薦腳本

您可能也會喜歡 Torn Bazaar Filler

安裝腳本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Torn Territory War Time Left
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  Shows time left until territory is captured given the current or bestcase attackers & defenders count right underneath war timeout ticker.
// @author       Ramin Quluzade, Silmaril [2665762]
// @license      MIT License
// @match        https://www.torn.com/factions.php?step=your*
// @match        https://www.torn.com/factions.php?step=profile&ID=*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=torn.com
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    const targetElementSelector = '.f-war-list.war-new';
    const observerOptions = { childList: true, subtree: true };

    const observerCallback = async function(mutationsList, observer) {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                const targetElement = document.querySelector(targetElementSelector);
                if (targetElement) {
                    let territoryWars = mutation.target.querySelectorAll(".f-war-list.war-new div[class^='status-wrap territoryBox']");
                    if (territoryWars.length > 0) {
                        console.log('Target element found!');
                        territoryWars.forEach(war => {
                            war.querySelector('.info .faction-progress-wrap').style.paddingTop = '0px';
                            let timeLeftElement = document.createElement('div');
                            timeLeftElement.classList.add('time-left', 'timer');
                            let timeLeftBestElement = document.createElement('div');
                            timeLeftBestElement.classList.add('time-left-best', 'timer');
                            war.querySelector('.info .faction-progress-wrap').append(timeLeftElement, timeLeftBestElement);
                        });
                        territoryWars.forEach(war => {
                            let enemyCountDiv = war.querySelector('.info .member-count.enemy-count .count');
                            let allyCountDiv = war.querySelector('.info .member-count.your-count .count');

                            renderTimeLeft(war);

                            // Set up a MutationObserver on the added child element
                            const childObserver = new MutationObserver(function(childMutations) {
                                childMutations.forEach(function(childMutation) {
                                    if (childMutation.type === 'characterData') {
                                        let territoryWar = childMutation.target.parentNode.parentNode.parentNode.parentNode;
                                        renderTimeLeft(territoryWar);
                                    }
                                });
                            });

                            setInterval(renderTimeLeft, 1000 + Math.floor(Math.random() * 10) + 1, war);

                            childObserver.observe(enemyCountDiv, { characterData: true, subtree: true });
                            childObserver.observe(allyCountDiv, { characterData: true, subtree: true });
                        });
                        observer.disconnect();
                    }
                }
            }
        }
    };

    const observer = new MutationObserver(observerCallback);
    observer.observe(document.documentElement, observerOptions);
    console.log('Observer started. Waiting for target element to appear...');

    function renderTimeLeft(war) {
        let enemyCountDiv = war.querySelector('.info .member-count.enemy-count .count');
        let allyCountDiv = war.querySelector('.info .member-count.your-count .count');
        let enemyCount = Number(enemyCountDiv.innerText);
        let allyCount = Number(allyCountDiv.innerText);
        let isAllyAttack = war.querySelector('.info .member-count.your-count .count i').classList.contains('swords-icon');
        let remainder = isAllyAttack ? allyCount - enemyCount : enemyCount - allyCount;
        let timeLeft = '??:??:??:??';
        let timeLeftBest = '??:??:??:??';
        let scoreText = war.querySelector('.info .faction-progress-wrap .score').innerText;
        let score = scoreText.replaceAll(',', '').split('/');
        let pointsLeft = Number(score[1]) - Number(score[0]);
        let maximumSlots = Number(score[1]) / 50000;
        if (remainder > 0) {
            let secondsUntilGoal = pointsLeft / remainder;
            timeLeft = convertSecondsToDHMS(secondsUntilGoal);
        }
        timeLeftBest = convertSecondsToDHMS(pointsLeft / maximumSlots);
        let timeLeftDiv = war.querySelector('.info .faction-progress-wrap .time-left');
        let timeLeftBestDiv = war.querySelector('.info .faction-progress-wrap .time-left-best');
        const timeLeftCharacters = timeLeft.split('');
        const timeLeftBestCharacters = timeLeftBest.split('');
        const timeLeftSpanArray = ['CURRENT '];
        timeLeftCharacters.forEach(char => {
            const span = document.createElement('span');
            span.textContent = char;
            timeLeftSpanArray.push(span);
        });
        timeLeftDiv.replaceChildren(...timeLeftSpanArray);
        const timeLeftBestSpanArray = ['BESTCASE '];
        timeLeftBestCharacters.forEach(char => {
            const span = document.createElement('span');
            span.textContent = char;
            timeLeftBestSpanArray.push(span);
        });
        timeLeftBestDiv.replaceChildren(...timeLeftBestSpanArray);
    }

    function convertSecondsToDHMS(seconds) {
        if (seconds === Infinity){
            return '??:??:??:??';
        }

        const oneDay = 86400; // number of seconds in a day
        const oneHour = 3600; // number of seconds in an hour
        const oneMinute = 60; // number of seconds in a minute

        // Calculate the number of days, hours, minutes, and seconds
        const days = Math.floor(seconds / oneDay);
        const hours = Math.floor((seconds % oneDay) / oneHour);
        const minutes = Math.floor((seconds % oneHour) / oneMinute);
        const remainingSeconds = Math.round(seconds % oneMinute);

        // Construct a formatted string with the results
        let output = '';
        output += `${days.toString().padStart(2, '0')}:`;
        output += `${hours.toString().padStart(2, '0')}:`;
        output += `${minutes.toString().padStart(2, '0')}:`;
        output += `${remainingSeconds.toString().padStart(2, '0')}`;

        return output;
    }
})();