您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Improvements to the Attack screen.
当前为
// ==UserScript== // @name Attack screen improvements // @namespace http://tampermonkey.net/ // @version 1.1.0 // @description Improvements to the Attack screen. // @author Cypher-[2641265] // @license MIT // @match https://www.torn.com/loader.php?sid=attack&user2ID=* // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // @grant none // ==/UserScript== // Todo: // -add refresh when leave/mug/hosp window comes up. // // Implemented: // -On/offline status icon for defender // -Energy display // -current status for target (hosp/okay etc with timer) (function() { 'use strict'; const API_KEY = 'MINIMAL_API_KEY'; // <-- Replace with "minimal" API Key const SVGs = { Online: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="-1.5 -1.2 14 14"><circle cx="6" cy="6" r="6" fill="#43d854" stroke="#fff" stroke-width="0"/></svg>`, Idle: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="-1.5 -1.2 14 14"><circle cx="6" cy="6" r="6" fill="#f7c325" stroke="#fff" stroke-width="0"/><rect x="5" y="3" width="4" height="4" fill="#f2f2f2"/></svg>`, Offline: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="-1.5 -1.2 14 14"><circle cx="6" cy="6" r="6" fill="#b3b3b3" stroke="#fff" stroke-width="0"/><rect x="3" y="5" width="6" height="2" fill="#f2f2f2"/></svg>` }; const urlParams = new URLSearchParams(window.location.search); const userID = urlParams.get('user2ID'); if (!userID) return; // Fetch defender status function fetchDefenderStatus() { fetch(`https://api.torn.com/user/${userID}?selections=profile&key=${API_KEY}&comment=attackpageimprovements`) .then(res => res.json()) .then(data => { if (!data) return; // Handle online/offline status icon if (data.last_action && data.last_action.status) { const state = data.last_action.status; const svg = SVGs[state] || SVGs.Offline; function insertIcon() { const usernameElement = document.querySelector('div[class*="rose"] .user-name'); if (usernameElement) { // Remove existing icon if present const existingIcon = usernameElement.parentNode.querySelector('.torn-status-icon'); if (existingIcon) { existingIcon.remove(); } const iconSpan = document.createElement('span'); iconSpan.className = 'torn-status-icon'; iconSpan.innerHTML = svg; iconSpan.style.verticalAlign = "middle"; iconSpan.style.marginRight = "4px"; iconSpan.title = state; usernameElement.parentNode.insertBefore(iconSpan, usernameElement); } else { setTimeout(insertIcon, 200); } } insertIcon(); } // Handle health status display if (data.status && data.status.state) { const statusState = data.status.state; const statusColor = data.status.color || 'gray'; const statusUntil = data.status.until; function insertHealthStatus() { const usernameElement = document.querySelector('div[class*="rose"] .user-name'); if (usernameElement) { // Remove existing health status if present const existingHealthStatus = usernameElement.parentNode.querySelector('.torn-health-status'); if (existingHealthStatus) { existingHealthStatus.remove(); } const healthContainer = document.createElement('span'); healthContainer.className = 'torn-health-status'; healthContainer.style.marginLeft = '8px'; healthContainer.style.fontSize = '0.85em'; healthContainer.style.fontWeight = 'bold'; healthContainer.style.cursor = 'pointer'; // Color mapping for different states const colorMap = { 'red': '#dc3545', 'orange': '#fd7e14', 'yellow': '#ffc107', 'green': '#28a745', 'blue': '#007bff', 'gray': '#6c757d' }; healthContainer.style.color = colorMap[statusColor] || '#6c757d'; // Add click handler for refresh healthContainer.addEventListener('click', () => { // Check if the display shows "Click to refresh" if (healthContainer.textContent.includes("Click to refresh")) { // Timer expired, refresh whole page location.reload(); } else { // Timer still active, just refresh status fetchDefenderStatus(); } }); function updateCountdown() { let displayText = statusState; // Add countdown timer if available if (statusUntil) { const currentTime = Math.floor(Date.now() / 1000); const timeRemaining = statusUntil - currentTime; if (timeRemaining > 0) { const hours = Math.floor(timeRemaining / 3600); const minutes = Math.floor((timeRemaining % 3600) / 60); const seconds = timeRemaining % 60; if (hours > 0) { displayText += ` (${hours}h ${minutes}m)`; } else if (minutes > 0) { displayText += ` (${minutes}m ${seconds}s)`; } else { displayText += ` (${seconds}s)`; } } else { // Timer expired, show click to refresh message displayText = statusState + " - Click to refresh"; healthContainer.style.textDecoration = 'underline'; } } healthContainer.textContent = displayText; } // Initial update updateCountdown(); // Update countdown every second if there's a timer if (statusUntil) { setInterval(updateCountdown, 1000); } usernameElement.parentNode.insertBefore(healthContainer, usernameElement.nextSibling); } else { setTimeout(insertHealthStatus, 200); } } insertHealthStatus(); } }); } // Initial fetch fetchDefenderStatus(); // Fetch attacker energy fetch(`https://api.torn.com/user/?selections=bars&key=${API_KEY}&comment=attackerEnergy&comment=attackpageimprovements`) .then(res => res.json()) .then(data => { if (!data || !data.energy) return; const currentEnergy = data.energy.current; const maxEnergy = data.energy.maximum; function insertEnergyDisplay() { const attackerUsernameElement = document.querySelector('div[class*="green"] .user-name'); if (attackerUsernameElement) { if (!attackerUsernameElement.nextSibling || !attackerUsernameElement.nextSibling.classList || !attackerUsernameElement.nextSibling.classList.contains('torn-energy-display')) { const energyContainer = document.createElement('div'); energyContainer.className = 'torn-energy-display'; energyContainer.style.display = 'inline-block'; energyContainer.style.marginLeft = '8px'; energyContainer.style.verticalAlign = 'middle'; energyContainer.title = `Energy: ${currentEnergy}/${maxEnergy}`; // Create progress bar container const progressContainer = document.createElement('div'); progressContainer.style.position = 'relative'; progressContainer.style.width = '80px'; progressContainer.style.height = '12px'; progressContainer.style.backgroundColor = '#2a2a2a'; progressContainer.style.borderRadius = '8px'; progressContainer.style.overflow = 'hidden'; progressContainer.style.border = '1px solid #444'; // Create progress bar fill const progressBar = document.createElement('div'); const percentage = (currentEnergy / maxEnergy) * 100; progressBar.style.width = `${percentage}%`; progressBar.style.height = '100%'; progressBar.style.backgroundColor = '#0ea01fff'; progressBar.style.borderRadius = '8px'; progressBar.style.transition = 'width 0.3s ease'; // Create text display (overlaid on bar) const textDisplay = document.createElement('span'); textDisplay.textContent = `${currentEnergy}/${maxEnergy}`; textDisplay.style.position = 'absolute'; textDisplay.style.top = '50%'; textDisplay.style.left = '50%'; textDisplay.style.transform = 'translate(-50%, -50%)'; textDisplay.style.fontSize = '10px'; textDisplay.style.color = '#fff'; textDisplay.style.fontWeight = 'bold'; textDisplay.style.textShadow = '1px 1px 2px rgba(0,0,0,0.8)'; textDisplay.style.zIndex = '10'; progressContainer.appendChild(progressBar); progressContainer.appendChild(textDisplay); energyContainer.appendChild(progressContainer); attackerUsernameElement.parentNode.insertBefore(energyContainer, attackerUsernameElement.nextSibling); } } else { setTimeout(insertEnergyDisplay, 200); } } insertEnergyDisplay(); }); })();