Torn Advanced Predictor Names

Adds predicted success % next to player names on Torn pages

// ==UserScript==
// @name         Torn Advanced Predictor Names
// @namespace    https://yournamespace.example.com/torn
// @version      1.2.0
// @description  Adds predicted success % next to player names on Torn pages
// @match        https://www.torn.com/*
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    const AGGRESSION_LEVEL = 1.2;
    const SPY_RECENT_DAYS = 30;

    function getTargets() {
        if(typeof GetLocalBattleStats !== "undefined") return GetLocalBattleStats().targets || [];
        return [];
    }

    function getImportedSpies() {
        if(typeof ImportedSpies !== "undefined") return ImportedSpies;
        return [];
    }

    function getFightHistory() {
        if(typeof fightHistory !== "undefined") return fightHistory;
        return [];
    }

    function computeScore(target, spies, history){
        let baseScore = target.Score || target.TBS || 0;

        let spy = spies.find(s => s.playerId === target.id);
        let spyModifier = 1;
        if(spy){
            let days = (Date.now() - new Date(spy.date))/86400000;
            if(days <= SPY_RECENT_DAYS) spyModifier += 0.5*(1 - days/SPY_RECENT_DAYS);
        }

        let pastFight = history.find(f => f.targetId === target.id);
        let fightModifier = 1;
        if(pastFight) fightModifier += pastFight.won ? 0.1 : -0.2;

        return baseScore * spyModifier * fightModifier * AGGRESSION_LEVEL;
    }

    function appendPercentage() {
        const targets = getTargets();
        const spies = getImportedSpies();
        const history = getFightHistory();

        const links = Array.from(document.querySelectorAll('a[href*="profile.php"], a[href*="index.php?XID=profile&ID="]'));

        links.forEach(link => {
            let match = link.href.match(/(?:XID=profile&ID=|profile\.php\?XID=)(\d+)/);
            if(!match) return;
            let playerId = parseInt(match[1]);
            let targetData = targets.find(t => t.id === playerId) || {id:playerId, Score:0};
            let score = computeScore(targetData, spies, history);
            let percent = Math.round(Math.min(100, score / 20000 * 100)); // scale to 100% for visibility

            // Remove old percent if exists
            let oldSpan = link.parentElement.querySelector('.predictorPercent');
            if(oldSpan) oldSpan.remove();

            // Create new span
            let span = document.createElement('span');
            span.className = 'predictorPercent';
            span.innerText = ` [${percent}%]`;
            span.style.color = percent > 75 ? '#00FF00' : percent > 50 ? '#FFFF00' : percent > 25 ? '#FFA500' : '#FF0000';
            span.style.fontWeight = 'bold';

            link.parentElement.appendChild(span);
        });
    }

    const observer = new MutationObserver(appendPercentage);
    observer.observe(document.body, { childList: true, subtree: true });

    appendPercentage();
    setInterval(appendPercentage, 15000);

})();