您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Optimized OC success chance calculator with debug
当前为
// ==UserScript== // @name OC Success Chance 2.0 // @namespace http://tampermonkey.net/ // @version 2.0.8 // @description Optimized OC success chance calculator with debug // @author Allenone [2033011] // @match https://www.torn.com/factions.php?step=your* // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // @connect tornprobability.com // @grant GM.xmlHttpRequest // @grant GM_info // ==/UserScript== (function() { 'use strict'; const DEBUG = false; // Set to false for production const ocs = new Map(); let observer; // Add parameter mapping for each OC type const PARAM_MAP = { 'Blast From The Past': { 'slot-P1': 'Picklock1', 'slot-P2': 'Hacker1', 'slot-P3': 'Engineer1', 'slot-P4': 'Bomber1', 'slot-P5': 'Muscle1', 'slot-P6': 'Picklock2' }, 'Break The Bank': { 'slot-P1': 'Robber1', 'slot-P2': 'Muscle1', 'slot-P3': 'Muscle2', 'slot-P4': 'Thief1', 'slot-P5': 'Muscle3', 'slot-P6': 'Thief2' } }; // Enhanced logging function log(...args) { if (DEBUG) console.log('[OC Success]', ...args); } function logError(...args) { console.error('[OC Success]', ...args); } // DOM observer configuration const observerConfig = { childList: true, subtree: true, attributes: false, characterData: false }; // Centralized API caller with timeout async function callOCAPI(endpoint, data, timeout = 5000) { return new Promise((resolve, reject) => { const controller = new AbortController(); const timeoutId = setTimeout(() => { controller.abort(); reject(new Error(`API call timed out after ${timeout}ms`)); }, timeout); GM.xmlHttpRequest({ method: 'POST', url: `https://tornprobability.com:3000/${endpoint}`, headers: { 'Content-Type': 'application/json' }, data: JSON.stringify(data), signal: controller.signal, onload: (response) => { clearTimeout(timeoutId); try { const result = JSON.parse(response.responseText); log('API response:', result); resolve(result); } catch (err) { logError('API parse error:', err); reject(err); } }, onerror: (err) => { clearTimeout(timeoutId); logError('API request failed:', err); reject(err); } }); }); } // Process individual OC element with validation async function processOCElement(element) { try { if (!element || !element.querySelector) { logError('Invalid element passed to processOCElement'); return; } const ocName = element.querySelector('.panelTitle___aoGuV')?.textContent?.trim(); log('Processing OC:', ocName); if (!PARAM_MAP[ocName]) { log('Skipping unsupported OC:', ocName); return; } const slots = element.querySelectorAll('.wrapper___Lpz_D'); const successChances = {}; for (const slot of slots) { try { const fiberKey = Object.keys(slot).find(k => k.startsWith('__reactFiber$')); if (!fiberKey) { logError('No React fiber found for slot'); continue; } const fiberNode = slot[fiberKey]; const key = fiberNode?.return?.key; const chanceText = slot.querySelector('.successChance___ddHsR')?.textContent; if (key && chanceText) { // Map slot key to API parameter name const paramName = PARAM_MAP[ocName][key]; if (paramName) { successChances[paramName] = parseFloat(chanceText.replace('%', '')); log(`Mapped ${key} → ${paramName}: ${successChances[paramName]}%`); } } } catch (error) { logError('Slot processing error:', error); } } if (Object.keys(successChances).length === 0) { logError('No success chances found for', ocName); return; } const endpointMap = { 'Blast From The Past': 'BlastFromThePast', 'Break The Bank': 'BreakTheBank' }; const endpoint = endpointMap[ocName]; if (!endpoint) { logError('No endpoint mapping for:', ocName); return; } const result = await callOCAPI(endpoint, successChances); if (result?.successChance) { ocs.set(ocName, result.successChance); injectSuccessChance(element, result.successChance); } } catch (error) { logError('OC processing failed:', error); } } // Main processor with mutation observer function processOCs() { log('Scanning for OC elements...'); document.querySelectorAll('.wrapper___U2Ap7:not(.oc-processed)').forEach(element => { try { element.classList.add('oc-processed'); processOCElement(element); } catch (error) { logError('Element processing error:', error); } }); } // Enhanced injection with styling function injectSuccessChance(element, chance) { try { const displayClass = 'oc-success-display'; let display = element.querySelector(`.${displayClass}`); if (!display) { display = document.createElement('p'); display.className = displayClass; element.querySelector('.panelTitle___aoGuV')?.after(display); } display.innerText = `Success Chance: ${(chance * 100).toFixed(2)}%`; log('Injected success chance:', chance); } catch (error) { logError('Injection failed:', error); } } // Initialize observer and event listeners function initialize() { log('Initializing script...'); // Main observer const rootNode = document.querySelector('#factionCrimes-root, #faction-crimes-root') || document.body; observer = new MutationObserver(processOCs); observer.observe(rootNode, observerConfig); // Tab change handler document.querySelector('.buttonsContainer___aClaa')?.addEventListener('click', () => { log('OC tab changed - reprocessing...'); document.querySelectorAll('.wrapper___U2Ap7.oc-processed').forEach(el => { el.classList.remove('oc-processed'); }); setTimeout(processOCs, 500); }); // Initial processing processOCs(); } // Start script if (document.readyState === 'complete') { initialize(); } else { window.addEventListener('load', initialize); } })();