您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Warns when opponent's life is below a set %, disables attacking, and provides an on-screen input to change the %.
// ==UserScript== // @name Torn Attack Life Monitor (with UI) // @namespace http://tampermonkey.net/ // @version 1.0 // @description Warns when opponent's life is below a set %, disables attacking, and provides an on-screen input to change the %. // @author HeyItzWerty [3626448] // @match https://www.torn.com/loader.php?sid=attack* // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; /** * Creates a compact settings UI and a placeholder for the warning message. */ function createSettingsUI() { const targetArea = document.querySelector('.labelsContainer___Oz6Su'); if (!targetArea || document.getElementById('life-threshold-container')) { return; } // 1. Create a placeholder for our new warning message, initially hidden const warningPlaceholder = document.createElement('div'); warningPlaceholder.id = 'life-warning-indicator'; Object.assign(warningPlaceholder.style, { color: '#D8000C', // A strong red color fontWeight: 'bold', fontSize: '14px', marginRight: '15px', padding: '2px 8px', backgroundColor: '#FFD2D2', // Light red background border: '1px solid #D8000C', borderRadius: '5px', display: 'none' // Start hidden }); // 2. Create the settings container const settingsContainer = document.createElement('div'); settingsContainer.id = 'life-threshold-container'; Object.assign(settingsContainer.style, { display: 'flex', alignItems: 'center', gap: '8px', border: '1px solid #ccc', padding: '2px 8px', borderRadius: '5px', backgroundColor: '#e0e0e0' }); settingsContainer.innerHTML = ` <label for="threshold-input" style="font-weight: bold; color: #333; font-size: 12px; margin-bottom: 0;">Warn at %:</label> <input type="number" id="threshold-input" style="width: 45px; padding: 2px; border: 1px solid #aaa; border-radius: 3px; font-size: 12px;"> <button id="save-threshold-btn" style="padding: 2px 8px; border: none; background-color: #4CAF50; color: white; border-radius: 3px; cursor: pointer; font-size: 12px;">Save</button> <span id="save-status" style="color: green; font-weight: bold; font-size: 12px;"></span> `; // 3. Add both the warning placeholder and settings UI to the page targetArea.appendChild(warningPlaceholder); targetArea.appendChild(settingsContainer); const thresholdInput = document.getElementById('threshold-input'); const saveButton = document.getElementById('save-threshold-btn'); const saveStatus = document.getElementById('save-status'); const savedThreshold = GM_getValue('lifeThreshold', 25); thresholdInput.value = savedThreshold; saveButton.addEventListener('click', () => { const newValue = parseInt(thresholdInput.value, 10); if (!isNaN(newValue) && newValue >= 0 && newValue <= 100) { GM_setValue('lifeThreshold', newValue); saveStatus.textContent = 'Saved!'; setTimeout(() => { saveStatus.textContent = ''; }, 2000); } else { alert('Please enter a valid number between 0 and 100.'); } }); } /** * Checks the opponent's health and triggers the warning and weapon disabling. */ function checkOpponentHealth() { const thresholdInput = document.getElementById('threshold-input'); if (!thresholdInput) return; const lifeThreshold = parseInt(thresholdInput.value, 10); const opponentBox = document.querySelector('.player___wiE8R .rose___QcHAq'); if (!opponentBox) return; const healthElement = opponentBox.querySelector('[id^="player-health-value"]'); if (!healthElement || !healthElement.textContent.includes('/')) return; const healthValues = healthElement.textContent.replace(/,/g, '').split('/'); const currentLife = parseInt(healthValues[0], 10); const maxLife = parseInt(healthValues[1], 10); if (isNaN(currentLife) || isNaN(maxLife) || maxLife === 0) return; const lifePercentage = (currentLife / maxLife) * 100; if (lifePercentage < lifeThreshold) { displayWarning(); disableAttacks(); } } /** * Displays the new, integrated warning message in the header. */ function displayWarning() { const warningIndicator = document.getElementById('life-warning-indicator'); if (warningIndicator && warningIndicator.style.display === 'none') { warningIndicator.textContent = 'WARNING!'; warningIndicator.style.display = 'flex'; // Use flex to align text vertically } } /** * Reliably finds and disables all player attack buttons. */ function disableAttacks() { const attackerHeader = document.querySelector('.headerWrapper___p6yrL.green___QtOKw'); if (!attackerHeader) return; const attackerContainer = attackerHeader.closest('.player___wiE8R'); if (!attackerContainer) return; const playerWeaponArea = attackerContainer.querySelector('.playerArea___AEVBU'); if (!playerWeaponArea) return; playerWeaponArea.querySelectorAll('[id^="weapon_"]').forEach(button => { if (button.style.opacity !== '0.5') { // Only disable once button.style.pointerEvents = 'none'; button.style.opacity = '0.5'; } }); } // --- OBSERVERS --- const uiObserver = new MutationObserver((mutations, obs) => { if (document.querySelector('.labelsContainer___Oz6Su')) { createSettingsUI(); obs.disconnect(); } }); const fightObserver = new MutationObserver(() => { checkOpponentHealth(); }); uiObserver.observe(document.body, { childList: true, subtree: true }); fightObserver.observe(document.body, { childList: true, subtree: true }); })();