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

})();