Descriptive Mission Titles

Renames (Duke) missions to reflect their main objective

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==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);
}