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.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

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

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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;
    }
})();