Torn: More Legible Player Names With Custom Color Outlines

More Legible Font and custom color outlines for player names on honor bars. No API key required. Set your player ID manually for full reliability.

// ==UserScript==
// @name         Torn: More Legible Player Names With Custom Color Outlines
// @namespace    http://tampermonkey.net/
// @version      2
// @description  More Legible Font and custom color outlines for player names on honor bars. No API key required. Set your player ID manually for full reliability.
// @author       Gingerbeardman
// @match        https://www.torn.com/*
// @grant        none
// @license      GNU GPLv3
// ==/UserScript==

(function () {
    'use strict';

    const MY_PLAYER_ID = 'USER ID #'; // ← Your player ID here
    const CONFIG_KEY = 'mlpn-config';

    const COLOR_OPTIONS = [
        { name: 'Black (Default)', value: '#000000' },
        { name: 'Red', value: '#ff4d4d' },
        { name: 'Blue', value: '#310AF5' },
        { name: 'Green', value: '#3B9932' },
        { name: 'Orange', value: '#ff9c40' },
        { name: 'Purple', value: '#c080ff' },
        { name: 'Yellow', value: '#f5d142' },
        { name: 'Pink', value: '#ff69b4' },
        { name: 'Teal', value: '#00d9c0' },
        { name: 'White', value: '#ffffff' },
        { name: 'Custom…', value: 'custom' }
    ];

    const loadFont = () => {
        const link = document.createElement('link');
        link.href = 'https://fonts.googleapis.com/css2?family=Manrope:wght@700&display=swap';
        link.rel = 'stylesheet';
        document.head.appendChild(link);
    };

    const injectStyles = () => {
        const style = document.createElement('style');
        style.textContent = COLOR_OPTIONS.filter(c => c.value !== 'custom').map(c => {
            const hex = c.value.replace('#', '');
            return `.mlpn-color-${hex} .custom-honor-text {
                text-shadow:
                    -1px -1px 0 ${c.value},
                     1px -1px 0 ${c.value},
                    -1px  1px 0 ${c.value},
                     1px  1px 0 ${c.value} !important;
            }`;
        }).join('\n') + `
            .custom-honor-text {
                font-family: 'Manrope', sans-serif !important;
                font-weight: 700 !important;
                color: white !important;
                text-transform: uppercase !important;
                letter-spacing: 0.5px !important;
                pointer-events: none !important;
                position: absolute !important;
                top: 50%;
                left: 0;
                transform: translateY(-50%);
                width: 100% !important;
                display: flex !important;
                align-items: center;
                justify-content: center;
                text-align: center !important;
                line-height: 1 !important;
                margin: 0 !important;
                padding: 0 !important;
                z-index: 10 !important;
            }

            .honor-text-svg {
                display: none !important;
            }

            .mlpn-panel {
                position: absolute;
                top: 50px;
                left: 50%;
                transform: translateX(-50%);
                background: #222;
                color: white;
                border: 1px solid #444;
                padding: 12px;
                z-index: 99999;
                font-size: 14px;
                border-radius: 6px;
                width: max-content;
            }

            .mlpn-panel label {
                display: block;
                margin-top: 10px;
            }

            .mlpn-button {
                background: #339CFF;
                color: white;
                border: none;
                padding: 6px 10px;
                font-weight: bold;
                border-radius: 4px;
                cursor: pointer;
                margin-top: 10px;
            }

            .mlpn-note {
                font-size: 12px;
                margin-top: 6px;
                color: #aaa;
            }
        `;
        document.head.appendChild(style);
    };

    const getConfig = () => JSON.parse(localStorage.getItem(CONFIG_KEY) || '{}');
    const saveConfig = config => localStorage.setItem(CONFIG_KEY, JSON.stringify(config));

    const createColorDropdown = (id, selectedValue) => {
        const select = document.createElement('select');
        select.id = id;
        COLOR_OPTIONS.forEach(c => {
            const opt = document.createElement('option');
            opt.value = c.value;
            opt.textContent = c.name;
            select.appendChild(opt);
        });
        select.value = COLOR_OPTIONS.some(c => c.value === selectedValue) ? selectedValue : 'custom';
        return select;
    };

    const showSettingsPanel = (isOwn, playerId) => {
        const config = getConfig();
        const panel = document.createElement('div');
        panel.id = 'mlpn-panel';
        panel.className = 'mlpn-panel';

        const closeBtn = document.createElement('button');
        closeBtn.className = 'mlpn-button';
        closeBtn.textContent = 'Done';
        closeBtn.onclick = () => panel.remove();

        if (isOwn) {
            panel.innerHTML = `
                <label>Font Size:
                    <input id="mlpn-font-size" type="number" min="8" max="24" value="${config.fontSize || 12}" />
                </label>
                <label>Color for Your Name:</label>
            `;
            const dropdown = createColorDropdown('mlpn-my-color', config.myColor || '#000000');
            const customInput = document.createElement('input');
            customInput.id = 'mlpn-my-custom';
            customInput.placeholder = '#hex';
            customInput.style.display = dropdown.value === 'custom' ? 'block' : 'none';
            customInput.value = config.myColor?.startsWith('#') ? config.myColor : '';

            dropdown.onchange = () => {
                customInput.style.display = dropdown.value === 'custom' ? 'block' : 'none';
                config.myColor = dropdown.value === 'custom' ? customInput.value : dropdown.value;
                saveConfig(config);
            };

            customInput.oninput = () => {
                config.myColor = customInput.value;
                saveConfig(config);
            };

            const fontInput = panel.querySelector('#mlpn-font-size');
            fontInput.oninput = () => {
                config.fontSize = parseInt(fontInput.value);
                saveConfig(config);
            };

            document.body.appendChild(panel);
            panel.appendChild(dropdown);
            panel.appendChild(customInput);
        } else {
            panel.innerHTML = `<label>Assign a color to this player's name:</label>`;
            const dropdown = createColorDropdown('mlpn-other-color', config.players?.[playerId] || '#000000');
            const customInput = document.createElement('input');
            customInput.id = 'mlpn-other-custom';
            customInput.placeholder = '#hex';
            customInput.style.display = dropdown.value === 'custom' ? 'block' : 'none';
            customInput.value = config.players?.[playerId]?.startsWith('#') ? config.players[playerId] : '';

            dropdown.onchange = () => {
                if (!config.players) config.players = {};
                customInput.style.display = dropdown.value === 'custom' ? 'block' : 'none';
                config.players[playerId] = dropdown.value === 'custom' ? customInput.value : dropdown.value;
                saveConfig(config);
            };

            customInput.oninput = () => {
                if (!config.players) config.players = {};
                config.players[playerId] = customInput.value;
                saveConfig(config);
            };

            document.body.appendChild(panel);
            panel.appendChild(dropdown);
            panel.appendChild(customInput);
        }

        const note = document.createElement('div');
        note.className = 'mlpn-note';
        note.textContent = 'Refresh the page to apply settings.';
        panel.appendChild(note);
        panel.appendChild(closeBtn);
    };

    const getProfileIdFromUrl = () => {
        const match = window.location.href.match(/profiles\.php\?XID=(\d+)/);
        return match ? match[1] : null;
    };

    const applyHonorStyles = () => {
        const config = getConfig();
        const fontSize = parseInt(config.fontSize) || 12;
        const profileId = getProfileIdFromUrl();

        document.querySelectorAll('.honor-text-wrap').forEach(wrap => {
            if (wrap.querySelector('.custom-honor-text')) return;

            const anchor = wrap.closest('a[href*="XID="]');
            let playerId = null;

            if (anchor) {
                const match = anchor.href.match(/XID=(\d+)/);
                if (match) playerId = match[1];
            } else if (profileId) {
                playerId = profileId; // Fallback for profile pages
            }

            const text = wrap.getAttribute('data-title') || wrap.getAttribute('aria-label') || wrap.innerText || '';
            const cleaned = text.trim().toUpperCase();
            if (!cleaned) return;

            let color = '#000000';
            if (playerId === MY_PLAYER_ID && config.myColor) {
                color = config.myColor;
            } else if (config.players?.[playerId]) {
                color = config.players[playerId];
            }

            const colorClass = `mlpn-color-${color.replace('#', '')}`;
            wrap.classList.add(colorClass);

            if (fontSize > 12) wrap.style.height = `${fontSize + 6}px`;

            const div = document.createElement('div');
            div.className = 'custom-honor-text';
            div.style.fontSize = `${fontSize}px`;
            div.textContent = cleaned;
            wrap.appendChild(div);
        });
    };

    const injectSettingsButton = (isSelfProfile, profileId) => {
        if (document.getElementById('mlpn-settings-btn')) return;

        const target = document.querySelector('.content-title');
        if (!target) return;

        const btn = document.createElement('button');
        btn.id = 'mlpn-settings-btn';
        btn.className = 'mlpn-button';
        btn.textContent = 'Custom Player Names';
        btn.onclick = () => showSettingsPanel(isSelfProfile, profileId);
        target.appendChild(btn);
    };

    const init = () => {
        const profileId = getProfileIdFromUrl();
        if (profileId) {
            injectSettingsButton(profileId === MY_PLAYER_ID, profileId);
        }

        applyHonorStyles();
        new MutationObserver(applyHonorStyles).observe(document.body, { childList: true, subtree: true });
    };

    loadFont();
    injectStyles();
    init();
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址