您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Dynamic OC success chance calculator using API endpoints
// ==UserScript== // @name OC Success Chance 2.0 // @namespace http://tampermonkey.net/ // @version 2.0.9 // @description Dynamic OC success chance calculator using API endpoints // @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; const request = GM.xmlHttpRequest || GM.xmlhttpRequest; let observer; let scenarioData = {}; function log(...args) { if (DEBUG) console.log('[OC Success]', ...args); } function logError(...args) { console.error('[OC Success]', ...args); } const observerConfig = { childList: true, subtree: true, attributes: false, characterData: false }; async function callOCAPI(endpoint, data = null, timeout = 5000) { return new Promise((resolve, reject) => { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); request({ method: data ? 'POST' : 'GET', url: `https://tornprobability.com:3000/${endpoint}`, headers: data ? { 'Content-Type': 'application/json' } : {}, data: data ? JSON.stringify(data) : null, signal: controller.signal, onload: (response) => { clearTimeout(timeoutId); try { const result = JSON.parse(response.responseText); if (response.status >= 200 && response.status < 300) { resolve(result); } else { reject(new Error(result.error || 'API error')); } } catch (err) { reject(err); } }, onerror: (err) => reject(err) }); }); } async function fetchScenarioData() { try { const [supportedScenarios, roleMappings] = await Promise.all([ callOCAPI('api/GetSupportedScenarios'), callOCAPI('api/GetRoleNames') ]); scenarioData = supportedScenarios.reduce((acc, scenario) => { const roles = roleMappings[scenario.name]; if (roles) { acc[scenario.name] = { paramCount: scenario.parameters, paramOrder: Object.keys(roles) .sort((a, b) => a.localeCompare(b, undefined, { numeric: true })) .slice(0, scenario.parameters) }; } return acc; }, {}); log('Loaded scenario data:', scenarioData); } catch (error) { logError('Failed to initialize scenario data:', error); } } async function processOCElement(element) { try { const ocName = element.querySelector('.panelTitle___aoGuV')?.textContent?.trim(); const scenario = scenarioData[ocName]; if (!scenario) { log('Skipping unsupported OC:', ocName); return; } const parameters = []; const slots = element.querySelectorAll('.wrapper___Lpz_D'); for (const slotKey of scenario.paramOrder) { const slot = Array.from(slots).find(s => { const fiberKey = Object.keys(s).find(k => k.startsWith('__reactFiber$')); const fiberNode = s[fiberKey]; return fiberNode?.return?.key === `slot-${slotKey}`; }); const chanceText = slot?.querySelector('.successChance___ddHsR')?.textContent; const chance = chanceText ? parseFloat(chanceText.replace('%', '')) : 0; parameters.push(chance); } if (parameters.length !== scenario.paramCount) { logError(`Parameter count mismatch for ${ocName}`); return; } const result = await callOCAPI('api/CalculateSuccess', { scenario: ocName, parameters }); if (result?.successChance) { injectSuccessChance(element, result.successChance); } } catch (error) { logError('OC processing failed:', error); } } function injectSuccessChance(element, chance) { const display = element.querySelector('.oc-success-display') || document.createElement('p'); display.className = 'oc-success-display'; display.textContent = `Success Chance: ${(chance * 100).toFixed(2)}%`; element.querySelector('.panelTitle___aoGuV')?.after(display); } function processOCs() { document.querySelectorAll('.wrapper___U2Ap7:not(.oc-processed)').forEach(element => { element.classList.add('oc-processed'); processOCElement(element); }); } async function initialize() { await fetchScenarioData(); const rootNode = document.querySelector('#factionCrimes-root, #faction-crimes-root') || document.body; observer = new MutationObserver(processOCs); observer.observe(rootNode, observerConfig); document.querySelector('.buttonsContainer___aClaa')?.addEventListener('click', () => { document.querySelectorAll('.wrapper___U2Ap7.oc-processed').forEach(el => { el.classList.remove('oc-processed'); }); setTimeout(processOCs, 500); }); processOCs(); } // Initialize script if (document.readyState === 'complete') { initialize(); } else { window.addEventListener('load', initialize); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址