Disposal J.A.R.V.I.S.

Color-codes disposal options based on safety and shows nerve requirements

// ==UserScript==
// @name         Disposal J.A.R.V.I.S.
// @namespace    http://tampermonkey.net/
// @version      1.04
// @description  Color-codes disposal options based on safety and shows nerve requirements
// @author       Terekhov
// @match        https://www.torn.com/crimes.php*
// @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';

    // Configuration
    const COLORS = {
        safe: '#40Ab24',
        moderatelySafe: '#A4D497',
        caution: '#D6BBA2',
        unsafe: '#B51B1B'
    };

    const NERVE_COSTS = {
        abandon: 6,
        bury: 8,
        burn: 10,
        sink: 12,
        dissolve: 14
    };

    // Method safety mappings for each disposal type
    const DISPOSAL_METHODS = {
        '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: []
        }
    };

    // Helper Functions
    function findElementByClass(targetClass, parent) {
        const elements = parent.getElementsByClassName(targetClass);
        return elements.length > 0 ? elements[0] : null;
    }

    function findElementByClassStartingWith(prefix, parent) {
        if (!parent || !parent.getElementsByClassName) return null;

        for (const element of parent.getElementsByTagName('*')) {
            for (const className of element.classList) {
                if (className.startsWith(prefix)) {
                    return element;
                }
            }
        }
        return null;
    }

    function getDisposalItemName(jobNode) {
        const sections = findElementByClassStartingWith('sections', jobNode);
        if (!sections) return null;

        // The item name is typically the second child in the sections container
        const nameElement = sections.children[1];
        return nameElement ? nameElement.textContent.trim() : null;
    }

    function getMethodsContainer(jobNode) {
        const sections = findElementByClassStartingWith('sections', jobNode);
        if (!sections) return null;

        // Try both desktop and tablet layouts
        let container = findElementByClassStartingWith('desktopMethodsSection', sections) ||
                       findElementByClassStartingWith('tabletMethodsSection', sections);

        if (container) {
            // For tablet layout, we need to go one level deeper
            const picker = findElementByClassStartingWith('methodPicker', container);
            return picker || container;
        }

        return null;
    }

    function calculateMaxNerve(itemName) {
        const methods = DISPOSAL_METHODS[itemName];
        if (!methods) return 0;

        let maxNerve = 0;
        for (const method of methods.safe) {
            const nerveCost = NERVE_COSTS[method];
            if (nerveCost > maxNerve) maxNerve = nerveCost;
        }
        return maxNerve;
    }

    // Main Functions
    function colorizeDisposalMethods(jobNode) {
        const itemName = getDisposalItemName(jobNode);
        if (!itemName || !DISPOSAL_METHODS[itemName]) return;

        const methodsContainer = getMethodsContainer(jobNode);
        if (!methodsContainer) return;

        const methods = DISPOSAL_METHODS[itemName];

        // Apply borders based on safety levels
        for (const [safety, methodList] of Object.entries({
            safe: methods.safe,
            moderatelySafe: methods.moderatelySafe,
            caution: methods.caution,
            unsafe: methods.unsafe
        })) {
            for (const method of methodList) {
                const button = findElementByClassStartingWith(method, methodsContainer);
                if (button) {
                    const borderWidth = (safety === 'safe' || safety === 'unsafe') ? '3px' : '2px';
                    button.style.border = `${borderWidth} solid ${COLORS[safety]}`;
                }
            }
        }
    }

    function updateDisposalHeader() {
        // Find all disposal items
        const currentCrime = document.querySelector('[class^="currentCrime"]');
        if (!currentCrime) return;

        const container = currentCrime.querySelector('[class^="virtualList"]');
        if (!container) return;

        let totalNerve = 0;
        let jobCount = 0;

        // Calculate total nerve needed
        for (const jobNode of container.getElementsByClassName('crimeOptionWrapper___IOnLO')) {
            const itemName = getDisposalItemName(jobNode);
            if (itemName) {
                totalNerve += calculateMaxNerve(itemName);
                jobCount++;
            }
        }

        // Update the header
        const titleDiv = document.querySelector('[class^="titleBar"]');
        if (titleDiv && titleDiv.children[0]) {
            const title = titleDiv.children[0];
            title.textContent = `Disposal ... Max Nerve needed: ${totalNerve} ... ${jobCount} jobs remaining`;
        }
    }

    // Main execution
    function initializeDisposalJARVIS() {
        if (!window.location.href.includes('crimes')) return;

        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                const disposalItems = document.getElementsByClassName('crimeOptionWrapper___IOnLO');
                if (disposalItems.length > 0) {
                    for (const item of disposalItems) {
                        colorizeDisposalMethods(item);
                    }
                    updateDisposalHeader();
                }
            }
        });

        // Start observing
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });

        // Initial run
        const disposalItems = document.getElementsByClassName('crimeOptionWrapper___IOnLO');
        for (const item of disposalItems) {
            colorizeDisposalMethods(item);
        }
        updateDisposalHeader();
    }

    // Initialize after a short delay to ensure page is loaded
    setTimeout(initializeDisposalJARVIS, 1000);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址