您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
color disposal options based on safety
当前为
// ==UserScript== // @name Disposal J.A.R.V.I.S. // @namespace http://tampermonkey.net/ // @version 1.0 // @description color disposal options based on safety // @author Terekhov // @match https://www.torn.com/loader.php?sid=crimes* // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // @grant none // ==/UserScript== (function () { 'use strict'; const colors = { safe: '#40Ab24', moderatelySafe: '#A4D497', caution: '#D6BBA2', unsafe: '#B51B1B' }; const JOB_METHOD_DIFFICULTIES_MAP = { 'Biological Waste': { safe: ['Sink'], moderatelySafe: [], caution: ['Burn'], unsafe: ['Bury'] }, 'Body Part': { safe: ['Dissolve'], moderatelySafe: [], caution: [], unsafe: [] }, 'Broken Appliance': { safe: ['Sink'], moderatelySafe: [], caution: ['Abandon', 'Bury'], unsafe: ['Dissolve'] }, 'Building Debris': { safe: ['Sink'], moderatelySafe: [], caution: ['Abandon', 'Bury'], unsafe: [] }, 'Dead Body': { safe: ['Dissolve'], moderatelySafe: [], caution: [], unsafe: [] }, Documents: { safe: ['Burn'], moderatelySafe: [], caution: ['Abandon', 'Bury'], unsafe: ['Dissolve', 'Sink'] }, Firearm: { safe: ['Sink'], moderatelySafe: ['Bury'], caution: [], unsafe: ['Dissolve'] }, 'General Waste': { safe: ['Burn'], moderatelySafe: ['Bury'], caution: ['Abandon', 'Sink'], unsafe: ['Dissolve'] }, 'Industrial Waste': { safe: ['Sink'], moderatelySafe: [], caution: ['Abandon', 'Bury'], unsafe: [] }, 'Murder Weapon': { safe: ['Sink'], moderatelySafe: [], caution: [], unsafe: ['Dissolve'] }, 'Old Furniture': { safe: ['Burn'], moderatelySafe: [], caution: ['Abandon', 'Bury', 'Sink'], unsafe: ['Dissolve'] }, Vehicle: { safe: ['Sink'], moderatelySafe: ['Burn'], caution: ['Abandon'], unsafe: [] } }; const NERVE_COST_BY_METHOD = { Abandon: 6, Bury: 8, Burn: 10, Sink: 12, Dissolve: 14 }; // // Based on guide here https://www.torn.com/forums.php#/p=threads&f=61&t=16367936&b=0&a=0 // Thanks Emforus [2535044]! // // The script start is triggered by formatPageOnce and/or startCheckingPageToFormat // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // This section of the script listens for page load and calls the main crime script // Note -- these functions are different depending on the crime; Pickpocketing, for example, is much more // complex to check for than Disposal. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// setTimeout(formatPageOnce, 650); const pageLandingInterval = setInterval(startCheckingPageToFormat, 650); // // GreaseMonkey can't listen for disposal page directly, so we run this on all crimes pages. // however if we navigate away from disposal, we stop listening with our observer // let pagePopInterval; window.addEventListener('popstate', function () { setTimeout(formatPageOnce, 650); pagePopInterval = setInterval(startCheckingPageToFormat, 650); }); function formatPageOnce() { if (!window.location.href.includes('#/disposal')) { return; } executeCrimeScript(); } /** * This function clears intervals checking the page, as the page has already been formatted */ function clearPageCheckingIntervals(reason) { if (pageLandingInterval) { console.warn('clearing pageLandingInterval: ' + reason); clearInterval(pageLandingInterval); } if (pagePopInterval) { console.warn('clearing pagePopInterval: ' + reason); clearInterval(pagePopInterval); } } let alreadyListening = false; /** * This function is called on an interval to see if the page needs formatting. * If it does, it calls {@link #formatPageOnce} and stops checking after that; otherwise, it keeps going. */ function startCheckingPageToFormat() { if (!window.location.href.includes('#/disposal')) { alreadyListening = false; clearPageCheckingIntervals('not disposal page'); return; } if (alreadyListening) { clearPageCheckingIntervals('already listening'); return; } formatPageOnce(); alreadyListening = true; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Global variables - functions and variables that can be re-used across all crimes /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * @return the div containing the list of crime targets (for pickpocketing and disposal, at least) */ function getCrimesContainer() { const crimesContainerName = document.querySelectorAll('[class^="crimeOptionGroup"]')[0].classList[0]; return document.getElementsByClassName(crimesContainerName)[0]; } /** * Utility for inspecting children of an element * * @return child which has a class starting with {@param name} */ function findChildByClassStartingWith(name, parentEle) { for (const child of parentEle.children) { for (const childClass of child.classList) { if (!!childClass && childClass.startsWith(name)) { return child; } } } return null; } /** * Utility for inspecting children of an element * * @return child which has a class starting with {@param name} */ function findChildByClassContaining(name, parentEle) { for (const child of parentEle.children) { for (const childClass of child.classList) { if (!!childClass && childClass.indexOf(name) !== -1) { return child; } } } return null; } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // MAIN SCRIPT - The code below is specific to this crime /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// let totalNerveHasBeenCalculated = false; /** * One purpose - execute the main crime script */ function executeCrimeScript() { let totalNerveNeeded = 0; for (const jobNode of getCrimesContainer().children) { totalNerveNeeded += getNerveNeededForJob(jobNode); // Format each cell formatJob(jobNode); } if (totalNerveHasBeenCalculated) { return; } else { totalNerveHasBeenCalculated = true; } // Set total nerve at top const titleDiv = document.querySelectorAll('[class^="crimeHeading"]')[1].children[0]; titleDiv.textContent = `${titleDiv.textContent} (Max Nerve needed: ${totalNerveNeeded})`; } function getNerveNeededForJob(jobNode) { const jobSections = findChildByClassStartingWith('sections', jobNode); // TODO not sure if jobName works for all views const jobName = jobSections.children[1].textContent; const methodDifficulties = JOB_METHOD_DIFFICULTIES_MAP[jobName]; if (methodDifficulties && methodDifficulties.safe.length) { const highestNerveMethod = methodDifficulties.safe[methodDifficulties.safe.length - 1]; return NERVE_COST_BY_METHOD[highestNerveMethod]; } else { return 0; } } function formatJob(jobNode) { const jobSections = findChildByClassStartingWith('sections', jobNode); // TODO not sure if jobName works for all views const jobName = jobSections.children[1].textContent; let disposalMethodsContainer = findChildByClassContaining('desktopMethodsSection', jobSections); if (!disposalMethodsContainer) { disposalMethodsContainer = findChildByClassContaining('tabletMethodsSection', jobSections); // Have to go one deeper to get the methods container // However they remain the same options disposalMethodsContainer = findChildByClassStartingWith('methodPicker', disposalMethodsContainer); } const methodDifficulties = JOB_METHOD_DIFFICULTIES_MAP[jobName]; if (methodDifficulties) { for (const safeMethod of methodDifficulties.safe) { const node = findChildByClassStartingWith(safeMethod.toLowerCase(), disposalMethodsContainer); if (node) { node.style.border = '3px solid ' + colors.safe; } } for (const moderatelySafeMethod of methodDifficulties.moderatelySafe) { const node = findChildByClassStartingWith(moderatelySafeMethod.toLowerCase(), disposalMethodsContainer); if (node) { node.style.border = '2px solid ' + colors.moderatelySafe; } } for (const cautionMethod of methodDifficulties.caution) { const node = findChildByClassStartingWith(cautionMethod.toLowerCase(), disposalMethodsContainer); if (node) { node.style.border = '2px solid ' + colors.caution; } } for (const unsafeMethod of methodDifficulties.unsafe) { const node = findChildByClassStartingWith(unsafeMethod.toLowerCase(), disposalMethodsContainer); if (node) { node.style.border = '3px solid ' + colors.unsafe; } } } } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址