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

color disposal options based on safety

当前为 2023-11-27 提交的版本,查看 最新版本

// ==UserScript==
// @name         Disposal J.A.R.V.I.S.
// @namespace    http://tampermonkey.net/
// @version      0.3
// @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';
  //
  // 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.
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
  // If we land directly on disposal page, these handle it correctly.
  setTimeout(formatPageOnce, 650);
  let 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(event) {
    setTimeout(formatPageOnce, 650);
    pagePopInterval = setInterval(startCheckingPageToFormat, 650);
  });

  function setSkillLevel() {
    currentSkillLevel = +document.getElementsByClassName('slick-slide')[0].children[0].children[0].children[0].children[2].textContent;
  }


  function formatPageOnce() {
    if (!window.location.href.includes("#/disposal")) {
      return;
    }
    setSkillLevel();
    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 observer;
  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;
    }
    setSkillLevel();
 
    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() {
    let 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 (let child of parentEle.children) {
      for (let 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 (let child of parentEle.children) {
      for (let childClass of child.classList) {
        if (!!childClass && childClass.indexOf(name) !== -1) {
          return child;
        }
      }
    }
    return null;
  }
 
  // Need to wait for page to initialize, before we know this. Assume 1, until then
  let currentSkillLevel = 1;
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 // MAIN SCRIPT - The code below is specific to this crime
 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 
  /**
   * One purpose - execute the main crime script
   */
  function executeCrimeScript() {
    for (let jobNode of getCrimesContainer().children) {
      formatJob(jobNode);
    }
  }

  const colors = {
    safe:          '#40Ab24',
    caution:       '#D6BBA2',
    unsafe:        '#B51B1B'
  };

  // List of method types by aria-label
  const METHOD_TYPES = [
    'Abandon',
    'Bury',
    'Burn',
    'Sink',
    'Dissolve'
  ];
  const JOB_METHOD_DIFFICULTIES_MAP = {
    'Biological Waste': {
      safe: ['Bury', 'Burn', 'Sink'],
      caution: [],
      unsafe: []
    },
    'Body Part': {
      safe: [],
      caution: [],
      unsafe: []
    },
    'Broken Appliance': {
      safe: ['Sink'],
      caution: ['Abandon', 'Bury'],
      unsafe: ['Dissolve']
    },
    'Building Debris': {
      safe: ['Sink'],
      caution: ['Abandon', 'Bury'],
      unsafe: []
    },
    'Dead Body': {
      safe: [],
      caution: [],
      unsafe: []
    },
    'Documents': {
      safe: ['Burn'],
      caution: ['Abandon', 'Bury'],
      unsafe: ['Dissolve']
    },
    'Firearm': {
      safe: [],
      caution: [],
      unsafe: []
    },
    'General Waste': {
      safe: ['Bury', 'Burn'],
      caution: ['Abandon', 'Sink'],
      unsafe: ['Dissolve']
    },
    'Industrial Waste': {
      safe: ['Sink'],
      caution: ['Abandon', 'Bury'],
      unsafe: []
    },
    'Murder Weapon': {
      safe: [],
      caution: [],
      unsafe: []
    },
    'Old Furniture': {
      safe: ['Burn'],
      caution: ['Abandon', 'Bury', 'Sink'],
      unsafe: ['Dissolve']
    },
    'Vehicle': {
      safe: ['Burn', 'Sink'],
      caution: ['Abandon'],
      unsafe: []
    }
  }


  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 (let safeMethod of methodDifficulties.safe) {
        const node = findChildByClassStartingWith(safeMethod.toLowerCase(), disposalMethodsContainer);
        if (node) {
          node.style.border = '3px solid ' + colors.safe;
        }
      }
      for (let cautionMethod of methodDifficulties.caution) {
        const node = findChildByClassStartingWith(cautionMethod.toLowerCase(), disposalMethodsContainer);
        if (node) {
          node.style.border = '2px solid ' + colors.caution;
        }
      }
      for (let unsafeMethod of methodDifficulties.unsafe) {
        const node = findChildByClassStartingWith(unsafeMethod.toLowerCase(), disposalMethodsContainer);
        if (node) {
          node.style.border = '3px solid ' + colors.unsafe;
        }
      }
    }
  }
})();

QingJ © 2025

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