Move Start Button (Torn)

Moves "Start Fight", forces profile attacks, strips heavy elements, and supports Torn PDA mobile app.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

Advertisement:

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

Advertisement:

// ==UserScript==
// @name         Move Start Button (Torn)
// @namespace    https://github.com/0xymandias
// @version      3.41
// @description  Moves "Start Fight", forces profile attacks, strips heavy elements, and supports Torn PDA mobile app.
// @author       smokey_ [2492729] & Fixed
// @match        https://www.torn.com/*
// @grant        GM_addStyle
// ==/UserScript==

(function () {
    'use strict';

    // ========================================================================
    // SETTINGS
    // ========================================================================
    
    // Change this value to 'Temp', 'Primary', 'Secondary', or 'Melee'
    const BUTTON_LOCATION = 'Primary'; 
    
    // Performance Toggles
    const ULTRA_PERFORMANCE_MODE = true;
    const HIDE_ENEMY_COMPLETELY = false;

    // ========================================================================
    // PERFORMANCE INJECTIONS
    // ========================================================================
    if (ULTRA_PERFORMANCE_MODE) {
        let perfCSS = `
            /* Instantly hide heavy graphics before they render */
            canvas, img[src*="models"], [class*="modelWrap"], .custom-bg-desktop {
                display: none !important;
            }
        `;
        if (HIDE_ENEMY_COMPLETELY) {
            perfCSS += `#defender { display: none !important; }`;
        }
        GM_addStyle(perfCSS);
    }

    // ========================================================================
    // FEATURE 1: Move the "Start Fight" Button
    // ========================================================================
    
    // Smart Locator to handle Desktop, Mobile, and Torn PDA layouts
    function getWeaponBox(locationName) {
        let targetText = locationName.toLowerCase();
        if (targetText === 'temp') targetText = 'temporary';

        // METHOD 1: Text Search (Standard Desktop UI)
        const allText = document.querySelectorAll('div, span, p');
        for (let el of allText) {
            if (el.children.length === 0) {
                const text = (el.textContent || '').trim().toLowerCase();
                if (text === targetText) {
                    const rect = el.getBoundingClientRect();
                    // Ensure it's on the left side (attacker side)
                    if (rect.left >= 0 && rect.left < window.innerWidth / 2) {
                        let parent = el.parentElement;
                        for (let i = 0; i < 6; i++) {
                            if (parent && parent.querySelector('img[src*="/items/"]')) {
                                return { wrapper: parent, image: parent.querySelector('img[src*="/items/"]') };
                            }
                            if (parent) parent = parent.parentElement;
                        }
                    }
                }
            }
        }

        // METHOD 2: Aria-Labels (Modern Accessibility UI)
        const ariaEls = document.querySelectorAll(`[aria-label*="${targetText}" i]`);
        for (let el of ariaEls) {
            const rect = el.getBoundingClientRect();
            if (rect.left >= 0 && rect.left < window.innerWidth / 2) {
                const img = el.querySelector('img[src*="/items/"]');
                if (img) return { wrapper: el, image: img };
            }
        }

        // METHOD 3: Vertical Position Geometry (Torn PDA / Mobile UI)
        // Primary=0, Secondary=1, Melee=2, Temp=3
        const leftImages = [];
        const allItemImages = document.querySelectorAll('img[src*="/items/"]');
        for (let img of allItemImages) {
            const rect = img.getBoundingClientRect();
            if (rect.left >= 0 && rect.left < window.innerWidth / 2) {
                leftImages.push({ img: img, top: rect.top });
            }
        }
        
        // Sort them top-to-bottom as they appear on your physical screen
        leftImages.sort((a, b) => a.top - b.top);

        if (leftImages.length > 0) {
            let targetIndex = 0;
            if (targetText === 'secondary') targetIndex = 1;
            if (targetText === 'melee') targetIndex = 2;
            if (targetText === 'temporary') targetIndex = 3;

            // Clamp index just in case you have empty weapon slots
            if (targetIndex >= leftImages.length) {
                targetIndex = leftImages.length - 1; 
            }
            
            const targetImg = leftImages[targetIndex].img;
            
            // Climb up the HTML tree to find the weapon's outer box (Height is usually between 50 and 250px)
            let parent = targetImg.parentElement;
            let bestWrapper = parent;
            for (let i = 0; i < 5; i++) {
                if (!parent) break;
                if (parent.tagName === 'DIV') {
                    const height = parent.getBoundingClientRect().height;
                    if (height > 50 && height < 250) {
                        bestWrapper = parent;
                    }
                }
                parent = parent.parentElement;
            }
            
            return { wrapper: bestWrapper, image: targetImg };
        }

        return { wrapper: null, image: null };
    }

    function moveStartFightButton() {
        if (document.querySelector('.button-wrapper')) return;

        let startFightButton = null;
        const allButtons = document.querySelectorAll('button, [role="button"], a');
        for (let el of allButtons) {
            const text = (el.innerText || el.textContent || '').toUpperCase();
            if (text.includes('START FIGHT') && text.length < 30) {
                startFightButton = el;
                break;
            }
        }

        if (!startFightButton) return;

        const weaponData = getWeaponBox(BUTTON_LOCATION);
        
        if (startFightButton && weaponData.wrapper && weaponData.image && !document.querySelector('.button-wrapper')) {
            const buttonWrapper = document.createElement('div'); 
            buttonWrapper.classList.add('button-wrapper');
            
            buttonWrapper.appendChild(startFightButton); 
            
            // Append it securely over the entire weapon box
            weaponData.wrapper.appendChild(buttonWrapper);

            buttonWrapper.style.position = 'absolute';
            buttonWrapper.style.top = '0';
            buttonWrapper.style.left = '0';
            buttonWrapper.style.width = '100%';
            buttonWrapper.style.height = '100%';
            buttonWrapper.style.display = 'flex';
            buttonWrapper.style.alignItems = 'center';
            buttonWrapper.style.justifyContent = 'center';
            buttonWrapper.style.zIndex = '99999';
            buttonWrapper.style.backgroundColor = 'rgba(0,0,0,0.5)';
            
            // Force the parent to contain the button overlay
            if (window.getComputedStyle(weaponData.wrapper).position === 'static') {
                weaponData.wrapper.style.position = 'relative';
            }

            startFightButton.addEventListener('click', function() {
                buttonWrapper.remove();
            });
        }
    }

    // ========================================================================
    // FEATURE 2: Force Attack Button on Profile Pages
    // ========================================================================
    function fixProfileAttackButton() {
        const urlParams = new URLSearchParams(window.location.search);
        const userId = urlParams.get('XID');
        if (!userId) return;

        const potentialBtns = new Set([
            ...document.querySelectorAll('a[aria-label*="Attack" i], a[title*="Attack" i]'),
            ...Array.from(document.querySelectorAll('i[class*="attack" i], svg[class*="attack" i]')).map(icon => icon.closest('a') || icon.closest('button'))
        ]);

        for (let btn of potentialBtns) {
            if (!btn) continue;
            
            const currentHref = btn.getAttribute('href') || '';
            const targetUrl = `https://www.torn.com/page.php?sid=attack&user2ID=${userId}`;
            
            if (!currentHref.includes('user2ID=') || btn.className.includes('disabled') || window.getComputedStyle(btn).opacity < 1) {
                btn.classList.remove('disabled');
                btn.style.opacity = '1';
                btn.style.cursor = 'pointer';
                btn.style.pointerEvents = 'auto'; 
                
                btn.setAttribute('href', targetUrl);
                
                btn.addEventListener('click', function(e) {
                    e.stopPropagation(); 
                }, true);
            }
        }
    }

    // ========================================================================
    // MAIN BACKGROUND LOOP (Runs 10x a second)
    // ========================================================================
    setInterval(function () {
        const url = window.location.href;
        
        if (url.includes('attack')) {
            moveStartFightButton();
            
            if (ULTRA_PERFORMANCE_MODE) {
                // Permanently delete rendering models to free CPU
                const models = document.querySelectorAll('canvas, img[src*="models"]');
                for (const element of models) {
                    element.remove();
                }
            }
        } else if (url.includes('profiles.php')) {
            fixProfileAttackButton();
        }
    }, 100);

})();