MWI TaskManager

sort all task in taskpanel

Ekde 2025/03/04. Vidu La ĝisdata versio.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         MWI TaskManager
// @namespace    http://tampermonkey.net/
// @version      0.11
// @description  sort all task in taskpanel
// @author       shykai
// @match        https://www.milkywayidle.com/*
// @match        https://test.milkywayidle.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    //configs
    const isShowIcon = true;

    const isShowDungeon = false;


    const taskBattleIndex = 99; //Battle at bottom
    const taskOrderIndex = {
        Milking: 1,
        Foraging: 2,
        Woodcutting: 3,
        Cheesesmithing: 4,
        Crafting: 5,
        Tailoring: 6,
        Cooking: 7,
        Brewing: 8,
        Alchemy: 9,
        Enhancing: 10,
        Defeat: taskBattleIndex, //Battle at bottom
    };
    const taskOrderIndex_CN = {
        挤奶: 1,
        采摘: 2,
        伐木: 3,
        奶酪锻造: 4,
        制作: 5,
        缝纫: 6,
        烹饪: 7,
        冲泡: 8,
        炼金: 9,
        强化: 10,
        击败: taskBattleIndex, //Battle at bottom
    };

    const allMonster = {
        "/monsters/abyssal_imp": {
            "en": "Abyssal Imp",
            "cn": "深渊小鬼",
            "zone": "/actions/combat/infernal_abyss",
            "dungeon": [
                "/actions/combat/enchanted_fortress"
            ],
            "sortIndex": 11
        },
        "/monsters/aquahorse": {
            "en": "Aquahorse",
            "cn": "水马",
            "zone": "/actions/combat/aqua_planet",
            "dungeon": [
                "/actions/combat/chimerical_den"
            ],
            "sortIndex": 3
        },
        "/monsters/black_bear": {
            "en": "Black Bear",
            "cn": "黑熊",
            "zone": "/actions/combat/bear_with_it",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 8
        },
        "/monsters/gobo_boomy": {
            "en": "Boomy",
            "cn": "轰轰",
            "zone": "/actions/combat/gobo_planet",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 5
        },
        "/monsters/butterjerry": {
            "en": "Butterjerry",
            "cn": "蝶鼠",
            "zone": "",
            "dungeon": [
                "/actions/combat/chimerical_den"
            ],
            "sortIndex": -1
        },
        "/monsters/centaur_archer": {
            "en": "Centaur Archer",
            "cn": "半人马弓箭手",
            "zone": "/actions/combat/jungle_planet",
            "dungeon": [
                "/actions/combat/chimerical_den"
            ],
            "sortIndex": 4
        },
        "/monsters/chronofrost_sorcerer": {
            "en": "Chronofrost Sorcerer",
            "cn": "霜时巫师",
            "zone": "/actions/combat/sorcerers_tower",
            "sortIndex": 7
        },
        "/monsters/crystal_colossus": {
            "en": "Crystal Colossus",
            "cn": "水晶巨像",
            "zone": "/actions/combat/golem_cave",
            "sortIndex": 9
        },
        "/monsters/demonic_overlord": {
            "en": "Demonic Overlord",
            "cn": "恶魔霸主",
            "zone": "/actions/combat/infernal_abyss",
            "sortIndex": 11
        },
        "/monsters/dusk_revenant": {
            "en": "Dusk Revenant",
            "cn": "黄昏亡灵",
            "zone": "/actions/combat/twilight_zone",
            "sortIndex": 10
        },
        "/monsters/elementalist": {
            "en": "Elementalist",
            "cn": "元素法师",
            "zone": "/actions/combat/sorcerers_tower",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 7
        },
        "/monsters/enchanted_pawn": {
            "en": "Enchanted Pawn",
            "cn": "秘法之兵",
            "zone": "",
            "dungeon": [
                "/actions/combat/enchanted_fortress"
            ],
            "sortIndex": -1
        },
        "/monsters/eye": {
            "en": "Eye",
            "cn": "独眼",
            "zone": "/actions/combat/planet_of_the_eyes",
            "dungeon": [
                "/actions/combat/enchanted_fortress"
            ],
            "sortIndex": 6
        },
        "/monsters/eyes": {
            "en": "Eyes",
            "cn": "叠眼",
            "zone": "/actions/combat/planet_of_the_eyes",
            "dungeon": [
                "/actions/combat/enchanted_fortress"
            ],
            "sortIndex": 6
        },
        "/monsters/flame_sorcerer": {
            "en": "Flame Sorcerer",
            "cn": "火焰巫师",
            "zone": "/actions/combat/sorcerers_tower",
            "dungeon": [
                "/actions/combat/enchanted_fortress",
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 7
        },
        "/monsters/fly": {
            "en": "Fly",
            "cn": "苍蝇",
            "zone": "/actions/combat/smelly_planet",
            "sortIndex": 1
        },
        "/monsters/frog": {
            "en": "Frogger",
            "cn": "青蛙",
            "zone": "/actions/combat/swamp_planet",
            "dungeon": [
                "/actions/combat/chimerical_den"
            ],
            "sortIndex": 2
        },
        "/monsters/sea_snail": {
            "en": "Gary",
            "cn": "蜗牛",
            "zone": "/actions/combat/aqua_planet",
            "sortIndex": 3
        },
        "/monsters/giant_shoebill": {
            "en": "Giant Shoebill",
            "cn": "鲸头鹳",
            "zone": "/actions/combat/swamp_planet",
            "sortIndex": 2
        },
        "/monsters/gobo_chieftain": {
            "en": "Gobo Chieftain",
            "cn": "哥布林酋长",
            "zone": "/actions/combat/gobo_planet",
            "sortIndex": 5
        },
        "/monsters/granite_golem": {
            "en": "Granite Golem",
            "cn": "花岗魔像",
            "zone": "/actions/combat/golem_cave",
            "sortIndex": 9
        },
        "/monsters/grizzly_bear": {
            "en": "Grizzly Bear",
            "cn": "棕熊",
            "zone": "/actions/combat/bear_with_it",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 8
        },
        "/monsters/gummy_bear": {
            "en": "Gummy Bear",
            "cn": "软糖熊",
            "zone": "/actions/combat/bear_with_it",
            "dungeon": [
                "/actions/combat/chimerical_den",
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 8
        },
        "/monsters/crab": {
            "en": "I Pinch",
            "cn": "螃蟹",
            "zone": "/actions/combat/aqua_planet",
            "sortIndex": 3
        },
        "/monsters/ice_sorcerer": {
            "en": "Ice Sorcerer",
            "cn": "冰霜巫师",
            "zone": "/actions/combat/sorcerers_tower",
            "dungeon": [
                "/actions/combat/enchanted_fortress",
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 7
        },
        "/monsters/infernal_warlock": {
            "en": "Infernal Warlock",
            "cn": "地狱术士",
            "zone": "/actions/combat/infernal_abyss",
            "sortIndex": 11
        },
        "/monsters/jackalope": {
            "en": "Jackalope",
            "cn": "鹿角兔",
            "zone": "",
            "dungeon": [
                "/actions/combat/chimerical_den"
            ],
            "sortIndex": -1
        },
        "/monsters/rat": {
            "en": "Jerry",
            "cn": "杰瑞",
            "zone": "/actions/combat/smelly_planet",
            "dungeon": [
                "/actions/combat/chimerical_den"
            ],
            "sortIndex": 1
        },
        "/monsters/jungle_sprite": {
            "en": "Jungle Sprite",
            "cn": "丛林精灵",
            "zone": "/actions/combat/jungle_planet",
            "dungeon": [
                "/actions/combat/chimerical_den"
            ],
            "sortIndex": 4
        },
        "/monsters/luna_empress": {
            "en": "Luna Empress",
            "cn": "月神之蝶",
            "zone": "/actions/combat/jungle_planet",
            "sortIndex": 4
        },
        "/monsters/magnetic_golem": {
            "en": "Magnetic Golem",
            "cn": "磁力魔像",
            "zone": "/actions/combat/golem_cave",
            "dungeon": [
                "/actions/combat/enchanted_fortress"
            ],
            "sortIndex": 9
        },
        "/monsters/marine_huntress": {
            "en": "Marine Huntress",
            "cn": "海洋猎手",
            "zone": "/actions/combat/aqua_planet",
            "sortIndex": 3
        },
        "/monsters/myconid": {
            "en": "Myconid",
            "cn": "蘑菇人",
            "zone": "/actions/combat/jungle_planet",
            "sortIndex": 4
        },
        "/monsters/nom_nom": {
            "en": "Nom Nom",
            "cn": "咬咬鱼",
            "zone": "/actions/combat/aqua_planet",
            "sortIndex": 3
        },
        "/monsters/novice_sorcerer": {
            "en": "Novice Sorcerer",
            "cn": "新手巫师",
            "zone": "/actions/combat/sorcerers_tower",
            "dungeon": [
                "/actions/combat/enchanted_fortress",
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 7
        },
        "/monsters/panda": {
            "en": "Panda",
            "cn": "熊猫",
            "zone": "/actions/combat/bear_with_it",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 8
        },
        "/monsters/polar_bear": {
            "en": "Polar Bear",
            "cn": "北极熊",
            "zone": "/actions/combat/bear_with_it",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 8
        },
        "/monsters/porcupine": {
            "en": "Porcupine",
            "cn": "豪猪",
            "zone": "/actions/combat/smelly_planet",
            "dungeon": [
                "/actions/combat/chimerical_den"
            ],
            "sortIndex": 1
        },
        "/monsters/rabid_rabbit": {
            "en": "Rabid Rabbit",
            "cn": "疯魔兔",
            "zone": "",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": -1
        },
        "/monsters/red_panda": {
            "en": "Red Panda",
            "cn": "小熊猫",
            "zone": "/actions/combat/bear_with_it",
            "sortIndex": 8
        },
        "/monsters/alligator": {
            "en": "Sherlock",
            "cn": "夏洛克",
            "zone": "/actions/combat/swamp_planet",
            "sortIndex": 2
        },
        "/monsters/gobo_shooty": {
            "en": "Shooty",
            "cn": "咻咻",
            "zone": "/actions/combat/gobo_planet",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 5
        },
        "/monsters/skunk": {
            "en": "Skunk",
            "cn": "臭鼬",
            "zone": "/actions/combat/smelly_planet",
            "sortIndex": 1
        },
        "/monsters/gobo_slashy": {
            "en": "Slashy",
            "cn": "砍砍",
            "zone": "/actions/combat/gobo_planet",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 5
        },
        "/monsters/slimy": {
            "en": "Slimy",
            "cn": "史莱姆",
            "zone": "/actions/combat/smelly_planet",
            "sortIndex": 1
        },
        "/monsters/gobo_smashy": {
            "en": "Smashy",
            "cn": "锤锤",
            "zone": "/actions/combat/gobo_planet",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 5
        },
        "/monsters/soul_hunter": {
            "en": "Soul Hunter",
            "cn": "灵魂猎手",
            "zone": "/actions/combat/infernal_abyss",
            "dungeon": [
                "/actions/combat/enchanted_fortress"
            ],
            "sortIndex": 11
        },
        "/monsters/gobo_stabby": {
            "en": "Stabby",
            "cn": "刺刺",
            "zone": "/actions/combat/gobo_planet",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 5
        },
        "/monsters/stalactite_golem": {
            "en": "Stalactite Golem",
            "cn": "钟乳石魔像",
            "zone": "/actions/combat/golem_cave",
            "dungeon": [
                "/actions/combat/enchanted_fortress"
            ],
            "sortIndex": 9
        },
        "/monsters/swampy": {
            "en": "Swampy",
            "cn": "沼泽虫",
            "zone": "/actions/combat/swamp_planet",
            "dungeon": [
                "/actions/combat/chimerical_den"
            ],
            "sortIndex": 2
        },
        "/monsters/the_watcher": {
            "en": "The Watcher",
            "cn": "观察者",
            "zone": "/actions/combat/planet_of_the_eyes",
            "sortIndex": 6
        },
        "/monsters/snake": {
            "en": "Thnake",
            "cn": "蛇",
            "zone": "/actions/combat/swamp_planet",
            "sortIndex": 2
        },
        "/monsters/treant": {
            "en": "Treant",
            "cn": "树人",
            "zone": "/actions/combat/jungle_planet",
            "sortIndex": 4
        },
        "/monsters/turtle": {
            "en": "Turuto",
            "cn": "忍者龟",
            "zone": "/actions/combat/aqua_planet",
            "sortIndex": 3
        },
        "/monsters/vampire": {
            "en": "Vampire",
            "cn": "吸血鬼",
            "zone": "/actions/combat/twilight_zone",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 10
        },
        "/monsters/veyes": {
            "en": "Veyes",
            "cn": "复眼",
            "zone": "/actions/combat/planet_of_the_eyes",
            "dungeon": [
                "/actions/combat/enchanted_fortress"
            ],
            "sortIndex": 6
        },
        "/monsters/werewolf": {
            "en": "Werewolf",
            "cn": "狼人",
            "zone": "/actions/combat/twilight_zone",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 10
        },
        "/monsters/zombie": {
            "en": "Zombie",
            "cn": "僵尸",
            "zone": "/actions/combat/twilight_zone",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": 10
        },
        "/monsters/zombie_bear": {
            "en": "Zombie Bear",
            "cn": "僵尸熊",
            "zone": "",
            "dungeon": [
                "/actions/combat/sinister_circus"
            ],
            "sortIndex": -1
        }
    };

    function getTaskDetailFromTaskName(fullTaskName) {
        var taskType = -1;
        var taskName = "";

        if (/^(.+) - (.+)$/.test(fullTaskName)) {
            let res = /^(.+) - (.+)$/.exec(fullTaskName);
            if (res[1] in taskOrderIndex) {
                taskType = taskOrderIndex[res[1]];
            }
            else if (res[1] in taskOrderIndex_CN) {
                taskType = taskOrderIndex_CN[res[1]];
            }
            taskName = res[2];
        }
        if (taskType == -1) console.log(fullTaskName, taskType);

        return { taskType, taskName };
    }

    function getHridFromMonsterName(name) {
        for (let key in allMonster) {
            if (allMonster[key].en === name || allMonster[key].cn === name) {
                return key;
            }
        }
        return null;
    }
    function getMapIndexFromMonsterName(name) {
        const key = getHridFromMonsterName(name);
        if (!key) {
            console.log("Monster not found", name);
            return -1;
        }
        return allMonster[key].sortIndex;
    }

    function getTaskDetailFromElement(ele) {
        const div = ele.querySelector("div.RandomTask_name__1hl1b");

        const translatedfrom = div.getAttribute("script_translatedfrom"); //adapt old CN Script
        if (translatedfrom) {
            return getTaskDetailFromTaskName(translatedfrom);
        }

        const fullTaskName = Array.from(div.childNodes).find(node => node.nodeType === Node.TEXT_NODE).textContent.trim();
        return getTaskDetailFromTaskName(fullTaskName);
    }

    function compareFn(a, b) {
        var { taskType: a_TypeIndex, taskName: a_taskName } = getTaskDetailFromElement(a);

        var { taskType: b_TypeIndex, taskName: b_TaskName } = getTaskDetailFromElement(b);

        if (a_TypeIndex === taskBattleIndex && b_TypeIndex === taskBattleIndex) {
            var a_MapIndex = getMapIndexFromMonsterName(a_taskName);
            var b_MapIndex = getMapIndexFromMonsterName(b_TaskName);

            if (a_MapIndex != b_MapIndex) {
                return (a_MapIndex > b_MapIndex ? 1 : -1);
            }
        }

        if (a_TypeIndex == b_TypeIndex) {
            return a_taskName == b_TaskName ? 0
                : (a_taskName > b_TaskName ? 1 : -1);
        }

        return a_TypeIndex > b_TypeIndex ? 1 : -1;
    }

    function addIconToTask(div) {
        var { taskType, taskName } = getTaskDetailFromElement(div);

        if (taskType != taskBattleIndex) {
            return;
        }

        const monsterHrid = getHridFromMonsterName(taskName);
        if (!monsterHrid) {
            return;
        }

        var offset = 5; // 5% from left and each 30% width
        if (!isShowDungeon) {
            offset = 50;
        }

        const backgroundDiv = document.createElement('div');
        backgroundDiv.id = "MonsterIcon";
        backgroundDiv.style.position = 'absolute';
        backgroundDiv.style.left = `${offset}%`; offset += 30;
        backgroundDiv.style.width = '30%';
        backgroundDiv.style.height = '100%';
        backgroundDiv.style.opacity = '0.3';

        const monsterName = monsterHrid.split("/").pop();
        const svgContent = `<svg width="100%" height="100%"><use href="/static/media/combat_monsters_sprite.395438a8.svg#${monsterName}"></use></svg>`;
        backgroundDiv.innerHTML = svgContent;

        div.appendChild(backgroundDiv);


        const dungeonMap = allMonster[monsterHrid]?.dungeon;
        if (isShowDungeon && dungeonMap) {
            ["/actions/combat/chimerical_den", "/actions/combat/sinister_circus", "/actions/combat/enchanted_fortress"].forEach(dungeon => {
                if (dungeonMap.includes(dungeon)) {
                    const dungeonDiv = document.createElement('div');
                    dungeonDiv.id = "DungeonIcon";
                    dungeonDiv.style.position = 'absolute';
                    dungeonDiv.style.left = `${offset}%`; offset += 30;
                    dungeonDiv.style.width = '30%';
                    dungeonDiv.style.height = '100%';
                    dungeonDiv.style.opacity = '0.3';

                    const dungeonName = dungeon.split("/").pop();
                    const svgContent = `<svg width="100%" height="100%"><use href="/static/media/actions_sprite.8d5ceb4a.svg#${dungeonName}"></use></svg>`;
                    dungeonDiv.innerHTML = svgContent;

                    div.appendChild(dungeonDiv);
                }
            })
        }

        // fix button style
        div.style.position = 'relative';
        div.querySelector(".RandomTask_content__VVQva").style.zIndex = 1;
        div.querySelectorAll(".Item_item__2De2O").forEach(node => node.style.backgroundColor = "transparent");

    }

    function addButton() {
        const targetNode = document.querySelector("div.TasksPanel_taskSlotCount__nfhgS");
        if (targetNode) {
            let sortButton = targetNode.querySelector("#TaskSort");
            if (!sortButton) {
                sortButton = document.createElement("button");
                sortButton.setAttribute("class", "Button_button__1Fe9z Button_small__3fqC7");
                sortButton.id = "TaskSort";
                sortButton.innerHTML = "TaskSort";
                sortButton.addEventListener("click", function (evt) {
                    const list = document.querySelector("div.TasksPanel_taskList__2xh4k");
                    [...list.querySelectorAll("div.RandomTask_randomTask__3B9fA")]
                        .sort(compareFn)
                        .forEach(node => list.appendChild(node));
                });
                targetNode.appendChild(sortButton);
                if (isShowIcon)
                    document.querySelectorAll("div.TasksPanel_taskList__2xh4k div.RandomTask_randomTask__3B9fA").forEach(node => addIconToTask(node));
            }
        }
    }

    const config = { attributes: true, childList: true, subtree: true };

    const observer = new MutationObserver(function (mutationsList, observer) {
        addButton();
    });

    observer.observe(document, config);

})();