// ==UserScript==
// @name Lazy Crimes
// @namespace http://tampermonkey.net/
// @version 1.0.2
// @description Adds a spam crime button for everybody that should've stayed on crimes 1.0
// @author Heartflower [2626587]
// @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';
console.log('[HF] Lazy Crimes script running');
// Variables to overwrite later on
let nerve = 0;
let currentHref = window.location.href;
// See if there's any previously saved settings
let settings = {};
let savedSettings = JSON.parse(localStorage.getItem('hf-lazy-crimes-settings'));
if (savedSettings) settings = savedSettings;
function crimePage(retries = 30) {
let titleBar = document.body.querySelector('.currentCrime___MN0T1 .titleBar___Cci85');
let list = document.body.querySelector('.virtualList___noLef');
let items = list?.querySelectorAll('.virtual-item');
// If DOM isn't fully loaded, try again
if (!titleBar || !list || !items || items.length < 2) {
if (retries > 0) {
setTimeout(() => crimePage(retries - 1), 100);
} else {
console.warn('[HF] Gave up looking for Crime Page items after 30 retries.');
}
return;
}
// Set variable for mobile to use later
let mobile = document.body.querySelector('.area-mobile___BH0Ku');
// If there's no button container yet, create it
let btnContainer = document.body.querySelector('.hf-lazy-crimes-btn-container');
if (!btnContainer) {
btnContainer = createButtonContainer(titleBar);
} else {
// Don't keep adding buttons
return;
}
// Create the "Lazy Crimes" button and check nerve value
let button = createLazyButton();
btnContainer.appendChild(button);
disableButtons(nerve);
// Variable to use later for certain crimes
let ignoreNextClick = false;
// If mobile scamming, make sure the icons stay up even if clicking the button
if (mobile && window.location.href.includes('scamming')) {
button.addEventListener('mousedown', (e) => {
e.preventDefault(); // prevent focus shift
e.stopImmediatePropagation(); // prevent other click handlers
ignoreNextClick = true;
button.click(); // trigger the actual click
});
}
button.addEventListener('click', function () {
// If already clicked with mousedown or other, then don't double click!
if (ignoreNextClick) {
ignoreNextClick = false;
return;
}
// Find all "subcrimes"/items
items = list.querySelectorAll('.virtual-item');
let bestItem = null;
// Different ways of finding the item to be clicked depending on the crime
if (window.location.href.includes('searchforcash')) {
bestItem = searchForCash(items);
} else if (window.location.href.includes('shoplifting')) {
bestItem = shoplifting(items);
} else if (window.location.href.includes('pickpocketing')) {
button.style.background = 'var(--default-blue-dark-color)';
bestItem = pickpocketing(items);
} else if (window.location.href.includes('hustling')) {
bestItem = hustling(items);
} else if (window.location.href.includes('cracking')) {
bestItem = cracking(items);
} else if (window.location.href.includes('forgery')) {
forgery(items, false);
return; // All the clicking is done in the forgery() function
} else if (window.location.href.includes('scamming')) {
scamming(items);
return; // All the clicking is done in the scamming() function
} else {
bestItem = items[0];
}
// If no item found, then return
if (bestItem === null) {
if (window.location.href.includes('pickpocketing')) {
button.title = 'No target found on last click';
button.style.background = 'var(--default-base-important-color)';
}
return;
}
// Click the commit crime button if it exists and isn't disabled
let commitButton = bestItem.querySelector('.commit-button');
if (window.location.href.includes('hustling') && commitButton.classList.contains('disabled') && nerve >= 2) {
button.title = `You don't have enough money on hand!`;
button.style.background = 'var(--default-base-important-color)';
} else {
button.title = '';
}
if (commitButton && !commitButton.classList.contains('disabled')) commitButton.click();
});
// For shoplifting, create a Notorious button if enabled in settings
if (window.location.href.includes('shoplifting') && settings.Notorious) {
let notoriousBtn = createLazyButton();
notoriousBtn.textContent = 'Notorious';
notoriousBtn.style.marginLeft = '8px';
if (mobile) notoriousBtn.textContent = '100%';
btnContainer.appendChild(notoriousBtn);
disableButtons(nerve);
notoriousBtn.addEventListener('click', function() {
let item = shoplifting(items, true);
let commitButton = item.querySelector('.commit-button');
if (commitButton && !commitButton.classList.contains('disabled')) commitButton.click();
});
}
// For mobile search for cash, make the "search for cash" smaller so the icons can actually appear
if (mobile && window.location.href.includes('searchforcash')) {
let title = document.body.querySelector('.title___uzsf7');
title.style.fontSize = 'small';
}
// For scamming, check if the morale script is installed - if not, prompt user to do so
if (window.location.href.includes('scamming')) {
setTimeout(function() {
let moraleScript = document.body.querySelector('.cm-sc-settings');
if (!moraleScript) {
let icon = document.body.querySelector('.hf-lazy-crimes-icon');
if (icon) {
icon.title = `Please install tobytorn [1617955]'s Crime Morale Script`;
icon.style.cursor = 'pointer';
icon.addEventListener('click', function() {
window.open(`https://www.torn.com/forums.php#/p=threads&f=67&t=16430177&b=0&a=0rh=82&`, '_blank', 'noopener');
});
}
button.style.background = 'var(--default-tabs-disabled-color)';
button.title = `Please install tobytorn [1617955]'s Crime Morale Script`;
button.addEventListener('click', function(e) {
e.preventDefault(); // prevent focus shift
e.stopImmediatePropagation(); // prevent other click handlers
ignoreNextClick = true;
window.open(`https://www.torn.com/forums.php#/p=threads&f=67&t=16430177&b=0&a=0rh=82&`, '_blank', 'noopener');
});
button.classList.add('hf-no-crime-morale');
}
}, 500);
}
}
// HELPER function to create a container for the buttons
function createButtonContainer(titleBar) {
let div = document.createElement('div');
div.classList.add('hf-lazy-crimes-btn-container');
div.style.display = 'flex';
div.style.alignItems = 'center';
div.style.height = '20px';
div.style.marginRight = 'auto';
div.style.marginLeft = '8px';
titleBar.insertBefore(div, titleBar.children[1]);
return div;
}
// HELPER function to create "lazy crimes" or even merit buttons
function createLazyButton() {
let button = document.createElement('button');
button.classList.add('hf-lazy-crimes-button');
button.textContent = `I AM LAZY`;
button.style.background = 'var(--default-blue-dark-color)';
button.style.color = 'var(--btn-color)';
button.style.borderRadius = '8px';
button.style.cursor = 'pointer';
button.style.fontWeight = 'bold';
let mobile = document.body.querySelector('.area-mobile___BH0Ku');
if (mobile) {
button.textContent = 'LAZY';
button.style.fontSize = 'smaller';
}
return button;
}
// HELPER function to find best item for SEARCH FOR CASH
function searchForCash(items) {
let highestPercentage = -Infinity;
let bestItem = null;
for (let item of items) {
let percentageElement = item.querySelector('.densityTooltipTrigger___VG9Wy');
let percentage = percentageElement.getAttribute('aria-label');
let match = percentage.match(/\((\d+)%\)/);
if (match) {
let number = parseInt(match[1], 10);
if (number > highestPercentage) {
highestPercentage = number;
bestItem = item;
}
}
}
return bestItem;
}
// MAIN function for BOOTLEGGING page
function bootlegging(retries = 30) {
let titleBar = document.body.querySelector('.currentCrime___MN0T1 .titleBar___Cci85');
let genres = document.body.querySelectorAll('.genreStock___IT7ld');
// If DOM hasn't fully loaded yet, try again
if (!titleBar || !genres || genres.length < 2) {
if (retries > 0) {
setTimeout(() => bootlegging(retries - 1), 100);
} else {
console.warn('[HF] Gave up looking for Bootlegging to load after 30 retries.');
}
return;
}
// Set variable to check if mobile user or not
let mobile = document.body.querySelector('.area-mobile___BH0Ku');
// Create a button container of one doesn't exist yet
let btnContainer = document.body.querySelector('.hf-lazy-crimes-btn-container');
if (!btnContainer) {
btnContainer = createButtonContainer(titleBar);
} else {
// Don't keep adding buttons
return;
}
// Create lazy crimes button and check nerve
let button = createLazyButton();
btnContainer.appendChild(button);
disableButtons(nerve);
button.addEventListener('click', function () {
let count = document.body.querySelector('.count___hBmtm');
// If no blank DVDs, enlarge the button to add them
if (Number(count.textContent) === 0) {
count.parentNode.click();
// Make the icon extra big so you can't miss it
setTimeout(function() {
let selectItemBtn = document.body.querySelector('.popover___KwcuU .buttonWrap___h7dcO');
selectItemBtn.style.position = 'fixed';
selectItemBtn.style.zIndex = 99999;
selectItemBtn.style.scale = '2000%';
selectItemBtn.style.top = '0px';
}, 500);
return;
}
let commitButtons = document.body.querySelectorAll('.commit-button');
let needStockElement = null;
let needCopyingElement = null;
// Ratio as per Emforus' guide
let info = {
'Action': 10 * 5,
'Fantasy': 7 * 5,
'Comedy': 7 * 5,
'Drama': 5.5 * 5,
'Thriller': 4 * 5,
'Horror': 3 * 5,
'Romance': 3 * 5,
'Sci-Fi': 2 * 5,
}
// Loop through all genres until you find one that doesn't have enough stock yet
for (let genre of genres) {
let name = genre.querySelector('.genreName___kBqTz').textContent;
let stock = Number(genre.querySelector('.currentStock___Bh9_b').textContent);
let currentlyBusy = Number(genre.querySelector('.statusText___fRZso').textContent.replace('copying', '').trim());
if (info[name] !== undefined && stock < info[name]) {
needStockElement = genre;
}
if (info[name] !== undefined && currentlyBusy < info[name]) {
needCopyingElement = genre;
break;
}
}
if (needCopyingElement || needStockElement) {
if (needCopyingElement) needStockElement = needCopyingElement;
// Copy more of the needed stock!
let selected = needStockElement.classList.contains('selected___UvibX');
if (!selected) {
// If already selected, copy
needStockElement.click();
} else {
// If not already selected, select
if (!commitButtons[0].classList.contains('disabled')) commitButtons[0].click();
}
} else {
// Sell stock
if (!commitButtons[1].classList.contains('disabled')) commitButtons[1].click();
}
});
// Add "Online Entrepreneur" merit button if enabled
if (settings['Online Entrepreneur']) {
let onlineBtn = createLazyButton();
onlineBtn.textContent = 'Online Entrepreneur';
onlineBtn.style.marginLeft = '8px';
if (mobile) onlineBtn.textContent = 'ON';
btnContainer.appendChild(onlineBtn);
disableButtons(nerve);
onlineBtn.addEventListener('click', function () {
let commitButtons = document.body.querySelectorAll('.commit-button');
if (!commitButtons[2].textContent.includes('Collect') || !document.body.querySelector('.input___Pgrid')) {
console.warn('[HF Lazy Crimes] Online store does not exist yet');
commitButtons[2].click();
return;
} else {
console.log('[HF Lazy Crimes] Online store exists');
// If the online store is disabled, enable
let input = document.body.querySelector('.input___Pgrid');
let onlineEnabled = input.checked;
if (!onlineEnabled) input.click();
return;
}
let needStockElement = null;
let needCopyingElement = null;
let info = {
'Action': (10 * 482) + (10 * 5),
'Fantasy': (7 * 482) + (7 * 5),
'Comedy': (7 * 482) + (7 * 5),
'Drama': (5.5 * 482) + (5.5 * 5),
'Thriller': (4 * 482) + (4 * 5),
'Horror': (3 * 482) + (3 * 5),
'Romance': (3 * 482) + (3 * 5),
'Sci-Fi': (2 * 482) + (2 * 5),
}
for (let genre of genres) {
let name = genre.querySelector('.genreName___kBqTz').textContent;
let stock = Number(genre.querySelector('.currentStock___Bh9_b').textContent);
let currentlyBusy = Number(genre.querySelector('.statusText___fRZso').textContent.replace('copying', '').trim());
if (info[name] !== undefined && stock < info[name]) {
needStockElement = genre;
}
if (info[name] !== undefined && currentlyBusy < info[name]) {
needCopyingElement = genre;
break;
}
}
if (needCopyingElement || needStockElement) {
if (needCopyingElement) needStockElement = needCopyingElement;
// Copy more of the needed stock!
let selected = needStockElement.classList.contains('selected___UvibX');
if (!selected) {
// If already selected, copy
needStockElement.click();
} else {
// If not already selected, select
commitButtons[0].click();
}
} else {
// Sell stock
commitButtons[1].click();
}
});
}
// Add "Cinephile" merit button if enabled
if (settings.Cinephile) {
let cineBtn = createLazyButton();
cineBtn.textContent = 'Cinephile';
cineBtn.style.marginLeft = '8px';
if (mobile) cineBtn.textContent = '10K';
btnContainer.appendChild(cineBtn);
disableButtons(nerve);
cineBtn.addEventListener('click', function () {
let count = document.body.querySelector('.count___hBmtm');
if (Number(count.textContent) === 0) {
count.parentNode.click();
setTimeout(function() {
let selectItemBtn = document.body.querySelector('.popover___KwcuU .buttonWrap___h7dcO');
selectItemBtn.style.position = 'fixed';
selectItemBtn.style.zIndex = 99999;
selectItemBtn.style.scale = '2000%';
selectItemBtn.style.top = '0px';
}, 500);
return;
}
let preferredGenre = null;
let statistics = document.body.querySelectorAll('.statistic___YkyjL');
for (let statistic of statistics) {
let label = statistic.querySelector('.label___gPPwp');
label = label.textContent.toLowerCase();
if (!label.includes('dvds sold')) continue;
let value = statistic.querySelector('.value___S0RNN');
value = Number(value.textContent.replace(/,/g, ''));
if (value < 10000) {
label = label.replace(' dvds sold', '').trim();
preferredGenre = label;
break;
}
}
return;
let commitButtons = document.body.querySelectorAll('.commit-button');
let needStockElement = null;
let needCopyingElement = null;
let info = {
'Action': 10 * 5,
'Fantasy': 7 * 5,
'Comedy': 7 * 5,
'Drama': 5.5 * 5,
'Thriller': 4 * 5,
'Horror': 3 * 5,
'Romance': 3 * 5,
'Sci-Fi': 2 * 5,
}
for (let genre of genres) {
let name = genre.querySelector('.genreName___kBqTz').textContent;
let stock = Number(genre.querySelector('.currentStock___Bh9_b').textContent);
let currentlyBusy = Number(genre.querySelector('.statusText___fRZso').textContent.replace('copying', '').trim());
if (info[name] !== undefined && stock < info[name]) {
needStockElement = genre;
}
if (info[name] !== undefined && currentlyBusy < info[name]) {
needCopyingElement = genre;
break;
}
}
if (needCopyingElement || needStockElement) {
if (needCopyingElement) needStockElement = needCopyingElement;
// Copy more of the needed stock!
let selected = needStockElement.classList.contains('selected___UvibX');
if (!selected) {
// If already selected, copy
needStockElement.click();
} else {
// If not already selected, select
if (!commitButtons[0].classList.contains('disabled')) commitButtons[0].click();
}
} else {
// Sell stock
if (!commitButtons[1].classList.contains('disabled')) commitButtons[1].click();
}
});
}
}
// MAIN function for the GRAFFITI page
function graffiti(retries = 30) {
let titleBar = document.body.querySelector('.currentCrime___MN0T1 .titleBar___Cci85');
let list = document.body.querySelector('.virtualList___noLef');
let items = list?.querySelectorAll('.virtual-item');
// If DOM hasn't fully loaded yet, try again
if (!titleBar || !list || !items || items.length < 2) {
if (retries > 0) {
setTimeout(() => graffiti(retries - 1), 100);
} else {
console.warn('[HF] Gave up looking for Crime Page items after 30 retries.');
}
return;
}
// Create button container if it doesn't exist yet
let btnContainer = document.body.querySelector('.hf-lazy-crimes-btn-container');
if (!btnContainer) {
btnContainer = createButtonContainer(titleBar);
} else {
// Don't keep adding buttons
return;
}
// Create the lazy crimes button
let button = createLazyButton();
btnContainer.appendChild(button);
disableButtons(nerve);
let icon = document.body.querySelector('.hf-lazy-crimes-icon');
button.addEventListener('click', function (event) {
for (let item of items) {
let tags = Number(item.querySelector('.tagsCount___meTI4').textContent);
if (tags < 500) {
// Do the first one it finds below 500
let sprayCanButton = item.querySelector('.sprayCanButton___URxYK');
let unselected = sprayCanButton.querySelector('.dim___AtLY0');
// If no can selected, make black spray can EXTRA big
if (unselected) {
sprayCanButton.click();
setTimeout(function() {
let rect = button.getBoundingClientRect();
let selectCanButton = document.body.querySelector('.buttonWrap___h7dcO');
selectCanButton.style.position = 'fixed';
selectCanButton.style.zIndex = 99999;
selectCanButton.style.scale = '2000%';
selectCanButton.style.top = '0px';
}, 500);
break;
} else {
// Do the graffiti
let commitButton = item.querySelector('.commit-button');
if (!commitButton.classList.contains('disabled')) commitButton.click();
break;
}
}
}
});
}
// MAIN function for the SEARCH FOR CASH merits
function searchForCashMerits(retries = 30) {
let titleBar = document.body.querySelector('.currentCrime___MN0T1 .titleBar___Cci85');
let list = document.body.querySelector('.virtualList___noLef');
let items = list?.querySelectorAll('.virtual-item');
// If DOM isn't fully loaded, try again
if (!titleBar || !list || !items || items.length < 2) {
if (retries > 0) {
setTimeout(() => searchForCashMerits(retries - 1), 100);
} else {
console.warn('[HF] Gave up looking for Crime Page items after 30 retries.');
}
return;
}
// Variable to check if mobile user or bit
let mobile = document.body.querySelector('.area-mobile___BH0Ku');
// Find button container or add if it doesn't exist
let btnContainer = document.body.querySelector('.hf-lazy-crimes-btn-container');
if (!btnContainer) btnContainer = createButtonContainer(titleBar);
// Create the spoiled rotten button if enabled
if (settings['Spoiled Rotten']) {
let spoiledBtn = createLazyButton();
spoiledBtn.textContent = 'Spoiled Rotten';
if (mobile) spoiledBtn.textContent = 'R';
spoiledBtn.style.marginLeft = '8px';
btnContainer.appendChild(spoiledBtn);
disableButtons(nerve);
spoiledBtn.addEventListener('click', function () {
items = list.querySelectorAll('.virtual-item');
let trash = items[0];
let commitButton = trash.querySelector('.commit-button');
if (commitButton && !commitButton.classList.contains('disabled')) commitButton.click();
});
}
// Create the shorte thing button if enabled
if (settings['Shore Thing']) {
let sandBtn = createLazyButton();
sandBtn.textContent = 'Shore Thing';
if (mobile) sandBtn.textContent = 'S';
sandBtn.style.marginLeft = '8px';
btnContainer.appendChild(sandBtn);
disableButtons(nerve);
sandBtn.addEventListener('click', function () {
items = list.querySelectorAll('.virtual-item');
let sand = items[3];
let commitButton = sand.querySelector('.commit-button');
if (commitButton) commitButton.click();
});
}
}
// HELPER function to decide best item in SHOPLIFTING crime
function shoplifting(items, notorious) {
let highestDisabled = 0;
let safestCrime = null;
let sweetNotoriety = 0;
let bobNotoriety = 0;
for (let item of items) {
let disabled = 0;
let securities = item.querySelectorAll('.securityMeasure___Zhpzp');
for (let security of securities) {
if (security.classList.contains('blank___L7Z5b')) continue;
if (!security.classList.contains('disabled___Enrnq')) continue;
disabled++;
}
let notorietyElement = item.querySelector('.notorietyBar___hikrJ');
let notoriety = notorietyElement.getAttribute('aria-label');
if (notoriety !== 'No notoriety') {
notoriety = Math.ceil(Number(notoriety.replace('Notoriety: ', '').replace('%', '')));
} else {
notoriety = 0;
}
if (notorious) {
if (notoriety === 100) continue;
safestCrime = item;
break;
} else {
if (item === items[0]) sweetNotoriety = notoriety;
if (item === items[1]) bobNotoriety = notoriety;
if (disabled > highestDisabled && notoriety < 90) {
highestDisabled = disabled;
safestCrime = item;
}
}
}
if (!safestCrime) {
if (sweetNotoriety < 90) {
safestCrime = items[0]; // sweet shop
} else if (bobNotoriety < 90) {
safestCrime = items[1]; // bits n bob
} else {
safestCrime = items[0]; // sweet shop
}
}
return safestCrime;
}
// HELPER function to decide best item for PICKPOCKETING crime
function pickpocketing(items) {
// Variable to check if mobile user or bit
let mobile = document.body.querySelector('.area-mobile___BH0Ku');
let availableTarget = null;
let skillElement = document.body.querySelector('.progressFill___zsgNm');
let skill = Number(skillElement.getAttribute('aria-label').replace('Crime skill: ', ''));
let targets = {
'Drunk man': { minskill: 1, maxskill: 20, avoidphsyical: 'Muscular', avoidactivity: 'Distracted' },
'Drunk woman': { minskill: 1, maxskill: 20, avoidphysical: null, avoidactivity: 'Distracted' },
'Elderly man': { minskill: 1, maxskill: 20, avoidphysical: null, avoidactivity: null },
'Elderly woman': { minskill: 1, maxskill: 20, avoidphysical: null, avoidactivity: null },
'Homeless person': { minskill: 1, maxskill: 20, avoidphysical: null, avoidactivity: 'Loitering' },
'Junkie': { minskill: 1, maxskill: 25, avoidphysical: 'Muscular', avoidactivity: 'Loitering' },
'Young man': { minskill: 20, maxskill: 70, avoidphysical: null, avoidactivity: null },
'Young woman': { minskill: 20, maxskill: 70, avoidphysical: null, avoidactivity: null },
'Student': { minskill: 20, maxskill: 70, avoidphysical: 'Athletic', avoidactivity: null },
'Laborer': { minskill: 20, maxskill: 70, avoidphysical: null, avoidactivity: 'Distracted' },
'Postal worker': { minskill: 20, maxskill: 70, avoidphysical: null, avoidactivity: null },
'Rich kid': { minskill: 40, maxskill: 90, avoidphysical: null, avoidactivity: null },
'Sex worker': { minskill: 40, maxskill: 90, avoidphysical: null, avoidactivity: null },
'Thug': { minskill: 40, maxskill: 90, avoidphysical: null, avoidactivity: null },
'Businessman': { minskill: 65, maxskill: 100, avoidphysical: 'Skinny', avoidactivity: 'Walking' },
'Businesswoman': { minskill: 65, maxskill: 100, avoidphysical: 'Heavy', avoidactivity: 'Walking' },
'Gang member': { minskill: 65, maxskill: 100, avoidphysical: 'Muscular', avoidactivity: null },
'Jogger': { minskill: 65, maxskill: 100, avoidphysical: null, avoidactivity: null },
'Cyclist': { minskill: 90, maxskill: 100, avoidphysical: null, avoidactivity: null },
}
let activityIcons = {
'Cycling': 0,
'Distracted': -34,
'Music': -102,
'Loitering': -136,
'Phone': -170,
'Running': -204,
'Soliciting': -238,
'Stumbling': -272,
'Walking': -306,
'Begging': -340
};
for (let item of items) {
let story = item.querySelector('.story___GmRvQ');
if (story) continue;
if (item.textContent === '') continue;
let titleAndProps = item.querySelector('.titleAndProps___DdeVu div').textContent; // e.g. Young Man, Cyclist
titleAndProps = titleAndProps.split(' (')[0]; // removes " (Moderately Unsafe)" and similar
let physicalProps = item.querySelector('.physicalProps___E5YrR').textContent; // e.g. Athletic (info), Skinny (info)
let activity = item.querySelector('.activity___e7mdA').textContent; // e.g. Walking, Distracted
let commitButton = item.querySelector('.commit-button');
if (mobile) {
let activityIcon = item.querySelector('.icon___VfCk2');
let background = Number(getComputedStyle(activityIcon).backgroundPositionY.replace('px', '').trim());
activity = Object.keys(activityIcons).find(key => activityIcons[key] === background);
}
if (commitButton.classList.contains('disabled')) continue;
if (!targets[titleAndProps]) continue;
if (physicalProps.includes(targets[titleAndProps].avoidphysical)) continue;
if (activity.includes(targets[titleAndProps].avoidactivity)) continue;
if (skill < targets[titleAndProps].minskill) continue;
availableTarget = item;
break;
}
return availableTarget;
}
// MAIN function for the BURGLARY page
function burglary(retries = 30) {
let titleBar = document.body.querySelector('.currentCrime___MN0T1 .titleBar___Cci85');
let categories = document.body.querySelectorAll('.targetCategoryButton___Rjnpf');
// If page hasn't fully loaded yet, try again
if (!titleBar || !categories || categories.length < 3) {
if (retries > 0) {
setTimeout(() => burglary(retries - 1), 100);
} else {
console.warn('[HF] Gave up looking for Burglary categories after 30 retries.');
}
return;
}
// Variable to check if user is mobile user or not
let mobile = document.body.querySelector('.area-mobile___BH0Ku');
// Add button container if it doesn't exist yet
let btnContainer = document.body.querySelector('.hf-lazy-crimes-btn-container');
if (!btnContainer) {
btnContainer = createButtonContainer(titleBar);
} else {
// Don't keep adding buttons
return;
}
// Create button
let button = createLazyButton();
btnContainer.appendChild(button);
disableButtons(nerve);
button.addEventListener('click', function () {
let list = document.body.querySelector('.virtualList___noLef');
let items = list?.querySelectorAll('.virtual-item');
if (items.length > 1) {
for (let item of items) {
if (item === items[0]) continue;
let progressBar = Number(item.querySelector('.progressBar___JhMrP').getAttribute('aria-label').replace('Confidence ', '').replace('%', ''));
let commitButtons = item.querySelectorAll('.commit-button');
// Make sure you scout until you've reached 80 confidence
if (progressBar < 80 && !commitButtons[0].classList.contains('disabled')) {
commitButtons[0].click();
return;
} else if (progressBar >= 80 && !commitButtons[1].classList.contains('disabled')) {
commitButtons[1].click();
return;
}
}
}
// Scout any residential
categories = document.body.querySelectorAll('.targetCategoryButton___Rjnpf');
if (!categories[0].classList.contains('selected___m5crj')) {
categories[0].click();
return;
}
let scoutButton = document.body.querySelector('.commit-button');
scoutButton.click();
});
// Add "key to the city" button if enabled
if (settings['Key to the City']) {
let targets = {
'Tool Shed': 'Residential',
'Beach Hut': 'Residential',
'Mobile Home': 'Residential',
'Bungalow': 'Residential',
'Cottage': 'Residential',
'Apartment': 'Residential',
'Suburban Home': 'Residential',
'Secluded Cabin': 'Residential',
'Farmhouse': 'Residential',
'Lake House': 'Residential',
'Luxury Villa': 'Residential',
'Manor House': 'Residential',
'Self Storage Facility': 'Commercial',
'Postal Office': 'Commercial',
'Funeral Directors': 'Commercial',
'Market': 'Commercial',
'Cleaning Agency': 'Commercial',
'Barbershop': 'Commercial',
'Liquor Store': 'Commercial',
'Dentists Office': 'Commercial',
'Chiropractors': 'Commercial',
'Recruitment Agency': 'Commercial',
'Advertising Agency': 'Commercial',
'Shipyard': 'Industrial',
'Dockside Warehouse': 'Industrial',
'Farm Storage Unit': 'Industrial',
'Printing Works': 'Industrial',
'Brewery': 'Industrial',
'Truckyard': 'Industrial',
'Old Factory': 'Industrial',
'Slaughterhouse': 'Industrial',
'Paper Mill': 'Industrial',
'Foundry': 'Industrial',
'Fertilizer Plant': 'Industrial'
}
let keyBtn = createLazyButton();
keyBtn.textContent = 'Key to the City';
if (mobile) keyBtn.textContent = 'KEY';
keyBtn.style.marginLeft = '8px';
btnContainer.appendChild(keyBtn);
disableButtons(nerve);
keyBtn.addEventListener('click', function () {
let done = false;
// Check amount already burgled out of 10k
for (let target in targets) {
let statistics = document.body.querySelectorAll('.statistic___YkyjL');
for (let statistic of statistics) {
let label = statistic.querySelector('.label___gPPwp').textContent.replace('burgled', '').trim();
let value = statistic.querySelector('.value___S0RNN').textContent;
value = parseInt(value.split('/')[0].trim(), 10);
if (target === label && value > 0) {
delete targets[target];
}
}
}
// User has already gotten the merit
if (Object.keys(targets).length === 0) {
done = true;
return;
}
categories = document.body.querySelectorAll('.targetCategoryButton___Rjnpf');
let list = document.body.querySelector('.virtualList___noLef');
let items = list?.querySelectorAll('.virtual-item');
for (let target in targets) {
for (let item of items) {
let itemName = item.querySelector('.crimeOptionSection___hslpu').textContent;
// Scout and burgle the still required targets for the merit
if (itemName.includes(target)) {
let progressBar = Number(item.querySelector('.progressBar___JhMrP').getAttribute('aria-label').replace('Confidence ', '').replace('%', ''));
let commitButtons = item.querySelectorAll('.commit-button');
if (progressBar < 80 && !commitButtons[0].classList.contains('disabled')) {
commitButtons[0].click();
done = true;
break;
} else if (progressBar >= 80 && !commitButtons[1].classList.contains('disabled')) {
commitButtons[1].click();
done = true;
break;
}
}
}
if (done) return;
// If none available, then turn on the category of what still needs to be done
let categoryIndex = {
'Residential': 0,
'Commercial': 1,
'Industrial': 2
};
let type = targets[target];
let index = categoryIndex[type];
if (index !== undefined) {
if (!categories[index].classList.contains('selected___m5crj')) {
let disabled = categories[index].getAttribute('aria-disabled');
if (disabled == false) continue;
categories[index].click();
done = true;
break;
} else {
let scoutButton = document.body.querySelector('.commit-button');
let disabled = scoutButton.getAttribute('aria-disabled');
if (disabled == false) continue;
scoutButton.click();
done = true;
break;
}
}
if (done) return;
}
});
}
}
// HELPER function to find best item for HUSTLING crime
function hustling(items) {
let preferredCrime = null;
for (let item of items) {
let audience = item.querySelector('.audienceSection___f3jVs');
if (audience) {
let info = audience.querySelector('.srOnly___Nqywa').textContent;
// If no audience, gather audience
if (info && info === 'No audience') {
let commitButton = item.querySelector('.commit-button');
commitButton.click();
return;
}
}
let technique = item.querySelector('.techniqueBar___JaXl6');
if (!technique) continue;
technique = technique.getAttribute('aria-label').replace('Technique: ', '');
// Unlock all techniques first
let [ currentTech, maxTech ] = technique.match(/\d+/g).map(Number);
if (currentTech === maxTech) continue;
preferredCrime = item;
break;
}
// If all techniques unlocked
if (!preferredCrime) preferredCrime = items[1]; // Cornhole
return preferredCrime;
}
// MAIN function for the DISPOSAL crime
function disposal(retries = 30) {
let info = {
'Biological Waste': 'Sink',
'Body Part': 'Dissolve',
'Building Debris': 'Sink',
'Dead Body': 'Bury',
'Document': 'Burn',
'Firearm': 'Sink',
'General Waste': 'Bury',
'Industrial Waste': 'Sink',
'Murder Weapon': 'Sink',
'Old Furniture': 'Burn',
'Broken Appliance': 'Sink',
'Vehicle': 'Burn',
};
let titleBar = document.body.querySelector('.currentCrime___MN0T1 .titleBar___Cci85');
let list = document.body.querySelector('.virtualList___noLef');
let items = list?.querySelectorAll('.virtual-item');
// If DOM isn't fully loaded, try again
if (!titleBar || !list || !items || items.length < 2) {
if (retries > 0) {
setTimeout(() => disposal(retries - 1), 100);
} else {
console.warn('[HF] Gave up looking for Disposal items after 30 retries.');
}
return;
}
// Variable to check if mobile user
let mobile = document.body.querySelector('.area-mobile___BH0Ku');
// Add button container if it doesn't exist yet
let btnContainer = document.body.querySelector('.hf-lazy-crimes-btn-container');
if (!btnContainer) {
btnContainer = createButtonContainer(titleBar);
} else {
// Don't keep adding buttons
return;
}
// Create lazy crimes button
let button = createLazyButton();
btnContainer.appendChild(button);
disableButtons(nerve);
button.addEventListener('click', function () {
items = list.querySelectorAll('.virtual-item');
let firstItem = items[1];
let stop = false;
for (let item of items) {
let methods = item.querySelectorAll('.methodButton___lCgpf');
if (!methods || methods.length < 2) continue;
let name = item.querySelector('.crimeOptionSection___hslpu').firstChild.textContent.trim();
for (let method of methods) {
let type = method.getAttribute('aria-label');
let commitButton = item.querySelector('.commit-button');
// If preferred method isn't selected yet, select it - else do the crime
if (info[name] && info[name] === type) {
if (!method.classList.contains('selected___TKH3R')) {
method.click();
} else {
if (commitButton.classList.contains('disabled')) continue;
commitButton.click();
}
stop = true;
break;
}
}
if (stop) break;
}
});
// Add dissolving agent button if enabled
if (settings['Dissolving Agent']) {
let dissolvingBtn = createLazyButton();
dissolvingBtn.textContent = 'Dissolving Agent';
if (mobile) dissolvingBtn.textContent = 'ACID';
dissolvingBtn.style.marginLeft = '8px';
btnContainer.appendChild(dissolvingBtn);
disableButtons(nerve);
let deadBody = false;
for (let item of items) {
let name = item.querySelector('.crimeOptionSection___hslpu');
if (!name) continue;
name = name.firstChild;
name = name.textContent.trim();
if (name === 'Dead Body') {
deadBody = true;
}
}
// If no dead body, then add a warning for it
if (!deadBody || deadBody === false) {
setTimeout(function() {
dissolvingBtn.style.background = 'var(--default-tabs-disabled-color)';
dissolvingBtn.title = 'No Dead Body in sight!';
dissolvingBtn.classList.add('hf-no-body');
}, 10);
dissolvingBtn.style.background = 'var(--default-tabs-disabled-color)';
dissolvingBtn.title = 'No Dead Body in sight!';
return;
}
dissolvingBtn.addEventListener('click', function() {
items = list.querySelectorAll('.virtual-item');
for (let item of items) {
let methods = item.querySelectorAll('.methodButton___lCgpf');
if (!methods || methods.length < 2) continue;
let name = item.querySelector('.crimeOptionSection___hslpu').firstChild.textContent.trim();
// Dissolve the dead body if found
if (name === 'Dead Body') {
let commitButton = item.querySelector('.commit-button');
let method = methods[4];
if (!method.classList.contains('selected___TKH3R')) {
method.click();
return;
} else {
if (commitButton.classList.contains('disabled')) continue;
commitButton.click();
return;
}
}
}
});
}
}
// HELPER function to find the best item for the CRACKING crime
function cracking(items) {
let leastSlots = -Infinity;
let bestItem = null;
for (let item of items) {
let commitButton = item.querySelector('.commit-button');
if (!commitButton) continue;
if (commitButton.classList.contains('disabled')) continue;
let type = item.querySelector('.title___kEtkc');
// Finish started crackings first
if (type) type = type.textContent;
if (!type) {
let nerveNeeded = item.querySelector('.nerve___i4mpF')
if (!nerveNeeded) continue;
nerveNeeded = nerve.textContent;
if (Number(nerveNeeded) === 5) type = 'crack';
}
if (type && type == 'crack') {
bestItem = item;
return bestItem;
}
let slotDummies = item.querySelectorAll('.charSlotDummy___s11h5');
let amount = slotDummies.length;
if (amount > leastSlots) {
leastSlots = amount;
bestItem = item;
}
}
return bestItem;
}
// HELPER function to find and click the best item in the FORGERY crime
function forgery(items, forced) {
let done = false;
if (items.length < 2 || forced) {
let item = items[0];
let dropdown = item.querySelector('.dropdownMainWrapper___PjDiT');
if (dropdown) {
let current = dropdown.querySelector('.optionWithLevelRequirement___cHH35').textContent;
if (current.includes('Parking Permit')) {
// Begin project
let commitButton = item.querySelector('.commit-button');
commitButton.click();
return null;
} else {
// Select parking permit
let dropdownLI = item.querySelector('#option-Parking-Permit-212');
dropdownLI.click();
return null;
}
}
} else {
for (let item of items) {
let dropdown = item.querySelector('.dropdownMainWrapper___PjDiT');
if (dropdown) continue;
let materials = item.querySelectorAll('.itemCell___aZaUE');
// If some material is unavailable, then select it
for (let material of materials) {
if (!material) continue;
let amountLeft = material.querySelector('span')?.textContent?.replace('%', '');
if (amountLeft == 0) {
let consume = material.querySelector('.consume___VXIEH');
if (consume) continue;
let unavailable = item.classList.contains('hf-unavailable-inventory');
if (unavailable) continue;
material.parentNode.click();
let selectItemBtn = null;
setTimeout(function() {
selectItemBtn = document.body.querySelector('.importItemPopover___ACpCG .buttonWrap___h7dcO');
if (!selectItemBtn) {
item.classList.add('hf-unavailable-inventory');
return;
}
selectItemBtn.style.position = 'fixed';
selectItemBtn.style.zIndex = 99999;
selectItemBtn.style.scale = '2000%';
selectItemBtn.style.top = '0px';
}, 500);
done = true;
break;
}
}
if (done) break;
let commitButton = item.querySelector('.commit-button');
if (commitButton.classList.contains('disabled')) continue;
commitButton.click();
done = true;
break;
}
if (!done) forgery(items, true);
return null;
}
}
// HELPER function to find and click the best item in the SCAMMING crime
function scamming(items, forcespam) {
let done = false;
let mobile = document.body.querySelector('.area-mobile___BH0Ku');
let commitButtons = document.body.querySelectorAll('.commit-button');
// If emails less than 20k, farm emails
let emails = Number(document.body.querySelector('.count___dBcR7').textContent.replace(',', ''));
if (emails < 20000 && !commitButtons[0].classList.contains('disabled')) {
commitButtons[0].click();
done = true;
return;
}
if (done) return;
let scraperPhisher = document.body.querySelectorAll('.scraperPhisher___oy1Wn');
// If scraper wanted and not available, try to get it
if (settings.Scraper) {
if (!scraperPhisher || scraperPhisher.length < 1) {
commitButtons[0].click();
done = true;
return;
} else {
let found = false;
for (let icon of scraperPhisher) {
let ariaLabel = icon.getAttribute('aria-label');
if (ariaLabel === "Scraper active") found = true;
}
if (found === false && !commitButtons[0].classList.contains('disabled')) {
commitButtons[0].click();
done = true;
return;
}
}
}
if (done) return;
// If phisher wanted and not available, try to get it
if (settings.Phisher) {
if (!scraperPhisher || scraperPhisher.length < 1) {
commitButtons[0].click();
done = true;
return;
} else {
let found = false;
for (let icon of scraperPhisher) {
let ariaLabel = icon.getAttribute('aria-label');
if (ariaLabel === "Phisher active") found = true;
}
if (found === false && !commitButtons[0].classList.contains('disabled')) {
commitButtons[0].click();
done = true;
return;
}
}
}
if (done) return;
if (items.length < 4 || forcespam) {
// fetch selected dropdown
let selectedDropdown = document.body.querySelector('.dropdownMainWrapper___PjDiT');
let dropdown = document.body.querySelector('.dropdown-additional-content');
// Reverse array to start high to low
let options = Array.from(dropdown.querySelectorAll('li')).reverse();
// If diminished, then find not-diminished options
for (let option of options) {
if (option.classList.contains('diminished')) continue;
if (option.getAttribute('aria-disabled') == 'true') continue;
let optionName = [...option.querySelector('.optionWithLevelRequirement___cHH35').childNodes]
.find(node => node.nodeType === Node.TEXT_NODE)?.textContent.trim() || '';
let selectedName = [...selectedDropdown.querySelector('.optionWithLevelRequirement___cHH35').childNodes]
.find(node => node.nodeType === Node.TEXT_NODE)?.textContent.trim() || '';
if (optionName === selectedName) {
let diminished = selectedDropdown.querySelector('button .diminishedIconWrapper___ntun9');
if (!diminished && !commitButtons[1].classList.contains('disabled')) {
commitButtons[1].click();
done = true;
return;
}
}
option.click();
done = true;
return;
}
}
if (done) return;
let options = ['strong', 'soft', 'back', 'accelerate', 'capitalize'];
for (let item of items) {
let emails = item.querySelector('.emailAddresses___ky_qG');
if (emails) continue;
let dropdown = item.querySelector('.dropdownMainWrapper___PjDiT');
if (dropdown) continue;
let abandonInfo = item.querySelector('.abandon-confirmation');
if (abandonInfo && !abandonInfo.classList.contains('hidden___omYpZ')) {
abandonInfo.querySelector('button').click();
done = true;
return;
}
let commitButton = item.querySelector('.commit-button');
if (!commitButton) continue;
let hesitation = item.querySelector('.hesitation___Hk0Ed');
if (hesitation) continue;
let buttonLabel = commitButton.getAttribute('aria-label');
if (buttonLabel === 'You have already scammed this target') continue;
let read = commitButton.querySelector('.noNerveCost___BCwZI');
if (mobile && !read) read = commitButton.querySelector('#iconmonstr-eye-6-2');
if (read && !commitButton.classList.contains('disabled')) {
commitButton.click();
done = true;
return;
}
let crime = item.querySelector('.crime-option');
let moraleInfo = crime.getAttribute('data-cm-action');
if (!moraleInfo) continue;
if (moraleInfo === "abandon" && !mobile) {
let quitButton = item.querySelector('.avatarButton___AQjYM');
if (quitButton) {
quitButton.click();
done = true;
return;
}
}
if (buttonLabel === 'Resolve, 3 nerve') {
commitButton.click();
done = true;
return;
}
let index = options.findIndex(option => moraleInfo === option);
if (index < 0) continue;
// Different layout for mobile
if (mobile) {
let unselected = item.querySelector('.responseToggleQuestionMark___eHwpF');
let responseWrapper = item.querySelector('.tabletResponseSelector___pEUzk');
if (responseWrapper && !unselected) {
// If no response selected yet, select response in responsewrapper
let selected = item.querySelector('.selectedTabletResponseTypeCircle___pY4g4 g').id;
let responses = item.querySelectorAll('.response-type-button');
let recommended = responses[index].querySelector('g').id;
if (recommended === selected) {
if (commitButton.classList.contains('disabled')) continue;
commitButton.click();
done = true;
return;
} else {
responses[index].click();
done = true;
return;
}
} else if (!responseWrapper && unselected) {
// If no response selected yet and responsewrapper not available, open responsewrapper
let expandButton = item.querySelector('.expandResponseTypeButton___RQkvW');
expandButton.click();
done = true;
return;
} else if (!unselected) {
// If response selected, do crime
if (commitButton.classList.contains('disabled')) continue;
commitButton.click();
done = true;
return;
} else {
// If no response selected, select response
let responses = item.querySelectorAll('.response-type-button');
responses[index].click();
done = true;
return;
}
} else {
// Not mobile, but desktop
let responses = item.querySelectorAll('.response-type-button');
if (!responses[index].classList.contains('selected___jDOCY')) {
// If no response selected, select it
responses[index].click();
done = true;
return;
} else {
// If response selected, do the crime
if (commitButton.classList.contains('disabled')) continue;
commitButton.click();
done = true;
return;
}
}
}
if (!done) {
// If no available items, spam some instead
scamming(items, true);
}
}
// Add the information button to the title bar
function addInfoButton(info, retries = 30) {
let titleBar = document.body.querySelector('.currentCrime___MN0T1 .titleBar___Cci85');
// If DOM isn't fully loaded, try again
if (!titleBar) {
if (retries > 0) {
setTimeout(() => addInfoButton(info, retries - 1), 100);
} else {
console.warn('[HF] Gave up looking for Crime Page items after 30 retries.');
}
return;
}
let icon = document.createElement('div');
icon.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" stroke="var(--default-base-navy-color)" stroke-width="2" fill="none" />
<text x="12" y="13" text-anchor="middle" dominant-baseline="middle" font-size="8" fill="var(--default-base-navy-color)" font-family="Arial, sans-serif">
LC
</text>
</svg>`;
icon.classList.add('hf-lazy-crimes-icon');
icon.style.marginLeft = '6px';
icon.style.cursor = 'help';
icon.title = info;
// Different icon size on mobile due to size issues
let mobile = document.body.querySelector('.area-mobile___BH0Ku');
if (mobile) {
icon.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" stroke="var(--default-base-navy-color)" stroke-width="2" fill="none" />
<text x="12" y="13" text-anchor="middle" dominant-baseline="middle" font-size="8" fill="var(--default-base-navy-color)" font-family="Arial, sans-serif">
LC
</text>
</svg>`;
icon.style.marginRight = '6px';
icon.style.marginTop = '2px';
}
let btnContainer = document.body.querySelector('.hf-lazy-crimes-btn-container');
if (!btnContainer) btnContainer = createButtonContainer(titleBar);
btnContainer.appendChild(icon);
disableButtons(nerve);
return icon;
}
// Check the nerve bar to see if nerve is running out and give warning if it is
function checkNerveBar(retries = 30) {
let nerveBar = document.body.querySelector('.nerve___AyYv_');
// If DOM isn't fully loaded, try again
if (!nerveBar) {
if (retries > 0) {
setTimeout(() => checkNerveBar(retries - 1), 100);
} else {
console.warn('[HF] Gave up looking for nerve bar after 30 retries.');
}
return;
}
let barValue = nerveBar.querySelector('.bar-value___NTdce');
let nerveValue = barValue.childNodes[0];
nerve = Number(nerveValue.textContent);
disableButtons(nerve);
// Keep checking nerve
createObserver(nerveValue);
}
// HELPER function to create a mutation observer and check nerve
function createObserver(element) {
let target;
target = element;
if (!target) {
console.error(`[HF] Mutation Observer target not found.`);
return;
}
let observer = new MutationObserver(function(mutationsList, observer) {
for (let mutation of mutationsList) {
if (mutation.type === 'characterData') {
// Disable buttons if nerve goes down
nerve = Number(mutation.target.textContent);
disableButtons(nerve);
}
}
});
let config = { attributes: true, childList: true, subtree: true, characterData: true };
observer.observe(target, config);
}
// HELPER function to disable buttons and give a warning if (running) out of nerve
function disableButtons(currentNerve) {
let buttons = document.body.querySelectorAll('.hf-lazy-crimes-button');
const config = {
searchforcash: [{ min: 2, message: "You don't have enough nerve" }],
bootlegging: [
{ min: 2, message: "You don't have enough nerve" },
{ min: 5, message: "You're running out of nerve" },
],
graffiti: [{ min: 3, message: "You don't have enough nerve" }],
shoplifting: [{ min: 4, message: "You don't have enough nerve" }],
pickpocketing: [{ min: 5, message: "You don't have enough nerve" }],
burglary: [
{ min: 2, message: "You don't have enough nerve" },
{ min: 6, message: "You're running out of nerve" },
],
hustling: [
{ min: 2, message: "You don't have enough nerve" },
{ min: 4, message: "You're running out of nerve" },
],
disposal: [
{ min: 6, message: "You don't have enough nerve" },
{ min: 14, message: "You're running out of nerve" },
],
cracking: [{ min: 7, message: "You don't have enough nerve" }],
forgery: [{ min: 5, message: "You don't have enough nerve" }],
scamming: [
{ min: 3, message: "You don't have enough nerve" },
{ min: 8, message: "You're running out of nerve" },
],
};
for (let key in config) {
if (window.location.href.includes(key)) {
const requirement = config[key].find(r => currentNerve < r.min);
const disabled = Boolean(requirement);
const title = requirement?.message || '';
for (let button of buttons) {
if (button.classList.contains('hf-no-body')) continue;
if (button.classList.contains('hf-no-crime-morale')) continue;
button.style.background = disabled
? 'var(--default-tabs-disabled-color)'
: 'var(--default-blue-dark-color)';
button.title = title;
// Disable click events if message is "You don't have enough nerve"
if (title === "You don't have enough nerve") {
if (!button.dataset.eventsDisabled) {
// Store existing click handler, if needed
button.dataset.eventsDisabled = "true";
button.addEventListener('click', preventClick, true);
}
} else {
// Re-enable if previously disabled
if (button.dataset.eventsDisabled) {
button.removeEventListener('click', preventClick, true);
delete button.dataset.eventsDisabled;
}
}
}
break;
}
}
function preventClick(e) {
e.stopImmediatePropagation();
e.preventDefault();
}
}
// Add settings button to the crime hub
function addSettings(retries = 30) {
let headerElement = document.body.querySelector('.crimes-app-header');
if (!headerElement) {
if (retries > 0) {
setTimeout(() => addSettings(retries - 1), 100);
} else {
console.warn('[HF] Gave up looking for the crime hub after 30 retries.');
}
return;
}
let icon = document.createElement('div');
icon.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" stroke="var(--default-base-navy-color)" stroke-width="2" fill="none" />
<text x="12" y="13" text-anchor="middle" dominant-baseline="middle" font-size="8" fill="var(--default-base-navy-color)" font-family="Arial, sans-serif">
LC
</text>
</svg>`;
icon.classList.add('hf-lazy-crimes-icon');
icon.style.cursor = 'pointer';
icon.addEventListener('click', function() {
createModal();
});
let link = headerElement.querySelector('.link___bH5rN');
headerElement.insertBefore(icon, link);
return;
}
// HELPER function to create the SETTINGS modal
function createModal() {
let mobile = document.body.querySelector('.area-mobile___BH0Ku');
let cachedSettings = settings;
let modal = document.createElement('div');
modal.style.position = 'fixed';
modal.style.top = '50%';
modal.style.left = '50%';
modal.style.transform = 'translate(-50%, -50%)';
modal.style.padding = '20px';
modal.style.backgroundColor = 'var(--sidebar-area-bg-attention)';
modal.style.border = '2px solid var(--default-tabs-color)';
modal.style.borderRadius = '15px';
modal.style.maxWidth = '400px';
modal.style.zIndex = '9999';
modal.style.maxHeight = '60vh';
modal.style.overflow = 'hidden';
modal.style.display = 'flex';
modal.style.flexDirection = 'column';
modal.style.lineHeight = 'normal';
let titleContainer = document.createElement('div');
titleContainer.textContent = 'Lazy Crimes Settings';
titleContainer.style.fontSize = 'x-large';
titleContainer.style.fontWeight = 'bolder';
titleContainer.style.paddingBottom = '8px';
let scrollContainer = document.createElement('div');
scrollContainer.style.maxHeight = '100%';
scrollContainer.style.flex = '1'; // Fill remaining space
scrollContainer.style.overflowY = 'auto';
scrollContainer.style.marginTop = '10px';
let mainContainer = document.createElement('div');
mainContainer.style.display = 'flex';
mainContainer.style.flexDirection = 'column';
scrollContainer.appendChild(mainContainer);
let creditSpan = document.createElement('span');
creditSpan.innerHTML = `Powered by <a href="https://www.torn.com/profiles.php?XID=2626587" target="_blank" rel="noopener noreferrer" style="color: var(--default-blue-color);">Heartflower [2626587]</a>
and <a href="https://www.torn.com/profiles.php?XID=2535044" target="_blank" rel="noopener noreferrer" style="color: var(--default-blue-color);">Emforus [2535044]</a>'s guides.
Requires the <a href="https://www.torn.com/forums.php#/p=threads&f=67&t=16430177&b=0&a=0rh=82&" target="_blank" rel="noopener noreferrer" style="color: var(--default-blue-color);">Crime Morale Script</a>
by <a href="https://www.torn.com/profiles.php?XID=1617955" target="_blank" rel="noopener noreferrer" style="color: var(--default-blue-color);">tobytorn [1617955]</a> for Scamming to work.
Requires the <a href="https://www.torn.com/forums.php#/p=threads&f=67&t=16382463&b=0&a=0" target="_blank" rel="noopener noreferrer" style="color: var(--default-blue-color);">Crime Profitability Script</a>
by <a href="https://www.torn.com/profiles.php?XID=2626587" target="_blank" rel="noopener noreferrer" style="color: var(--default-blue-color);">Heartflower [2626587]</a> for Search For Cash and Cracking to work.`;
creditSpan.style.paddingBottom = '8px';
mainContainer.appendChild(creditSpan);
let checkboxDiv = document.createElement('div');
checkboxDiv.style.display = 'flex';
checkboxDiv.style.flexDirection = 'column';
checkboxDiv.style.paddingTop = '15px';
let infoSpan = document.createElement('span');
infoSpan.textContent = 'Enable/disable which crimes you want the Lazy Crimes button and/or merit button(s) to appear for here.';
checkboxDiv.appendChild(infoSpan);
let crimes = {
'Enable All': [],
'Search For Cash': ['Spoiled Rotten', 'Shore Thing'],
'Bootlegging': ['Cinephile', 'Online Entrepreneur'],
'Graffiti': [],
'Shoplifting': ['Notorious'],
'Pickpocketing': [],
'Card Skimming': [],
'Burglary': ['Key to the City'],
'Hustling': [],
'Disposal': ['Dissolving Agent'],
'Cracking': [],
'Forgery': [],
'Scamming': ['Scraper', 'Phisher'],
}
for (let crime in crimes) {
// Create container for the crime
let crimeContainer = document.createElement('div');
crimeContainer.style.display = 'flex';
if (mobile) crimeContainer.style.flexDirection = 'column';
// Add toggle for the main crime
addToggle(crimeContainer, crime);
// If there are subitems, add toggles for each
if (crimes[crime].length > 0) {
for (let item of crimes[crime]) {
let toggle = addToggle(crimeContainer, item, crimes[crime]);
toggle.style.marginLeft = '20px';
}
}
// Append container to the page (adjust target parent as needed)
checkboxDiv.appendChild(crimeContainer);
}
createToggleStyleSheet();
mainContainer.appendChild(checkboxDiv);
// Create a container for "Done" and "Cancel" buttons
let buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.justifyContent = 'space-between';
buttonContainer.style.paddingTop = '15px';
let cancelButton = document.createElement('button');
cancelButton.textContent = 'Cancel';
cancelButton.style.color = 'black';
cancelButton.style.border = '1px solid black';
cancelButton.style.borderRadius = '5px';
cancelButton.style.backgroundColor = '#ccc';
cancelButton.addEventListener('click', function () {
settings = cachedSettings;
localStorage.setItem('hf-lazy-crimes-settings', JSON.stringify(settings));
modal.style.display = 'none';
});
buttonContainer.appendChild(cancelButton);
// Add style for hover effect
cancelButton.style.cursor = 'pointer';
// Add event listeners for hover effect
cancelButton.addEventListener('mouseover', function () {
cancelButton.style.fontWeight = 'bold';
});
cancelButton.addEventListener('mouseout', function () {
cancelButton.style.fontWeight = '';
});
let doneButton = document.createElement('button');
doneButton.textContent = 'Done';
doneButton.style.color = 'black';
doneButton.style.border = '1px solid black';
doneButton.style.borderRadius = '5px';
doneButton.style.backgroundColor = '#ccc';
doneButton.addEventListener('click', function () {
location.reload();
modal.style.display = 'none';
});
buttonContainer.appendChild(doneButton);
// Add style for hover effect
doneButton.style.cursor = 'pointer';
// Add event listeners for hover effect
doneButton.addEventListener('mouseover', function () {
doneButton.style.fontWeight = 'bold';
});
doneButton.addEventListener('mouseout', function () {
doneButton.style.fontWeight = '';
});
modal.appendChild(titleContainer);
modal.appendChild(scrollContainer);
modal.appendChild(buttonContainer);
document.body.appendChild(modal);
return modal;
}
// HELPER function to add the checkbox toggle in the SETTINGS modal
function addToggle(container, labelText, mainCrime) {
let toggleContainer = document.createElement('div');
toggleContainer.classList.add('hf-toggle-container');
toggleContainer.style.paddingTop = '5px';
let text = document.createElement('span');
text.textContent = labelText;
text.style.paddingLeft = '5px';
let label = document.createElement('label');
label.classList.add('switch');
let input = document.createElement('input');
input.type = 'checkbox';
input.classList.add('hf-checkbox');
input.checked = true; // Check on by default
let span = document.createElement('span');
span.classList.add('slider', 'round');
let savedInfo = settings[labelText];
if (savedInfo === true) input.checked = true;
if (savedInfo === false) input.checked = false;
if (labelText === 'Enable All') input.checked = false;
settings[labelText] = input.checked;
localStorage.setItem('hf-lazy-crimes-settings', JSON.stringify(settings));
toggleContainer.appendChild(label);
toggleContainer.appendChild(text);
label.appendChild(input);
label.appendChild(span);
container.appendChild(toggleContainer);
// Add event listener to detect changes in the checkbox state
input.addEventListener('change', function() {
if (input.checked) {
if (labelText === 'Enable All') {
let inputs = document.body.querySelectorAll('.hf-checkbox');
for (let input of inputs) {
input.checked = true;
let event = new Event('change', { bubbles: true });
input.dispatchEvent(event);
}
text.textContent = 'Disable All';
return;
}
if (mainCrime) {
let input = container.querySelector('.hf-checkbox');
input.checked = true;
let event = new Event('change', { bubbles: true });
input.dispatchEvent(event);
}
settings[labelText] = true;
localStorage.setItem('hf-lazy-crimes-settings', JSON.stringify(settings));
} else {
if (labelText === 'Enable All' || labelText === 'Disable All') {
let inputs = document.body.querySelectorAll('.hf-checkbox');
for (let input of inputs) {
input.checked = false;
let event = new Event('change', { bubbles: true });
input.dispatchEvent(event);
}
text.textContent = 'Enable All';
return;
}
settings[labelText] = false;
localStorage.setItem('hf-lazy-crimes-settings', JSON.stringify(settings));
}
});
return toggleContainer;
}
// HELPER FUNCTION to create a style sheet to make the fancier toggles work
function createToggleStyleSheet() {
let styles = `
.switch {
position: relative;
display: inline-block;
width: 20px;
height: 10px;
top: 1px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 10px;
width: 10px;
background-color: white;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
transform: translateX(10px);
}
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
`;
// Add the styles to a <style> tag in the document head
let styleSheet = document.createElement("style");
styleSheet.type = "text/css";
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
}
// Check if there's a page chance - if yes, rerun script
function handlePageChange() {
if (window.location.href === currentHref) return;
let existingButton = document.body.querySelector('.hf-lazy-crimes-icon');
if (existingButton) existingButton.remove();
findHref();
currentHref = window.location.href;
}
// run if button click
function handleButtonClick(event) {
setTimeout(() => {
handlePageChange();
}, 500);
}
function findHref() {
checkNerveBar();
let href = window.location.href;
currentHref = href;
let crimes = ['Search for Cash', 'Spoiled Rotten', 'Shore Thing', 'Bootlegging', 'Cinephile', 'Online Entrepreneur',
'Graffiti', 'Shoplifting', 'Notorious', 'Pickpocketing', 'Card Skimming', 'Burglary', 'Key to the City',
'Hustling', 'Disposal', 'Dissolving Agent', 'Cracking', 'Forgery', 'Scamming', 'Scraper', 'Phisher'];
if (!settings || Object.keys(settings).length === 0) {
settings = {};
for (let crime of crimes) {
settings[crime] = true;
}
}
if (href.includes('searchforcash')) {
if (settings['Search For Cash']) {
crimePage();
searchForCashMerits(); // merits - trash & sand
if (settings['Spoiled Rotten'] && settings['Shore Thing']) {
addInfoButton(`The spam button chooses the highest percentage crime.<br><b>Shore Thing</b> looks for sand at the beach.<br><b>Spoiled Rotten</b> looks for rotten food in the trash.`);
} else if (settings['Spoiled Rotten']) {
addInfoButton(`The spam button chooses the highest percentage crime.<br><b>Spoiled Rotten</b> looks for rotten food in the trash.`);
} else if (settings['Shore Thing']) {
addInfoButton(`The spam button chooses the highest percentage crime.<br><b>Shore Thing</b> looks for sand at the beach.`);
} else {
addInfoButton(`The spam button chooses the highest percentage crime.`);
}
}
} else if (href.includes('bootlegging')) {
if (settings.Bootlegging) {
bootlegging();
if (settings.Cinephile && settings['Online Entrepreneur']) {
addInfoButton(`The spam button copies DVDs per Emforus [2535044]'s ratio and once enough DVDs are copied, sells them.*<br><b>Cinephile</b> copies DVDs for any crime not yet at 10,000 DVDs.*
<br>Online Entrepreneur</b> sets up an online shop and turns it on in order to work on the customers needed for this merit.<br>*Please note that this does not keep in mind the currently queued DVDs.`);
} else if (settings.Cinephile) {
addInfoButton(`The spam button copies DVDs per Emforus [2535044]'s ratio and once enough DVDs are copied, sells them.*<br><b>Cinephile</b> copies DVDs for any crime not yet at 10,000 DVDs.*
<br>*Please note that this does not keep in mind the currently queued DVDs.`);
} else if (settings['Online Entrepreneur']) {
addInfoButton(`The spam button copies DVDs per Emforus [2535044]'s ratio and once enough DVDs are copied, sells them.*
<br>Online Entrepreneur</b> sets up an online shop and turns it on in order to work on the customers needed for this merit.<br>*Please note that this does not keep in mind the currently queued DVDs.`);
} else {
addInfoButton(`The spam button copies DVDs per Emforus [2535044]'s ratio and once enough DVDs are copied, sells them.*
<br>*Please note that this does not keep in mind the currently queued DVDs.`);
}
}
} else if (href.includes('graffiti')) {
if (settings.Graffiti) {
graffiti();
addInfoButton(`The spam button brings each district to 500 first before moving to the next and thus should also work if you aim for both merits.`);
}
} else if (href.includes('shoplifting')) {
if (settings.Shoplifting) {
crimePage();
if (settings.Notorious) {
addInfoButton(`The spam button redirects to:<br>(1) Any disabled securities with < 80 notoriety<br>(2) Sweet Shop or Bits 'n' Bobs with < 80 notoriety<br>(3) Sweet Shop<br><br>
For the <b>Notorious</b> merit please have a LOT of nerve on standby.`);
} else {
addInfoButton(`The spam button redirects to:<br>(1) Any disabled securities with < 80 notoriety<br>(2) Sweet Shop or Bits 'n' Bobs with < 80 notoriety<br>(3) Sweet Shop`);
}
}
} else if (href.includes('pickpocketing')) {
if (settings.Pickpocketing) {
crimePage();
addInfoButton(`The spam button redirects to the earliest safe target based on Emforus [2535044]'s guide.<br><br>
For the <b>Pig Rustler</b> merit, just wait for a running cop to appear and try pickpocketing him. Be aware this might lose you a lot of CS in a lot of tries.`);
}
} else if (href.includes ('cardskimming')) {
if (settings['Card Skimming']) addInfoButton(`Just take a moment to spam Bus Stations, forget about it for a month, then spam collect.`);
} else if (href.includes('burglary')) {
if (settings.Burglary) {
burglary();
if (settings['Key to the City']) {
addInfoButton(`The spam button scouts any residential, then cases it to about 80% and then collects it.<br><br><b>Key to the City</b> spawns, cases and burgles whichever target you've not burgled yet.`);
} else {
addInfoButton(`The spam button scouts any residential, then cases it to about 80% and then collects it.`);
}
}
} else if (href.includes('hustling')) {
if (settings.Hustling) {
crimePage();
addInfoButton(`The spam button gathers 1 audience and keeps spamming lose until nerve is empty. It works on whichever technique you haven't completed yet, so should technically work for the <b>Tekkers</b> merit.
<span style="color:var(--default-base-important-color)"> PLEASE NOTE THIS WILL MAKE YOU LOSE MONEY! Remember to keep enough money on hand at all times!</span>`);
}
} else if (href.includes('disposal')) {
if (settings.Disposal) {
disposal();
if (settings['Dissolving Agent']) {
addInfoButton(`The spam button selects the proposed method for the first available crime as per Emforus [2535044]'s guide and then finishes the crime. <span style="color:var(--default-base-important-color)">You need to make sure you have the required item available in order for the crime to be possible.</span><br><br>
<b>Dissolving Agent</b> attempts to dissolve a dead body in acid when available (and visible on the page!)`);
} else {
addInfoButton(`The spam button selects the proposed method for the first available crime as per Emforus [2535044]'s guide and then finishes the crime.`);
}
}
} else if (href.includes('cracking')) {
if (settings.Cracking) {
crimePage();
addInfoButton(`The spam button chooses:<br>(1) Any finished crackings<br>(2) The cracking with the lowest amount of characters<br><br>The <b>Character Assassination</b> merit is simply not possible by spamming, sorry!`);
}
} else if (href.includes('forgery')) {
if (settings.Forgery) {
crimePage();
addInfoButton(`The spam button spams parking permits.<span style="color:var(--default-base-important-color)"> Please make sure you have enough Cardstock and Adhesive Plastic in your inventory.</span>
You should be able to get the <b>Assembly Line</b> merit by simply spamming enough crimes at once.`);
}
} else if (href.includes('scamming')) {
if (settings.Scamming) {
crimePage();
addInfoButton(`The spam button:<br>(1) Farms e-mails up to 20k<br>(2) Launches spam waves if no available targets<br>(3) Finishes targets based on tobytorn [1617955]'s Crime Morale Script info.<br><br>
Merits should be doable with the Crime Morale script as well.<br><br><span style="color:var(--default-base-important-color)">Be sure to disable scraper/phisher if you haven't done the educations yet.</span>`);
}
} else {
addSettings();
}
}
if (!window.lazyCrimesInjected) {
window.lazyCrimesInjected = true;
findHref();
// Checking arrows and document click handler only work like half of the time, so interval
setInterval(handlePageChange, 200);
// Attach click event listener
document.body.addEventListener('click', handleButtonClick);
}
})();