// ==UserScript==
// @name Descriptive Mission Titles
// @namespace https://github.com/sulsay/torn
// @version 1.4
// @description Renames (Duke) missions to reflect their main objective
// @author Sulsay [2173590]
// @match https://www.torn.com/loader.php?sid=missions
// @grant GM_addStyle
// ==/UserScript==
GM_addStyle(`
#missionsMainContainer .giver-tabs a {
position: relative;
}
#missionsMainContainer .giver-tabs a i.right:not(.mission-timeout-icon) {
position: absolute;
left: 0;
top: .4rem;
}`);
const missionTitles = new Map([
['Introduction: Duke', 'Complete 10 Duke contracts'],
['Show Some Muscle', 'Attack {name}'],
['Battering Ram', 'Attack {name} 3 times'],
['New Kid on the Block', 'Defeat any 5 players'],
['Against the Odds', 'Defeat 2 {name}'],
['A Good Day To Get Hard', 'Achieve a killstreak of 10'],
['A Kimpossible Task', 'Defeat {name} using only melee and temp weapons'],
['An Honorary Degree', 'Defeat {name} without using any guns'],
['A Problem at the Tracks', 'Defeat 3 {name} without using guns'],
['Army of One', 'Attack {name} 3 times as normal, with mask, with different mask'],
['A Thor Loser', 'Use Duke\'s hammer and hit 15 different body parts'],
['Bakeout Breakout', 'Buy a fruitcake, use cake frosting & lock-picking kits from inventory given by Duke, then send special fruitcake to any player in jail. Player does not actually receive the item.'],
['Bare Knuckle', 'Defeat {name} with no weapons or armour equipped'],
['Batshit Crazy', 'Inflict aprox 17k, 45K damage using Duke\'s Bat'],
['Big Tub of Muscle', 'Defeat {name} despite their gargantuan strength'],
['Birthday Surprise', 'Obtain afro comb/edvd/laptop, place in empty box, giftwrap, send to Duke'],
['Bonnie and Clyde', 'Defeat {name} and their spouse {name}'],
['Bountiful', 'Claim 2,3, or 5 bounties'],
['Bounty on the Mutiny', 'Bounty {name} and wait for bounty to be fulfilled'],
['Candy From Babies', 'Collect $150k or $250k in bounties'],
['Charity Work', 'Mug 2 {name}'],
['Cracking Up', 'Defeat & Interrogate {name} to find the code for Duke\'s safe'],
['Critical Education', 'Achieve 8 critical hits'],
['Cut Them Down to Size', 'Defeat someone your level or higher'],
['Defeat Duke', 'Defeat Duke within 7 days of activating this mission.'],
['Dirty Little Secret', 'Put a bounty on {name}, wait for it to be claimed, attack the person who claimed the bounty'],
['Double Jeopardy', 'Put a bounty on someone, then defeat them.'],
['Drug Problem', 'Defeat 4-7 {name}'],
['Emotional Debt', 'Hit {name} with tear gas or pepper spray'],
['Estranged', 'Injure one of {name} legs'],
['Family Ties', 'Hospitalize {name} 3 times'],
['Field Trip', 'Win $1k, $10k, $100k or 10M on 3 named Casino Games'],
['Fireworks', 'Expend 500, 1000, 1500, 2500 rounds of ammo'],
['Forgotten Bills', 'Defeat {name}'],
['Frenzy', 'Defeat any 5, 7, 11, 11, or 15 players'],
['Get Things Jumping', 'Cause 2k, 8k, 30k dmg, receive 1k, 4k, 15k'],
['Graffiti', 'Hit {name} with pepper spray'],
['Guardian', 'Defeat {name}'],
['Hammer Time', 'Defeat {name} using only a hammer'],
['Hands Off', 'Defeat 3-5 {name}'],
['Hare, Meet Tortoise', 'Defeat {name} despite their lightening fast speed'],
['Hide and Seek', 'Defeat correct {name} of 4 players, clues given'],
['Hiding in Plain View', 'Travel to (country) and defeat {name}'],
['High Fliers', 'Defeat 3 {name} in 3 foreign (country)'],
['Hobgoblin', 'Defeat a player of your choice 5 times'],
['Immovable Object', 'Defeat {name} despite their impenetrable defense'],
['Inside Job', 'Attack someone and secrete the Taser into or onto them'],
['Keeping Up Appearances', 'Successfully mug {name} then give everything you mugged back to them.'],
['Kiss of Death', 'Defeat {name} and use only the kiss option'],
['Lack of Awareness', 'Defeat {name}'],
['Lost and Found', 'Put {name} in hospital for 12 hours'],
['Loud and Clear', 'Use 5,11 explosive grenades'],
['Loyal Customer', 'Defeat {name}'],
['Make it Slow', 'Defeat {name} in no fewer than 13 turns in a single attack'],
['Marriage Counselling', 'Defeat the spouse of {name}'],
['Massacrist', 'Defeat {name}'],
['Meeting the Challenge', 'Mug people for a listed total amount.'],
['Motivator', 'Lose or stalemate to {name}'],
['No Man is an Island', 'Mug at least 2 of 3 {name}'],
['No Second Chances', 'Defeat {name} on first attempt'],
['Out of the Frying Pan', 'Use Felovax while in jail, Zylkene while in hospital'],
['Painleth Dentitht', 'Defeat {name} with a baseball bat'],
['Party Tricks', 'Defeat {name} despite their nimble dexterity'],
['Pass the Word', 'Send {name} a mail message with keyword included'],
['Peak Experience', 'Defeat {name} despite their high level'],
['Proof of the Pudding', 'Acquire a named gun, shoot {name} with it, send it to {name}'],
['Rabbit Response', 'Defeat 3 {name} within 30 minutes, 20, 20, 15, and 10'],
['Reconstruction', 'Equip kitchen knife/leather gloves, defeat {name} then dump both items.'],
['Red Faced', 'Defeat {name} using a trout as finishing hit'],
['Rising Costs', 'Hit {name} with a brick'],
['Rolling in it', 'Mug {name}'],
['Safari', 'Travel to South Africa and defeat {name} with a rifle'],
['Scammer', 'Defeat {name}'],
['Sellout Slayer', 'Buy a gun from item market or bazaar, use gun on any 3, 6 players, sell gun in item market or bazaar'],
['Sending a Message', 'Defeat {name}'],
['Sleep Aid', 'Defeat {name}'],
['Some People', 'Send any item as a parcel to {name}'],
['Standard Routine', 'Defeat {name} with a clubbed weapon or fists'],
['Stomach Upset', 'Injure {name} stomach'],
['Swan Step Too Far', 'Search the dump for any previously owned item and defeat the previous owner'],
['The Executive Game', 'Defeat {name} using only fists or kick'],
['The Tattoo Artist', 'Defeat {name} using only a slashing weapon'],
['Three-Peat', 'Defeat any 3 players by leave 1, mug 1, hosp 1'],
['Training Day', 'Spend 250e, 500e, 750e, 1000e, 1250e gym training'],
['Tree Huggers', 'Defeat up to 8: {name}, {name}, {name}, {name}, {name}, {name}, {name} and {name}'],
['Undercutters', 'Defeat 3: {name}, {name} and {name}'],
['Unwanted Attention', 'Hospitalize 4: {name}, {name}, {name} and {name}'],
['Withdrawal', 'Injure {name}\'s both arms'],
['Wrath of Duke', 'Defeat 3: {name}, {name} and {name}'],
]);
const originalMissionNames = Array.from(missionTitles.keys());
(async function () {
const missionsContainer = document.getElementById('missionsMainContainer');
new MutationObserver(() => missionsContainerUpdated()).observe(missionsContainer, {childList: true});
missionsContainerUpdated();
})();
function missionsContainerUpdated() {
const missionsList = document.getElementById('giver-tabs');
const missionListItems = Array.from(missionsList.children);
for (let missionListItem of missionListItems) {
const missionDetailsPane = document.getElementById(missionListItem.getAttribute('aria-controls'));
const targetNames = getMissionTargetNames(missionDetailsPane);
renameMissionListItem(missionListItem, targetNames);
renameMissionDetailsTitle(missionDetailsPane, targetNames);
}
}
function getMissionTargetNames(missionDetailsPane) {
const profileLinks = missionDetailsPane.querySelectorAll('a[href^="profiles.php?XID="]');
return Array.from(profileLinks).map(profileLink => profileLink.innerText.trim());
}
function renameMissionListItem(missionListItem, targetNames) {
const anchor = missionListItem.querySelector('a');
const originalMissionTitleNode = getOriginalMissionTitleTextNode(anchor);
replaceMissionTitle(anchor, originalMissionTitleNode, targetNames);
}
function renameMissionDetailsTitle(missionDetailsPane, targetNames) {
const titleDiv = missionDetailsPane.querySelector('.title-black');
const originalMissionTitleNode = getOriginalMissionTitleTextNode(titleDiv);
replaceMissionTitle(titleDiv, originalMissionTitleNode, targetNames);
}
function getOriginalMissionTitleTextNode(context) {
return Array.from(context.childNodes).find(childNode => {
if (childNode.nodeType !== Node.TEXT_NODE) {
return false;
}
const textContent = childNode.textContent.trim();
return textContent.length > 0 && originalMissionNames.includes(textContent);
}) || null;
}
function replaceMissionTitle(context, originalTitleNode, targetNames) {
if (originalTitleNode === null) {
return;
}
let replacementTitle = missionTitles.get(originalTitleNode.textContent.trim());
for (let targetName of targetNames) {
replacementTitle = replacementTitle.replace('{name}', targetName);
}
context.replaceChild(document.createTextNode(replacementTitle), originalTitleNode);
}