Torn Execute Indicator

Shows EXECUTE indicator when opponent can be executed

Versión del día 16/09/2025. Echa un vistazo a la versión más reciente.

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

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

Necesitarás 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.

Necesitará instalar una extensión como Tampermonkey para 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)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

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

// ==UserScript==
// @name         Torn Execute Indicator
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Shows EXECUTE indicator when opponent can be executed
// @author       PedroXimenez
// @match        *://www.torn.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // Check if we're on an attack page (loader.php?sid=attack)
    if (!window.location.pathname.includes('loader.php')) {
        return; // Exit if not on loader.php
    }

    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.get('sid') !== 'attack') {
        return; // Exit if not on attack page
    }

    function canExecuteTarget(html) {
        // Extract execute percentage (e.g., "15%" from "below 15% life")
        const executeMatch = html.match(/below\s+(\d+)%\s+life/i);
        if (!executeMatch) {
            return false; // No execute threshold found
        }
        const executeThreshold = parseInt(executeMatch[1]);

        // Extract ALL health values
        const healthMatches = Array.from(html.matchAll(/>(\d{1,3}(?:,\d{3})*)[\s]*\/[\s]*(\d{1,3}(?:,\d{3})*)</g));

        if (healthMatches.length < 2) {
            return false; // Need at least 2 health values (player and opponent)
        }

        // Take the second health value (opponent's health)
        const opponentMatch = healthMatches[1];
        const opponentHealth = {
            current: parseInt(opponentMatch[1].replace(/,/g, '')),
            max: parseInt(opponentMatch[2].replace(/,/g, ''))
        };

        // Calculate life percentage
        const lifePercentage = (opponentHealth.current / opponentHealth.max) * 100;

        // Check if life percentage is at or below the execute threshold
        const canExecute = lifePercentage <= executeThreshold;

        // Debug logging
        console.log(`Execute threshold: ${executeThreshold}%`);
        console.log(`Opponent health: ${opponentHealth.current}/${opponentHealth.max}`);
        console.log(`Opponent life percentage: ${lifePercentage.toFixed(2)}%`);
        console.log(`Can execute: ${canExecute}`);

        // Add EXECUTE indicator to the figure if can execute
        if (canExecute) {
            // Find the figure element that contains the execute weapon
            const executeElement = document.querySelector('[data-bonus-attachment-description*="below"][data-bonus-attachment-description*="life"]');
            if (executeElement) {
                // Find the nearest figure element (should be a sibling or nearby)
                const weaponWrapper = executeElement.closest('.weaponWrapper___h3buK');
                if (weaponWrapper) {
                    const figure = weaponWrapper.querySelector('figure');
                    if (figure && !figure.querySelector('.execute-indicator')) {
                        const indicator = document.createElement('div');
                        indicator.className = 'execute-indicator';
                        indicator.textContent = 'EXECUTE';
                        indicator.style.cssText = `
                            position: absolute;
                            top: 50%;
                            left: 50%;
                            transform: translate(-50%, -50%);
                            color: red;
                            font-weight: bold;
                            font-size: 20px;
                            text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
                            pointer-events: none;
                            z-index: 1000;
                        `;
                        figure.style.position = 'relative';
                        figure.appendChild(indicator);
                    }
                }
            }
        }

        return canExecute;
    }

    // Function to remove existing EXECUTE indicators
    function removeExecuteIndicators() {
        const indicators = document.querySelectorAll('.execute-indicator');
        indicators.forEach(indicator => indicator.remove());
    }

    // Function to run the check and update display
    function checkAndUpdate() {
        removeExecuteIndicators(); // Clear any existing indicators
        const result = canExecuteTarget(document.documentElement.innerHTML);
        return result;
    }

    // Set up continuous monitoring
    function startMonitoring(intervalMs = 250) {
        // First check if there's an execute attachment on the page
        const hasExecuteAttachment = document.querySelector('[data-bonus-attachment-description*="below"][data-bonus-attachment-description*="life"]');

        if (!hasExecuteAttachment) {
            console.log('No execute attachment found on this page - monitoring not started');
            return null;
        }

        // Initial check
        checkAndUpdate();

        // Set up interval for continuous checking
        const intervalId = setInterval(checkAndUpdate, intervalMs);

        console.log(`Execute monitor started (checking every ${intervalMs}ms)`);
        console.log('To stop monitoring, run: stopMonitoring()');

        // Store the interval ID globally so it can be stopped
        window.executeMonitorInterval = intervalId;

        return intervalId;
    }

    // Function to stop monitoring
    function stopMonitoring() {
        if (window.executeMonitorInterval) {
            clearInterval(window.executeMonitorInterval);
            removeExecuteIndicators();
            console.log('Execute monitor stopped');
            delete window.executeMonitorInterval;
        }
    }

    // Wait for page to load before starting monitoring
    // Torn dynamically loads content, so we need to wait a bit
    setTimeout(() => {
        startMonitoring();
    }, 5000); // Wait 2 seconds for page content to load

    // Also try to start monitoring when the DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => {
            setTimeout(startMonitoring, 1000);
        });
    }

})();