Move Start Button (Torn)

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

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Advertisement:

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

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);

})();