Greasy Fork is available in English.
Moves "Start Fight", forces profile attacks, strips heavy elements, and supports Torn PDA mobile app.
// ==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);
})();