您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Calculate total cache value for ranked war payouts
// ==UserScript== // @name Torn War Cache Value Calculator // @namespace http://tampermonkey.net/ // @version 1.0 // @description Calculate total cache value for ranked war payouts // @author swervelord // @match https://www.torn.com/war.php?* // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // ==/UserScript== (function() { 'use strict'; // Cache types and their item IDs const CACHE_TYPES = { 'Heavy Arms Cache': 1122, 'Armor Cache': 1118, 'Medium Arms Cache': 1121, 'Melee Cache': 1119, 'Small Arms Cache': 1120 }; // API endpoint for Torn Exchange const API_BASE = 'https://tornexchange.com/api/listings'; // Cache for API results to avoid duplicate calls (with 5 minute expiry) const CACHE_EXPIRY = 5 * 60 * 1000; // 5 minutes // Global flag to prevent multiple executions let scriptExecuted = false; // Set to track processed text content to prevent duplicates const processedResults = new Set(); /** * Get cached price with expiry check */ function getCachedPrice(cacheType) { const cached = GM_getValue(`price_${cacheType}`, null); if (cached) { const data = JSON.parse(cached); if (Date.now() - data.timestamp < CACHE_EXPIRY) { return data.price; } } return null; } /** * Set cached price with timestamp */ function setCachedPrice(cacheType, price) { const data = { price: price, timestamp: Date.now() }; GM_setValue(`price_${cacheType}`, JSON.stringify(data)); } /** * Fetch average price for a cache type */ function fetchCachePrice(cacheType) { return new Promise((resolve) => { const itemId = CACHE_TYPES[cacheType]; // Check cache first const cachedPrice = getCachedPrice(cacheType); if (cachedPrice !== null) { resolve(cachedPrice); return; } const url = `${API_BASE}?item_id=${itemId}&sort_by=price&order=desc&page=1`; GM_xmlhttpRequest({ method: 'GET', url: url, timeout: 10000, onload: function(response) { try { const data = JSON.parse(response.responseText); if (data.status === 'success' && data.data.listings) { // Filter out fake listings (anything over $500M is likely fake) const legitimateListings = data.data.listings.filter(listing => listing.price < 500000000); if (legitimateListings.length >= 3) { // Take top 3 legitimate listings and average them const top3 = legitimateListings.slice(0, 3); const average = top3.reduce((sum, listing) => sum + listing.price, 0) / 3; const roundedAverage = Math.round(average); setCachedPrice(cacheType, roundedAverage); resolve(roundedAverage); } else if (legitimateListings.length > 0) { // If less than 3 legitimate listings, use what we have const average = legitimateListings.reduce((sum, listing) => sum + listing.price, 0) / legitimateListings.length; const roundedAverage = Math.round(average); setCachedPrice(cacheType, roundedAverage); resolve(roundedAverage); } else { resolve(0); } } else { resolve(0); } } catch (error) { resolve(0); } }, onerror: function(error) { resolve(0); }, ontimeout: function() { resolve(0); } }); }); } /** * Parse cache quantities from faction result text */ function parseCacheQuantities(text) { const caches = {}; const cacheRegex = /(\d+)x\s+(Heavy Arms Cache|Armor Cache|Medium Arms Cache|Melee Cache|Small Arms Cache)/g; let match; while ((match = cacheRegex.exec(text)) !== null) { const quantity = parseInt(match[1]); const cacheType = match[2]; caches[cacheType] = quantity; } return caches; } /** * Calculate total cache value for a faction */ async function calculateTotalValue(caches) { let totalValue = 0; const pricePromises = []; // Fetch all prices concurrently for (const [cacheType, quantity] of Object.entries(caches)) { pricePromises.push( fetchCachePrice(cacheType).then(price => price * quantity) ); } const results = await Promise.all(pricePromises); totalValue = results.reduce((sum, value) => sum + value, 0); return totalValue; } /** * Format number as currency */ function formatCurrency(amount) { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }).format(amount); } /** * Create a unique identifier for faction result text */ function getResultIdentifier(text) { // Extract faction name and cache info for unique identification const factionMatch = text.match(/^([^<]+?)\s+ranked/); const cacheMatch = text.match(/(\d+x\s+(?:Heavy Arms Cache|Armor Cache|Medium Arms Cache|Melee Cache|Small Arms Cache)(?:,\s*)?)+/); if (factionMatch && cacheMatch) { return `${factionMatch[1].trim()}_${cacheMatch[0]}`; } return text.substring(0, 100); // fallback } /** * Process faction results and add cache value display */ async function processFactionResults() { if (scriptExecuted) { return; } // Find all elements that contain faction result text const allElements = document.querySelectorAll('*'); const factionResults = []; for (const element of allElements) { // Only check elements with direct text content (not nested) if (element.children.length === 0 || element.children.length === 1) { const text = element.textContent || element.innerHTML; if ((text.includes('ranked up from') || text.includes('ranked down from')) && text.includes('Cache')) { const identifier = getResultIdentifier(text); if (!processedResults.has(identifier)) { factionResults.push({ element: element, text: text, identifier: identifier }); processedResults.add(identifier); } } } } for (const result of factionResults) { // Parse cache quantities from the text const caches = parseCacheQuantities(result.text); if (Object.keys(caches).length > 0) { try { // Calculate total value const totalValue = await calculateTotalValue(caches); // Add the cache value directly after the original text with just a line break const valueSpan = document.createElement('span'); valueSpan.style.cssText = ` color: #00E676; font-weight: bold; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); display: block; `; valueSpan.textContent = `Total Cache Value: ${formatCurrency(totalValue)}`; result.element.appendChild(valueSpan); } catch (error) { // Silently handle errors } } } // Mark script as executed and stop all future executions scriptExecuted = true; } /** * Initialize the script with one-time execution */ function initialize() { // Reset execution flag on page load scriptExecuted = false; processedResults.clear(); // Wait for page to be fully loaded if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { setTimeout(processFactionResults, 2000); }); } else { // Page already loaded, wait a bit for dynamic content setTimeout(processFactionResults, 2000); } } // Initialize the script initialize(); })();