金庸群侠•大乱斗额外按钮

金庸群侠•大乱斗便利性按钮插件:1.战斗中修改主角目标选择策略按钮;2.残卷使用数量+30按钮。

// ==UserScript==
// @name         金庸群侠•大乱斗额外按钮
// @namespace    http://tampermonkey.net/
// @version      0.6.161
// @description  金庸群侠•大乱斗便利性按钮插件:1.战斗中修改主角目标选择策略按钮;2.残卷使用数量+30按钮。
// @author       Ymmzy
// @match        https://zhouxiaobo1990.gitee.io/jyf/0.*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=gitee.io
// @grant        none
// @run-at       document-end
// @license      MIT
// ==/UserScript==
const ELEMENT = {
    navigator: {
        selector: "#navigator"
    },
    dialogTitle: {
        selector: "#actionDialog > div > div.mdc-dialog__container > div.mdc-dialog__surface > h2"
    },
    dialogConfirm: {
        selector: "#actionDialog > div > div.mdc-dialog__container > div.mdc-dialog__surface > div.mdc-dialog__actions > button.acceptButton",
        condition: ele => !document.querySelector("#actionDialog > div > div.mdc-dialog__container > div.mdc-dialog__surface > div.mdc-dialog__actions").classList.contains("hidden")
    },
    battleButtonContainer: {
        selector: "#battleUiPage > div.pixiContainer > div.bottomLeftBar"
    },
    battleSetting: {
        selector: "#battleUiPage > div.pixiContainer > div.topRightBar > button.settingsButton"
    },
    battleSettingTarget: {
        selector: "#actionDialog > div > div.mdc-dialog__container > div.mdc-dialog__surface > div.mdc-dialog__content > div > div > button",
        text: "目标选择策略"
    },
    battleSettingBack: {
        selector: "#actionDialog > div > div.mdc-dialog__container > div.mdc-dialog__surface > div.mdc-dialog__content > div > div > button",
        text: "返回"
    },
    battleTargetItem: {
        selector: "#actionDialog > div > div.mdc-dialog__container > div.mdc-dialog__surface > div.mdc-dialog__content > div > div:nth-child(1) > div > div > div.mdc-select__menu > ul > li"
    },
    itemTitle: {
        selector: "#inspectItemsPage > main > div > div.jyfPageTemplate.mainContent > div.description > div.name"
    },
    itemNumberLabel: {
        selector: "#actionDialog > div > div.mdc-dialog__container > div.mdc-dialog__surface > div.mdc-dialog__content > div > div > div > label"
    },
    itemNumberInput: {
        selector: "#actionDialog > div > div.mdc-dialog__container > div.mdc-dialog__surface > div.mdc-dialog__content > div > div > div > label > input"
    }
};

function getEle({selector, text, condition}) {
    const eles = document.querySelectorAll(selector);
    for (const ele of eles) {
        if (ele && ele.style.display !== 'none' && (!text || ele.textContent.includes(text)) && (!condition || condition(ele))) return ele;
    }
    return null;
}

async function waitEle({selector, text, condition}) {
    let ele = null;
    let time = 100;
    await new Promise(resolve => {
        const timer = setInterval(() => {
            if ((ele = getEle({selector, text, condition}))) {
                clearInterval(timer);
                resolve();
            }
            if (!time--) {
                console.warn("Timeout:", {selector, text, condition});
                resolve();
            }
        }, 10);
    });
    return ele;
}

async function clickEle({selector, text, condition}) {
    let ele = await waitEle({selector, text, condition});
    ele && ele.click();
}

function createEle(parent, type, classes, text) {
    const ele = document.createElement(type);
    if (classes && classes.length > 0) ele.classList.add(...classes);
    if (text) ele.innerText = text;
    if (parent) parent.appendChild(ele);
    return ele;
}

//残卷+30按钮
function initAddThirtyButton() {
    const dialogTitle = getEle(ELEMENT.dialogTitle);

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

    const mutationObserver = new MutationObserver(async (mutationsList, observer) => {
        for (const mutation of mutationsList) {
            if (mutation.type === 'childList' && mutation.target === dialogTitle && mutation.addedNodes.length > 0) {
                const addedNode = mutation.addedNodes[0];
                if (addedNode.nodeType === Node.TEXT_NODE && addedNode.textContent.match(/使用.+残卷/)) {
                    observer.disconnect();

                    const label = await waitEle(ELEMENT.itemNumberLabel);
                    const btn = createEle(label, "button", ["maxValueButton", "mdc-icon-button"], "+30");
                    btn.style.alignSelf = "center";
                    btn.style.display = "flex";
                    btn.style.justifyContent = "center";
                    btn.style.alignItems = "center";
                    btn.style.fontSize = "18px";
                    btn.onclick = () => {
                        //获取持有数量
                        const item = getEle(ELEMENT.itemTitle);
                        const match = item.innerText.match(/剩余:([\d]+)/);
                        if (match) {
                            const max = Number(match[1]);
                            const input = getEle(ELEMENT.itemNumberInput);
                            input.value = Math.min(max, (Math.floor(input.value / 30) + 1) * 30);
                        }
                    };

                    observer.observe(dialogTitle, config);
                }
            }
        }
    });

    mutationObserver.observe(dialogTitle, config);
}

//战斗目标策略按钮
function initBattleControlButton() {
    const battleButtonContainer = getEle(ELEMENT.battleButtonContainer);
    let lock = false;
    const createBattleControlButton = (text, act, x, y) => {
        const btn = createEle(battleButtonContainer, "button", ["mdc-icon-button", "battle-control-button"], text);
        btn.onclick = async () => {
            if (lock) return;
            lock = true;

            await clickEle(ELEMENT.battleSetting);
            const battleTarget = await waitEle(ELEMENT.battleSettingTarget);
            //目标策略按钮是否可见
            if (!battleTarget.parentElement.classList.contains("hidden")) {
                battleTarget.click();
                await clickEle({...ELEMENT.battleTargetItem, text: act});
                await clickEle(ELEMENT.dialogConfirm);
            } else await clickEle(ELEMENT.battleSettingBack);

            lock = false;
        };
        const biasX = 52 * x;
        const biasY = 52 * y;
        btn.style.left = biasX + "px";
        btn.style.bottom = biasY + "px";
        btn.style.position = "absolute";
        btn.style.fontSize = "18px";
    };

    createBattleControlButton("北", "向北移动", 1, 2);
    createBattleControlButton("南", "向南移动", 1, 0);
    createBattleControlButton("西", "向西移动", 0, 1);
    createBattleControlButton("东", "向东移动", 2, 1);
    createBattleControlButton("近", "最近的敌人", 1, 1);
}

(async function () {
    'use strict';
    await waitEle(ELEMENT.navigator);

    initAddThirtyButton();
    initBattleControlButton();

    console.log("===== extra buttons =====");
})();