Pickpocket Helper

color pick-pocket targets based on difficulty

目前为 2023-10-20 提交的版本。查看 最新版本

作者
Terekhov
评分
0 0 0
版本
0.4
创建于
2023-10-20
更新于
2023-10-20
大小
8.3 KB
许可证
暂无
适用于

// ==UserScript==
// @name Pickpocket Helper
// @namespace http://tampermonkey.net/
// @version 0.2
// @description color pick-pocket targets based on difficulty
// @author Terekhov
// @match 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=16358739&b=0&a=0
// Thanks Emforus [2535044]!
//
// This script is triggered down at the bottom; see formatCrimesContainerOnce and startListeningToFormatNewCrimes
//
const lsKey = 'pickpocketSkillLevel';

// Need to wait for page to initialize, before we know this. Assume 1, until then
let currentSkillLevel = 1;

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;
}

function getColorForCrimeChild(crimeChild) {
const crimesDivAkaSections = findChildByClassStartingWith('sections', crimeChild);
const mainSection = findChildByClassStartingWith('mainSection', crimesDivAkaSections);

// e.g. 'Elderly woman'
const types = [
'Businessman',
'Businesswoman',
'Classy lady',
'Elderly man',
'Elderly woman',
'Homeless person',
'Jogger',
'Junkie',
'Police officer',
'Postal Worker',
'Rich kid',
'Sex worker',
'Thug',
'Young man',
'Young woman'
];
let targetType = findChildByClassStartingWith('titleAndProps', mainSection).children[0].textContent;
for (let type of types) {
if (targetType.startsWith(type)) {
// Handle mobile view e.g. "Police officer 5m 10s"
targetType = type;
}
}

// e.g. Average 5'0" 158 lbs
const physicalProps = findChildByClassStartingWith('titleAndProps', mainSection).children[1].textContent;

// Average
const build = physicalProps.substring(0, physicalProps.indexOf(' '));

// e.g. Begging0s
const activity = findChildByClassStartingWith('activity', mainSection).textContent;

// e.g. Begging
// The ternary handles mobile - in mobile we don't get the status like "Begging" so we can't do optimize there.
const activityName = activity.match(/^\D+/) ? activity.match(/^\D+/)[0] : '';

// e.g. 0s
// const activityTime = activity.substring(activityName.length);

return colors[getColorSemanticBasedOnAttributes(targetType, build, activityName)];
}

let colors = {
ideal: '#65E037',
easy: '#B4E0AD',
'too-easy': '#C7DCC4',
'too-hard': '#fa8e8e',
uncategorized: '#DA85FF'
};

let skillCats = ['Safe', 'Moderately Unsafe', 'Unsafe', 'Risky', 'Dangerous', 'Very Dangerous'];
let skillStarts = [1, 10, 35, 65, 80, 100];

function getMaxSkillIndex() {
let idx = 0;
skillStarts.forEach((ele, currentIdx) => {
if (Math.floor(currentSkillLevel) >= ele) {
idx = currentIdx;
}
});
return idx;
}

function getAllSafeSkillCats() {
let maxIndex = getMaxSkillIndex();
if (maxIndex >= skillCats.length) {
return skillCats.slice();
} else {
return skillCats.slice(0, maxIndex + 1);
}
}
const markGroups = {
// CS 1-20
'Safe': ['Drunk man', 'Drunk woman', 'Homeless person', 'Junkie', 'Elderly man', 'Elderly woman'],

// CS 10-70
'Moderately Unsafe': ['Laborer', 'Postal worker', 'Young man', 'Young woman', 'Student'],

// CS 35-90
'Unsafe': ['Classy lady', 'Rich kid', 'Sex worker', 'Thug'],

// CS 65+
'Risky': ['Jogger', 'Businessman', 'Businesswoman', 'Gang member', 'Mobster'],

// CS 80+
'Dangerous': ['Cyclist'],

// ???
'Very Dangerous': ['Police officer'],
};


/**
* @param mark e.g. 'Rich Kid'
*
* @return 'too-hard', 'ideal', 'easy', 'too-easy'
*/
function getMarkIdeality(mark) {
let safeSkillCats = getAllSafeSkillCats();
for (let idx = 0; idx < safeSkillCats.length; idx++) {
let safeSkillCat = safeSkillCats[idx];
if (markGroups[safeSkillCat].includes(mark)) {
if (idx === safeSkillCats.length - 1) {
return 'ideal';
} else if (idx === safeSkillCats.length - 2) {
return 'easy';
} else {
return 'too-easy';
}
}
};
return 'too-hard';
}

/**
*
* @param markType Elderly woman
* @param build Average
* @param status Begging
*
* @return green if ideal, red if too hard, yellow otherwise
*/
function getColorSemanticBasedOnAttributes(markType, build, status) {
// TODO builds and statuses to favor. Too much for now
const buildsToAvoid = {
'Businessman': ['Skinny'],
'Drunk man': ['Muscular'],
'Gang member': ['Muscular'],
'Junkie': ['Loitering'],
'Student': ['Athletic'],
'Thug': ['Muscular']
};
const statusesToAvoid = {
'Businessman': ['Walking'],
'Businesswoman': ['Walking'],
'Drunk man': ['Distracted'],
'Drunk woman': ['Distracted'],
'Homeless person': ['Loitering'],
'Junkie': ['Loitering'],
'Laborer': ['Distracted'],
'Sex worker': ['Distracted']
};

let colorSemantic = 'uncategorized';
colorSemantic = getMarkIdeality(markType);
if (buildsToAvoid[markType] && buildsToAvoid[markType].includes(build)) {
colorSemantic = 'too-hard';
}
if (statusesToAvoid[markType] && statusesToAvoid[markType].includes(status)) {
colorSemantic = 'too-hard';
}
return colorSemantic;
}

function setCrimeChildColor(crimeChild) {
const crimesDivAkaSections = findChildByClassStartingWith('sections', crimeChild);
const divContainingButton = findChildByClassStartingWith('commitButtonSection', crimesDivAkaSections)
divContainingButton.style.backgroundColor = getColorForCrimeChild(crimeChild)
}

function getCrimesContainer() {
let crimesContainerName = document.querySelectorAll('[class^="crimeOptionGroup"]')[0].classList[0];
return document.getElementsByClassName(crimesContainerName)[0];
}

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

function formatCrimesContainerOnce() {
if (!window.location.href.includes("#/pickpocketing")) {
return;
}
setSkillLevel();

for (let node of getCrimesContainer().children) {
setCrimeChildColor(node);
}
}

let observer;
let alreadyListening = false;
function startListeningToFormatNewCrimes() {
if (!window.location.href.includes("#/pickpocketing")) {
if (observer) {
observer.disconnect();
observer = undefined;
}
alreadyListening = false;
return;
}
if (alreadyListening) {
return;
}
setSkillLevel();

// Select the node that will be observed for mutations
const targetNode = getCrimesContainer();

// Options for the observer (which mutations to observe)
const config = {
attributes: false,
childList: true,
subtree: false
};

// Callback function to execute when mutations are observed
const callback = (mutationList, observer) => {
for (const mutation of mutationList) {
if (mutation.type === "childList" && mutation.addedNodes.length > 0) {

for (let node of targetNode.children) {
setCrimeChildColor(node);
}
}
}
};

// Create an observer instance linked to the callback function
observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);
alreadyListening = true;
}

// If we land directly on pickpocket page, these handle it correctly.
setTimeout(formatCrimesContainerOnce, 650);
setTimeout(startListeningToFormatNewCrimes, 650);

//
// GreaseMonkey can't listen for Pickpocket page directly, so we run this on all crimes pages.
// however if we navigate away from Pickpocket, we stop listening with our observer
//
window.onpopstate = function(event) {
setTimeout(formatCrimesContainerOnce, 650);
setTimeout(startListeningToFormatNewCrimes, 650);
}


})();

QingJ © 2025

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