wsmud_Raid

武神传说 MUD

目前為 2018-12-30 提交的版本,檢視 最新版本

// ==UserScript==
// @name         wsmud_Raid
// @namespace    cqv
// @version      0.5.0
// @date        23/12/2018
// @modified     28/12/2018
// @homepage     https://gf.qytechs.cn/zh-CN/scripts/375851
// @description  武神传说 MUD
// @author       Bob.cn
// @match        http://game.wsmud.com/*
// @match        http://www.wsmud.com/*
// @run-at       document-end
// @require      https://cdn.staticfile.org/vue/2.2.2/vue.min.js
// @grant        unsafeWindow
// @grant        GM_getValue
// @grant        GM_setValue

// ==/UserScript==

(function () {
    'use strict';

    var WG = unsafeWindow.WG;
    var messageAppend = undefined;
    var messageClear = undefined;

    var Role = {
        id: undefined,
        name: undefined,

        hp: 0,
        maxHp: 0,
        mp: 0,
        maxMp: 0,

        status: [],

        equipments: [],

        init: function() {
            WG.add_hook("login", function(data) {
                Role.id = data.id;
                Role.status = [];
            });

            $("li[command=SelectRole]").on("click", function () {
                Role.name = $('.role-list .select').text().replace(/[\s]+/,".");
            });
            
            Role._monitorHpMp();
            Role._monitorStatus();
            Role._monitorEquipments();
            Role._monitorSkillCD();
            Role._monitorLocation();
            Role._monitorItemsInRoom();
        },

        hasStatus: function(s) {
            return Role.status.indexOf(s) != -1;
        },
        isFree: function() {
            return !Role.hasStatus("busy") && !Role.hasStatus("faint") && !Role.hasStatus("rash");
        },

        atPath: function(p) {
            switch (arguments.length) {
            case 0:
                return Role._roomPath;
            case 1:
                return p == Role._roomPath;
            }
        },
        inRoom: function(n) {
            switch (arguments.length) {
            case 0:
                return Role._roomName;
            case 1:
                return n == Role._roomName;
            }
        },

        renew: function(callback) {
            if (!Role.isFree) {
                window.setTimeout(function() { Role.renew(callback) }, 2000);
                return;
            }

            switch (Role._renewStatus) {
            case "resting":
                WG.go("扬州城-武庙");

                if (Role._renewHookIndex) WG.remove_hook(Role._renewHookIndex);
                Role._renewHookIndex = WG.add_hook("text", function(data) {
                    let patt1 = new RegExp("你运功完毕,深深吸了口气,站了起来。");
                    let count1 = patt1.exec(data.msg);
                    if (count1) {
                        Role._renewStatus = "dazuo finish"; return;
                    }
                    let patt2 = new RegExp("你目前气血充沛,没有受到任何伤害。|你疗伤完毕,深深吸了口气,脸色看起来好了很多。");
                    let count2 = patt2.exec(data.msg);
                    if (count2) {
                        Role._renewStatus = "liaoshang finish"; return;
                    }
                });

                Role._renewStatus = "liaoshang doing";
                WG.Send("stopstate;liaoshang");
                break;
            case "liaoshang finish":
                if (Role.mp/Role.maxMp < 0.7) {
                    Role._lastWeapon = Role.equipments[0];
                    Role._renewStatus = "dazuo doing";
                    WG.Send("stopstate;dazuo");
                }
                break;
            case "liaoshang doing":
            case "dazuo doing":
            case "dazuo finish":
                break;
            }

            if (Role._renewStatus == "liaoshang finish" || Role._renewStatus == "dazuo finish") {
                if (Role._renewStatus == "liaoshang finish") {
                    if (callback) callback();
                } else if (Role._renewStatus == "dazuo finish") {
                    window.setTimeout(function() {
                        WG.Send("stopstate");
                        Role.getDressed([Role._lastWeapon]);
                        if (callback) callback();
                    }, 4500);
                }
                WG.remove_hook(Role._renewHookIndex);
                Role._renewHookIndex = undefined;
                Role._renewStatus = "resting";
                return;
            }
            
            window.setTimeout(function() { Role.renew(callback); }, 2000);
        },

        cleanBag: function(callback) {
            WG.clean_all();
            if (callback) callback();
        },

        tidyBag: function(callback) {
            Role._tidyBag(0, callback);
        },

        getDressed: function(equipments) {
            for (var i = equipments.length - 1; i >= 0; i--) {
                let e = equipments[i];
                if (e == null) {
                    WG.Send("uneq " + Role.equipments[i]);
                } else {
                    WG.Send("eq " + e);
                }
            }
        },

        hasCoolingSkill: function() {
            return Role._coolingSkills.length > 0;
        },

        findItem: function(name) {
            for (var i = 0; i < Role._itemsInRoom.length; i++) {
                let item = Role._itemsInRoom[i];
                if (item.name == name) {
                    return item.id;
                }
            }
            return null;
        },

        _renewHookIndex: undefined,
        _renewStatus: "resting",

        _coolingSkills: [],

        _itemsInRoom: {},

        _tidyBag: function(counter, callback) {
            if (counter == 0) WG.sell_all();

            if (!WG.packup_listener) {
                window.setTimeout(callback, 2000);
                return;
            }
            if (counter > 5) {
                if (WG.packup_listener) WG.sell_all();
                callback();
                return;
            }
            window.setTimeout(function() { Role._tidyBag(counter + 1, callback); }, 1000);
        },

        _monitorHpMp: function() {
            WG.add_hook(["items", "sc", "itemadd"], function(data) {
                switch (data.type) {
                case "items":
                    if (data.items == undefined) break;
                    for (var i = data.items.length - 1; i >= 0; i--) {
                        let item = data.items[i]
                        if (item.id == Role.id) {
                            Role.hp = item.hp;
                            Role.maxHp = item.max_hp;
                            Role.mp = item.mp;
                            Role.maxMp = item.max_mp;
                            break;
                        }
                    }
                    break;
                case "itemadd":
                case "sc":
                    if (data.id != Role.id) break;
                    if (data.hp != undefined) Role.hp = data.hp;
                    if (data.max_hp != undefined) Role.maxHp = data.max_hp;
                    if (data.mp != undefined) Role.mp = data.mp;
                    if (data.max_mp != undefined) Role.maxMp = data.max_mp;
                    break;
                }
            });
        },
        _monitorStatus: function() {
            WG.add_hook(["items", "status", "itemadd"], function(data) {
                switch (data.type) {
                case "items":
                    if (data.items == undefined) break;
                    for (var i = data.items.length - 1; i >= 0; i--) {
                        let item = data.items[i];
                        if (item.id != Role.id) continue;
                        if (item.status == undefined) break;
                        Role.status = [];
                        for (var j = item.status.length - 1; j >= 0; j--) {
                            let s = item.status[j];
                            Role.status.push(s.sid);
                        }
                        break;
                    }
                    break;
                case "status":
                    if (data.id != Role.id) break;
                    if (data.action == "add") {
                        Role.status.push(data.sid);
                    } else if (data.action == "remove") {
                        let index = Role.status.indexOf(data.sid);
                        if (index == -1) return;
                        Role.status.splice(index,1);
                    }
                    break;
                case "itemadd":
                    if (data.id != Role.id) break;
                    if (data.status == undefined) break;
                    Role.status = [];
                    for (var k = data.status.length - 1; k >= 0; k--) {
                        let s = data.status[k];
                        Role.status.push(s.sid);
                    }
                    break;
                }
            });
        },
        _monitorEquipments: function() {
            WG.add_hook("dialog", function(data) {
                if (data.dialog != "pack") return;
                if (data.eqs != undefined) {
                    for (var i = 0; i < data.eqs.length; i++) {
                        let eq = data.eqs[i];
                        if (eq != null && eq.id != null) {
                            Role.equipments.push(eq.id);
                        } else {
                            Role.equipments.push(null);
                        }
                    }
                } else if (data.uneq != undefined) {
                    Role.equipments[data.uneq] = null;
                } else if (data.eq != undefined) {
                    Role.equipments[data.eq] = data.id;
                } else {
                    return;
                }
            });
        },
        _monitorSkillCD: function() {
            WG.add_hook("dispfm", function(data) {
                let timestamp = Date.parse(new Date());
                let mark = data.id + "_" + timestamp;
                Role._coolingSkills.push(mark);
                window.setTimeout(function() {
                    let index = Role._coolingSkills.indexOf(mark);
                    if (index != -1) Role._coolingSkills.splice(index, 1);
                }, data.distime);
            });
        },
        _monitorLocation: function() {
            WG.add_hook("room", function(data) {
                Role._roomName = data.name;
                Role._roomPath = data.path;
            });
        },
        _monitorItemsInRoom: function() {
            WG.add_hook(["items", "itemadd", "itemremove"], function(data) {
                switch (data.type) {
                case "items":
                    if (data.items == undefined) break;
                    Role._itemsInRoom = [];
                    for (var i = 0; i < data.items.length; i++) {
                        let item = data.items[i];
                        if (item.name == undefined || item.id == undefined) continue;
                        Role._itemsInRoom.push(item);
                    }
                    break;
                case "itemadd":
                    if (data.name == undefined || data.id == undefined) break;
                    Role._itemsInRoom.push(data);
                    break;
                case "itemremove":
                    for (var i = 0; i < Role._itemsInRoom.length; i++) {
                        let item = Role._itemsInRoom[i];
                        if (item.id == data.id) {
                            Role._itemsInRoom.splice(i, 1);
                        }
                    }
                    break;
                }
            });
        },
    };

    var Config = {
        hpThresholdInDungeon: function() {
            return GM_getValue(Role.id + "@hpThresholdInRaid", "50");
        },
        setHpThresholdInDungeon: function(value) {
            GM_setValue(Role.id + "@hpThresholdInRaid", value);
        },

        waitSkillCD: function() {
            return GM_getValue(Role.id + "@waitSkillCD", "no");
        },
        setWaitSkillCD: function(value) {
            GM_setValue(Role.id + "@waitSkillCD", value);
        },

        // none, clean, tidy
        bagCleanWay: function() {
            return GM_getValue(Role.id + "@bagCleanWay", "clean");
        },
        setBagCleanWay: function(value) {
            GM_setValue(Role.id + "@bagCleanWay", value);
        },

        cmdInterval: function() {
            return GM_getValue(Role.id + "@cmdInterval", 1000);
        },

        setCmdInterval: function(value) {
            GM_setValue(Role.id + "@cmdInterval", value);
        },

        /*
        hpThresholdInRaid: function(raid) {
            return GM_getValue(Role.id + "@hpThresholdInRaid@" + raid, "50");
        },
        setHpThresholdInRaid: function(raid, value) {
            GM_setValue(Role.id + "@hpThresholdInRaid@" + raid, value);
        },

        waitSkillCD: function(raid) {
            return GM_getValue(Role.id + "@waitSkillCD@" + raid, "no");
        },
        setWaitSkillCD: function(raid, value) {
            GM_setValue(Role.id + "@waitSkillCD@" + raid, value);
        },
         */

        wudaota: {
            autoToFloor: function() {
                return GM_getValue(Role.id + "@wudao.autoToFloor", 0);
            },
            setAutoToFloor: function(value) {
                GM_setValue(Role.id + "@wudao.autoToFloor", value);
            },

            fastCombatOpening: function() {
                return GM_getValue(Role.id + "@wudao.fastCombatOpening", "no");
            },
            setFastCombatOpening: function(value) {
                GM_setValue(Role.id + "@wudao.fastCombatOpening", value);
            },

            hpThresholdInRaid: function() {
                return GM_getValue(Role.id + "@wudao.hpThresholdInRaid", "50");
            },
            setHpThresholdInRaid: function(value) {
                GM_setValue(Role.id + "@wudao.hpThresholdInRaid", value);
            },

            waitSkillCDFrom: function() {
                return GM_getValue(Role.id + "@wudao.waitSkillCDFrom", 100);
            },
            setWaitSkillCDFrom: function(value) {
                GM_setValue(Role.id + "@wudao.waitSkillCDFrom", value);
            },
        },
    };

    /* ------------------------ CmdExecuter ------------------------ *\
    \* ------------------------------------------------------------- */

    function CmdExecuter(cmds, willStartExecute, didFinishExecute, willPerformCmd, didPerformCmd, interval) {
        this.cmds = cmds;
        this.willStartExecute = willStartExecute;
        this.didFinishExecute = didFinishExecute;
        this.willPerformCmd = willPerformCmd;
        this.didPerformCmd = didPerformCmd;
        this.interval = interval ? interval : 1000;
    }
    CmdExecuter.prototype.execute = function() {
        if (this.isWorking) return;
        this.isWorking = true;

        if (this.willStartExecute) this.willStartExecute();

        this._monitorItemsInRoom();

        this._performCmd(0);
    };
    CmdExecuter.prototype._performCmd = function(index) {
        if (index >= this.cmds.length) {
            this._finishExecute();
            return;
        }

        if (!Role.isFree()) { this._delayPerformCmd(index); return; }

        var cmd = this.cmds[index];
        if (this.willPerformCmd) {
            var lastCmd = null;
            if (index > 0) lastCmd = this.cmds[index - 1];
            let valid = this.willPerformCmd(lastCmd, cmd);
            if (!valid) { this._delayPerformCmd(index); return; }
            cmd = valid;
        }

        // kill?开头,询问击杀是否完成命令
        if (cmd.indexOf("kill?") != -1) {
            let items = cmd.substring(5).split(";");
            if (CmdExecuter._removedItems == undefined) {
                this._delayPerformCmd(index); 
                return;
            }
            var removedItems = CmdExecuter._removedItems.slice();
            for (var i = 0; i < items.length; i++) {
                let item = items[i];
                let k = removedItems.indexOf(item);
                if (k == -1) { // 尚存在未击杀对象
                    WG.Send("kill " + Role.findItem(item));
                    this._delayPerformCmd(index); 
                    return;
                }
                removedItems.splice(k, 1);
            }
            CmdExecuter._removedItems = [];
        }

        // {item.name} 会被该 item 的 id 替换
        let patt = /\{.*?\}/g;
        var placeholders = [];
        var result = patt.exec(cmd);
        while(result != null) {
            placeholders.push(result[0]);
            result = patt.exec(cmd);
        }
        var realCmd = cmd;
        for (var j = 0; j < placeholders.length; j++) {
            let placeholder = placeholders[j];
            let item = Role.findItem(placeholder.substring(1, placeholder.length - 1));
            if (item == null) {
                this._delayPerformCmd(index); 
                return;
            }
            realCmd = realCmd.replace(placeholder, item);
        }
        cmd = realCmd;

        // @开头,虚命令,不真正执行
        if (cmd.indexOf("@") == -1 && cmd.indexOf("kill?") == -1) {
            console.log("执行命令:" + cmd);
            WG.Send(cmd);
        }
        if (this.didPerformCmd) { this.didPerformCmd(cmd); }

        // [exit] 保留命令,立即退出执行器
        if (cmd.indexOf("[exit]") != -1) {
            this._finishExecute();
            return;
        } else {

            this._delayPerformCmd(index + 1);
        }
    };
    CmdExecuter.prototype._delayPerformCmd = function(index) {
        let executer = this;
        window.setTimeout(function() { 
            executer._performCmd(index); 
        }, executer.interval);
    };
    CmdExecuter.prototype._monitorItemsInRoom = function() {
        CmdExecuter._hookIndex = WG.add_hook(["items", "itemadd", "itemremove"], function(data) {
            switch (data.type) {
            case "items":
                if (data.items == undefined) break;
                CmdExecuter._itemsInRoom = [];
                CmdExecuter._removedItems = [];
                for (var i = 0; i < data.items.length; i++) {
                    let item = data.items[i];
                    if (item.name == undefined || item.id == undefined) continue;
                    CmdExecuter._itemsInRoom.push(item);
                }
                break;
            case "itemadd":
                if (CmdExecuter._itemsInRoom == undefined) break;
                if (data.name == undefined || data.id == undefined) break;
                CmdExecuter._itemsInRoom.push(data);
                break;
            case "itemremove":
                if (CmdExecuter._itemsInRoom == undefined) break;
                for (var i = 0; i < CmdExecuter._itemsInRoom.length; i++) {
                    let item = CmdExecuter._itemsInRoom[i];
                    if (item.id == data.id) {
                        CmdExecuter._itemsInRoom.splice(i, 1);
                        CmdExecuter._removedItems.push(item.name);
                    }
                }
                break;
            }
        });
    };
    CmdExecuter.prototype._finishExecute = function() {
        this.isWorking = false;
        WG.remove_hook(CmdExecuter._hookIndex);
        if (this.didFinishExecute) this.didFinishExecute();
    };

    /* ------------------------ Workflow ------------------------ *\
    \* ---------------------------------------------------------- */

    function WorkItem(name, run, defaultParams) {
        this.name = name;
        this.run = run;
        this._params = defaultParams;
        this.description = function() {
            return this.name;
        };
    }
    WorkItem.prototype.didFinish = function() {
        this._didFinish();
    };
    // 可以为 WorkItem 设置 assert (function()),若返回 false 则该 WorkItem 不会被执行
    WorkItem.prototype.setAssert = function(assert) {
        this._assert = assert;
    };
    // 可以为 WorkItem 设置 params,如果该 WorkItem 需要的话
    WorkItem.prototype.setParams = function(params) {
        this._params = params;
    };

    var WorkItemCenter = {
        register: function(value, key) {
            if (typeof value == "function") {
                WorkItemCenter._items[key] = value;
            } else {
                WorkItemCenter._items[value.name] = value;
            }
        },
        take: function(key) {
            return WorkItemCenter._items[key];
        },
        allKey: function() {
            return Object.keys(WorkItemCenter._items);
        },
        _items: {},
    };

    WorkItemCenter.register(new WorkItem("回满状态", function() {
        let item = this;
        Role.renew(function() {
            item.didFinish();
        });
    }));
    WorkItemCenter.register(new WorkItem("清理背包", function() {
        let item = this;
        Role.cleanBag(function() {
            item.didFinish();
        });
    }));
    WorkItemCenter.register(new WorkItem("整理背包", function() {
        let item = this;
        Role.tidyBag(function() {
            item.didFinish();
        });
    }));
    WorkItemCenter.register(new WorkItem("通用结束动作", function() {
        WG.go("练功房");
        let item = this;
        window.setTimeout(function() {
            WG.Send("stopstate;dazuo");
            item.didFinish();
        }, 1000);
    }));
    WorkItemCenter.register(new WorkItem("等待技能冷却", function() {
        var timer;
        let item = this;
        timer = window.setInterval(function() {
            if (!Role.hasCoolingSkill()) {
                window.clearInterval(timer);
                item.didFinish();
            }
        }, 1000);
    }));
    WorkItemCenter.register(new WorkItem("等待时间", function() {
        let item = this;
        window.setTimeout(function() {
            item.didFinish();
        }, item._params);
    }), 1000);

    var Workflow = {
        name: undefined,
        items: [],

        willStartWorkflow: undefined,
        didFinishWorkflow: undefined,

        willStartWorkItem: undefined,       // optional: function(item.name)
        didFinishWorkItem: undefined,       // optional: function(item.name)

        start: function() {
            WG.Send('stopstate');
            WG.stopAllAuto();
            messageClear()
            messageAppend("正在运行工作流 " + Workflow.name + "...");
            messageAppend("* 运行工作流期间会暂时关闭 自动Boss 和 自动婚宴。");
            messageAppend("* 如需要立即中断,请刷新网页。");

            if (Workflow.willStartWorkflow) Workflow.willStartWorkflow();
            Workflow._start(0);
        },

        _start: function(number) {
            if (number >= Workflow.items.length) {
                Workflow._finish();
                return;
            }

            let item = Workflow.items[number];

            if (item._assert && item._assert() == false) {
                Workflow._start(number + 1);
                return;
            }

            item._didFinish = function() {
                if (Workflow.didFinishWorkItem) Workflow.didFinishWorkItem(item);
                messageAppend("▶ " + item.description() + "执行完毕。");
                window.setTimeout(function() {
                    Workflow._start(number + 1);
                }, 2000);
            };

            if (Workflow.willStartWorkItem) Workflow.willStartWorkItem(item);
            messageAppend("▷ 正在运行 " + item.description() + "...");
            item.run(item._params);
        },
        _finish: function() {
            let name = Workflow.name;
            Workflow.name = undefined;
            Workflow.items = [];
            Workflow.willStart = undefined;
            let didFinishWorkflow = Workflow.didFinishWorkflow;
            Workflow.didFinish = undefined;
            Workflow.willStartWorkItem = undefined;
            Workflow.didFinishWorkItem = undefined;

            messageAppend("顺利完成工作流 " + name + "。");
            WG.reSetAllAuto();
            if (didFinishWorkflow) didFinishWorkflow();
        }
    };

    /* ------------------------ Dungeon ------------------------ *\
    \* --------------------------------------------------------- */

    function Dungeon(name, description, cmds, willStartDungeon, didFinishDungeon, willPerformCmd, didPerformCmd) {
        this.name = name;
        this.description = description;
        this.cmds = cmds;
        this.willStartDungeon = willStartDungeon;
        this.didFinishDungeon = didFinishDungeon;
        this.willPerformCmd = willPerformCmd;

        var totalCmds = this.cmds.slice(); totalCmds.push("cr;cr over");
        let dungeon = this;
        let didFinishExecute = function() {
            if (dungeon.didFinishDungeon) dungeon.didFinishDungeon();
        };
        let executerWillPerformCmd = function(lastCmd, cmd) {
            return dungeon._willPerformCmd(lastCmd, cmd);
        };
        this.executer = new CmdExecuter(
            totalCmds,
            undefined,
            didFinishExecute,
            executerWillPerformCmd,
            didPerformCmd,
            Config.cmdInterval()
        );
    }
    Dungeon.prototype.enter = function() {
        this._reset();

        if (this.willStartDungeon) { this.willStartDungeon(); }

        this.executer.execute();
    };
    Dungeon.prototype._reset = function() {
        this._liaoshangDoing = false;
        this._waitingSkillCD = false;
        this._noneEnemy = false;
    };
    Dungeon.prototype._willPerformCmd = function(lastCmd, cmd) {
        if (this._liaoshangDoing) {
            if (Role.hp/Role.maxHp < 0.99) {
                return null;
            } else {
                this._liaoshangDoing = false;
                WG.Send("stopstate");
            }
        }
        if (Role.hp/Role.maxHp < Config.hpThresholdInDungeon()/100 && !this._noneEnemy) {
            WG.Send("liaoshang");
            this._liaoshangDoing = true;
            return null;
        }

        // #结尾,表明执行完此命令将会遇到 Boss
        if (cmd.indexOf("#") != -1) {
            if (Config.waitSkillCD() == "yes" && Role.hasCoolingSkill()) {
                if (!this._waitingSkillCD) {
                    messageAppend("! 前方高能,等待技能冷却再战...");
                    this._waitingSkillCD = true;
                }
                return null;
            }
            cmd = cmd.substring(0, cmd.length - 1);
        }
        this._waitingSkillCD = false;

        // $结尾,表明之后的命令不再会遇敌
        if (cmd.indexOf("$") != -1) {
            this._noneEnemy = true;
            cmd = cmd.substring(0, cmd.length - 1);
        }

        if (this.willPerformCmd) cmd = this.willPerformCmd(lastCmd, cmd);
        return cmd;
    };

    // Dungeon Config

    let DungeonConfig = {

        "移花宫": {
            "shared": {
                willStartDungeon: function() {
                    let index1 = WG.add_hook("item", function(data) {
                        if (data.desc == undefined) return;
                        let patt = new RegExp("你数了下大概有\\d(?=朵花。)");
                        let result = patt.exec(data.desc);
                        if (result) {
                            let text = result.toString();
                            let count = text[text.length - 1];
                            ToRaid.leftPush = count;
                        }
                    });
                    let index2 = WG.add_hook("exits", function(data) {
                        if (data.items == undefined || data.items.down == undefined) return;
                        if (data.items.down == "密道") {
                            ToRaid.didFindSecretPath = true;
                        }
                    });
                    ToRaid.indexes = [index1, index2];

                    ToRaid.leftPush = 0;
                    ToRaid.didFindSecretPath = false;
                },
                didFinishDungeon: function() {
                    for (var i = ToRaid.indexes.length - 1; i >= 0; i--) {
                        let index = ToRaid.indexes[i];
                        WG.remove_hook(index);
                    }
                },
                willPerformCmd: function(lastCmd, cmd) {
                    if (lastCmd == "look hua" && (ToRaid.leftPush == undefined || ToRaid.leftPush == 0)) {
                        return null;
                    }
                    if (lastCmd == "look bed" && !ToRaid.didFindSecretPath) {
                        WG.Send("pushstart bed");
                        for (var i = ToRaid.leftPush - 1; i >= 0; i--) {
                            WG.Send("pushleft bed");
                        }
                        for (var j = 7; j >= 0; j--) {
                            WG.Send("pushright bed");
                        }
                        return null;
                    }
                    return cmd;
                },
            },
            "困难": {
                number: 222,
                cmds: [
                    "jh fb 22 start2;cr huashan/yihua/shandao 1 0",
                    "go south;go south;go south;go south;go south;go south;go south",
                    "go south;go south;go south;go south;go south;go south;go south",
                    "kill?花月奴",
                    "go south;go south",
                    "kill?移花宫女弟子;移花宫女弟子",
                    "go south",
                    "kill?移花宫女弟子;移花宫女弟子",
                    "go southeast#",
                    "kill?移花宫二宫主 涟星;移花宫大宫主 邀月",
                    "go northwest;go southwest",
                    "look hua",
                    "go southeast",
                    "look bed",
                    "go down;fire;go west",
                    "kill?移花宫少宫主 花无缺",
                    "look xia;open xia",
                    "@wait"
                ],
            },
            "简单": {
                number: 221,
                cmds: [
                    "jh fb 22 start1;cr huashan/yihua/shandao",
                    "go south;go south;go south;go south;go south;go south;go south",
                    "go south;go south;go south;go south;go south;go south;go south",
                    "kill?花月奴",
                    "go south;go south",
                    "kill?移花宫女弟子;移花宫女弟子",
                    "go south",
                    "kill?移花宫女弟子;移花宫女弟子",
                    "go southeast#",
                    "kill?移花宫二宫主 涟星",
                    "go northwest;go southwest",
                    "kill?移花宫大宫主 邀月",
                    "look hua",
                    "go southeast",
                    "look bed",
                    "go down;fire;go west",
                    "kill?移花宫少宫主 花无缺",
                    "look xia;open xia",
                    "@wait"
                ],
            },
        },

        "白驼山": {
            "shared": {
                cmds: [
                    "jh fb 19 start3;cr baituo/damen 2 0",
                    "go north;go north;go north",
                    "kill?白驼山少庄主 欧阳克;白衣少女",
                    "go north#",
                    "kill?<hiy>西毒</hiy> 欧阳锋",
                    "go south;go south;go south;go west;go west;go west",
                    "kill?毒蛇",
                    "go north",
                    "kill?毒蛇",
                    "go north;go north",
                    "kill?蟒蛇$"
                ],
            },
            "组队": {
                number: 193,
            }
        },

        "燕子坞": {
            "简单": {
                number: 231,
                cmds: [
                    "jh fb 23 start1;cr murong/anbian",
                    "go east;go east",
                    "kill?金凤庄庄主 包不同",
                    "go east;go south;go east;go south;go south",
                    "kill?曼佗罗山庄庄主 王夫人",
                    "go north;go north;go west;go north",
                    "go east;go east;go east#",
                    "kill?姑苏慕容公子 慕容复",
                    "go west;go north",
                    "look pai;bai pai;bai pai;bai pai",
                    "go north;search",
                    "go south#",
                    "kill?慕容博$"
                ],
            },
            "困难": {
                number: 232,
                cmds: [
                    "jh fb 23 start1;cr murong/anbian",
                    "go east;go east",
                    "kill?金凤庄庄主 包不同",
                    "go east;go south;go east;go south;go south",
                    "kill?曼佗罗山庄庄主 王夫人",
                    "go north;go north;go west;go north",
                    "go east;go east;go east#",
                    "kill?姑苏慕容公子 慕容复",
                    "go west;go north",
                    "look pai;bai pai;bai pai;bai pai",
                    "go north;search",
                    "go south#",
                    "kill?慕容博$"
                ],
            },
            "偷书": {
                number: 233,
                description: "👏 <hiy>感谢 Airson 提供本副本代码。</hiy>",
                cmds: [
                    "jh fb 23 start1;cr murong/anbian",
                    "go east;go east",
                    "kill?金凤庄庄主 包不同$",
                    "go east;go east;go east;go north",
                    "look pai;bai pai;bai pai;bai pai",
                    "go north;search",
                ],
            },
        },

        "华山论剑": {
            "shared": {
                number: 300,
                description: "👏 <hiy>感谢 koyodakla、freesunny 对本副本代码提供的帮助。</hiy>",
                cmds: [
                    "jh fb 30 start1;cr huashan/lunjian/leitaixia",
                    "go up#",
                    "kill?<hiy>东邪</hiy> 黄药师;<hiy>南帝</hiy> 一灯大师;<hiy>西毒</hiy> 欧阳锋;<hiy>北丐</hiy> 洪七公;<hiy>中神通</hiy> 王重阳$",
                    "jump bi",
                    "@get box",
                    "@wait"
                ],
                willPerformCmd: function(lastCmd, cmd) {
                    if (cmd == "@get box") WG.get_all();
                    return cmd;
                },
            },
        },

        "温府": {
            ">2k闪避": {
                number: 103,
                description: "👏 <hiy>感谢 JiaQi Wan 提供本副本代码。</hiy>",
                cmds: [
                    "jh fb 23 start2;cr cd/wen/damen",
                    "look tree;climb tree;go north;go northeast;go north;go north;go northwest;go north",
                    "look zhuang;tiao zhuang",
                    "kill?温家老二 温方义;温家老三 温方山;温家老四 温方施;温家老五 温方南;温家老大 温方达",
                    "look zhuang;tiao zhuang#",
                    "kill?<hiy>金蛇郎君</hiy> 夏雪宜",
                    "go north",
                    "kill?温家小姐 温仪$"
                ],
            }
        },

        "桃花岛": {
            "困难": {
                number: 182,
                cmds: [
                    "jh fb 18 start2;cr taohua/haitan 1 0",
                    "go south",
                    "@look 1",
                    "@look 5",
                    "go south;go south",
                    "kill?桃花岛四弟子 陆乘风",
                    "go east;go east",
                    "kill?桃花岛大弟子 曲灵风",
                    "go east;go north#",
                    "kill?<hiy>东邪</hiy> 黄药师$",
                ],
                willStartDungeon: function() {
                    TaohuaIsland._monitorMaze();
                },
                didFinishDungeon: function() {
                    TaohuaIsland._cancelMonitorMaze();
                },
                willPerformCmd: function(lastCmd, cmd) {
                    if (cmd == "@look 1") {
                        if (TaohuaIsland._goCenterCmd) {
                           return TaohuaIsland._goCenterCmd;
                        } else {
                            return null;
                        }
                    }
                    if (cmd == "@look 5") {
                        if (TaohuaIsland._decodedMaze) {
                            return TaohuaIsland._outMazeCmd();
                        } else {
                            return null;
                        }
                    }
                    return cmd;
                }
            }
        },

        "财主家": {
            "简单": {
                number: 21,
                cmds: [
                    "jh fb 1 start1;cr yz/cuifu/caizhu",
                    "kill?大狼狗;大狼狗",
                    "go north",
                    "kill?管家;家丁;家丁",
                    "go north#",
                    "kill?财主 崔员外",
                    "look men;open men;go east",
                    "ok {丫鬟}",
                    "go west;go south;go south",
                    "go north;go north;go west",
                    "select {财主女儿 崔莺莺};ask {财主女儿 崔莺莺} about 东厢",
                    "kill?财主女儿 崔莺莺$",
                    "go east;go east;look gui;search gui"
                ],
            },
            "困难": {
                number: 22,
                cmds: [
                    "jh fb 1 start2;cr yz/cuifu/caizhu 1 0",
                    "kill?大狼狗;大狼狗",
                    "go north",
                    "kill?管家;家丁;家丁",
                    "go north#",
                    "kill?财主 崔员外",
                    "look men;open men;go east",
                    "ok {丫鬟}",
                    "go west;go south;go south",
                    "go north;go north;go west",
                    "select {财主女儿 崔莺莺};ask {财主女儿 崔莺莺} about 东厢",
                    "kill?财主女儿 崔莺莺$",
                    "go east;go east;look gui;search gui"
                ],
            }
        },

        "星宿海": {
            "shared": {
                number: 200,
                cmds: [
                    "jh fb 20 start1;cr xingxiu/xxh6",
                    "go northeast",
                    "kill?星宿派八师兄 出尘子",
                    "go north",
                    "kill?星宿派小师妹 阿紫",
                    "go northwest",
                    "kill?星宿派二师兄 狮吼子",
                    "go southwest",
                    "kill?星宿派大师兄 摘星子",
                    "go south",
                    "kill?星宿派三师兄 天狼子",
                    "#go north;go northeast;go north$",
                    "kill?星宿老怪 丁春秋"
                ],
            },
        }
    };

    function parseDungeonConfig() {
        var result = [];
        for (var key in DungeonConfig) {
            let value = DungeonConfig[key];
            let count = Object.keys(value).length;
            var shared = value["shared"];
            if (shared && count > 1) {
                for (var subkey in value) {
                    if (subkey == "shared") continue;
                    var item = { };
                    for (var sharedKey in shared) {
                        item[sharedKey] = shared[sharedKey];
                    }
                    let subvalue = value[subkey];
                    for (var itemKey in subvalue) {
                        item[itemKey] = subvalue[itemKey];
                    }
                    item.name = key + "(" + subkey + ")";
                    result.push(item);
                }
            } else if (shared && count == 1) {
                shared.name = key;
                result.push(shared);
            } else if (count > 0) {
                for (var subkey in value) {
                    var item = { };
                    let subvalue = value[subkey];
                    for (var itemKey in subvalue) {
                        item[itemKey] = subvalue[itemKey];
                    }
                    item.name = key + "(" + subkey + ")";
                    result.push(item);
                }
            }
        }
        result.sort(function(a, b) {
            return b.number - a.number;
        });
        return result;
    }

    let ParsedDungeonConfig = parseDungeonConfig();

    (function registerDungeonWorkItems() {
        let item = new WorkItem("扫荡副本", function(dungeonName) {
            var config;
            for (var i = 0; i < ParsedDungeonConfig.length; i++) {
                let c = ParsedDungeonConfig[i];
                if (c.name == dungeonName) {
                    config = c; break;
                }
            }
            let dungeon = new Dungeon(
                config.name,
                config.description,
                config.cmds,
                config.willStartDungeon,
                undefined,
                config.willPerformCmd,
                config.didPerformCmd
            );
            let didFinishDungeon = config.didFinishDungeon;
            let item = this;
            dungeon.didFinishDungeon = function() {
                if (didFinishDungeon) didFinishDungeon();
                item.didFinish();
            };
            dungeon.enter();
        }, "副本名称");
        item.description = function() {
            return "扫荡" + this._params;
        };
        WorkItemCenter.register(item);
    })();

    /* ------------------------ Raid ------------------------ *\
    \* ------------------------------------------------------ */

    var Raid = {
        repeatRun: function(name) {
            let num = prompt("输入自动【" + name + "】副本次数,例如:\"1\"", '1');
            if (num > 0) {
                Raid._repeatTotal = num;
                Raid._repeatCounter = 0;
            } else {
                return;
            }

            Workflow.name = "自动扫荡副本 <" + name + "> " + num + "次";
            var items = [];
            let toDungeon = WorkItemCenter.take("扫荡副本");
            toDungeon.setParams(name);
            let cleanBagItem = Raid._cleanBagItem();
            for (var i = 0; i < num; i++) {
                items.push(WorkItemCenter.take("回满状态"));
                if (cleanBagItem) items.push(cleanBagItem);
                items.push(toDungeon);
            }
            if (cleanBagItem) items.push(cleanBagItem);
            items.push(WorkItemCenter.take("通用结束动作"));
            Workflow.items = items;
            Workflow.willStartWorkItem = function(item) {
                if (item.name == "扫荡副本") {
                    Raid._repeatCounter += 1;
                    messageAppend("- 自动扫荡进度:正在进行 " + Raid._repeatCounter + "/" + Raid._repeatTotal + "...");
                }
            };
            Workflow.start();
        },

        _cleanBagItem: function() {
            switch (Config.bagCleanWay()) {
            case "clean":
                return WorkItemCenter.take("清理背包");
            case "tidy":
                return WorkItemCenter.take("整理背包");
            default:
                return null;
            }
        },

        _repeatTotal: 1,
        _repeatCounter: 0,
    };

    var Wudaota2 = {
        run: function() {
            Workflow.name = "自动扫荡武道塔";
            var items = [];
            items.push(WorkItemCenter.take("清理背包"));
            items.push(Wudaota2._reset);

            items.push(Wudaota2._fastFight);
            items.push(WorkItemCenter.take("通用结束动作"));
            Workflow.items = items;

            Workflow.start();
        },

        _reset: new WorkItem("重置武道塔", function() {
            let cmds = [
                "jh fam 0 start;jh fam 8 start",
                "@reset",
            ];
            let item = this;
            let didFinishExecute = function() {
                item.didFinish();
            };
            let willPerformCmd = function(lastCmd, cmd) {
                if (cmd == "@reset") {
                    let butler = Role.findItem("守护人");
                    return "select " + butler + ";ask1 " + butler;
                }
                return cmd;
            };
            let executer = new CmdExecuter(cmds, willStartExecute, didFinishExecute, willPerformCmd);
            executer.execute();
        }),
        _fight: new WorkItem("挑战守护者", function() {

        }),
        _fastFight: new WorkItem("快速挑战守护者(扫荡符)", function() {

        }),
    };

    /* ------------------------ UI ------------------------ *\
    \* ---------------------------------------------------- */

    let HighlightedRaids = ["移花宫(简单)", "移花宫(困难)", "华山论剑"];

    var MoreRaid = new Vue({
        el: '#MoreRaid',
        data: {
            items: [],
        },
        methods: {
            getItems: function() {
                var result = [{name: "< 返回"}];
                for (var i = 0; i < ParsedDungeonConfig.length; i++) {
                    let config = ParsedDungeonConfig[i];
                    if (HighlightedRaids.indexOf(config.name) != -1) continue;
                    result.push(config);
                }
                return result;
            },
            createSpan: function(createElement, item) {
                return createElement(
                    'span',
                    {
                        attrs: { class: "zdy-item" },
                        style: { width: "120px" },
                        on: {
                            click: function() {
                                if (item.name == "< 返回") {
                                    UI.home();
                                } else {
                                    Raid.repeatRun(item.name);
                                }
                            },
                        },
                    },
                    item.name,
                );
            },
        },
        render: function(createElement, item) {
            let theSelf = this;
            var subNodes = [
                createElement("br"),
                "⛩ ",
                createElement("hic", ["更多副本"]),
                createElement("br"),
                createElement("br"),
            ];
            let spans = this.items.map(function(item) {
                return theSelf.createSpan(createElement, item);
            });
            subNodes.push(spans);
            return createElement(
                "div",
                {
                    attrs: { class: "item-commands" },
                    style: { "text-align": "center" },
                },
                subNodes,
            );
        }
    });
    MoreRaid.items = MoreRaid.getItems();

    var UI = {
        home: function() {
            messageClear();
            var html = `
            <div class = "item-commands" style="text-align:center">
                </br>🏮 <ord>恭祝 2019(己亥年) 新春大吉、万事如意!</ord>🏮</br></br>
                <span class = "zdy-item yihua" style="width:120px"> 移花宫(简单) </span>
                <span class = "zdy-item yihuaH" style="width:120px"> 移花宫(困难) </span>
                <span class = "zdy-item lunjian" style="width:120px"> 华山论剑(简单) </span>
                <span class = "zdy-item raidSetting" style="width:120px"> ⚙ 副本设置 </span>
                <span class = "zdy-item wudaota" style="width:120px"> 🏯 <hio>武道塔</hio> </span>
                <span class = "zdy-item xiangyang" style="width:120px"> ⚔ <hiy>守卫襄阳</hiy> </span>
                <span class = "zdy-item shortcut" style="width:120px"> 🚀 <hig>捷径</hig> </span>
                <span class = "zdy-item moreRaid" style="width:120px"> ⛩ <hic>更多副本</hic> </span>
                <div>
                    </br><a href="https://gf.qytechs.cn/zh-CN/scripts/375851-wsmud-raid/feedback" style="font-size:1em">(版本: ${GM_info.script.version})</a>
                </div>
            </div>`;
            messageAppend(html);

            $(".yihua").on('click',function(){
                Raid.repeatRun("移花宫(简单)");
            });
            $(".yihuaH").on('click', function () {
                Raid.repeatRun("移花宫(困难)");
            });
            $(".lunjian").on('click', function () {
                Raid.repeatRun("华山论剑");
            });
            $(".raidSetting").on('click', function () {
                UI.setting();
            });
            $(".wudaota").on('click', function () {
                UI.wudaota();
            });
            $(".xiangyang").on('click', function () {
                UI.xiangyang();
            });
            $(".shortcut").on('click', function () {
                UI.shortcut();
            });
            $(".moreRaid").on('click', function () {
                UI.moreRaid();
            });
        },
        setting: function() {
            messageClear();
            var html = `
            <div class = "item-commands" style="text-align:center">
                </br>⚙ 副本设置</br></br>
                <span style='border:solid 0px gray'>
                    <label for="liaoshangInRaid">◆ 副本内疗伤,当气血低于: </label><select style='width:80px' id="liaoshangInRaid">
                        <option value="100">100%</option>
                        <option value="90">90%</option>
                        <option value="80">80%</option>
                        <option value="70">70%</option>
                        <option value="60">60%</option>
                        <option value="50">50%</option>
                        <option value="40">40%</option>
                        <option value="30">30%</option>
                        <option value="20">20%</option>
                        <option value="10">10%</option>
                    </select>
                </span>
                <span style='border:solid 0px gray'>
                    <label for="waitSkillCD">◆ Boss战前等待技能冷却: </label><select style='width:80px' id = "waitSkillCD">
                        <option value="no">关闭</option>
                        <option value="yes">开启</option>
                    </select>
                </span>
                <span style='border:solid 0px gray'>
                    <label for="bagCleanWay">◆ 背包清理方案: </label><select style='width:80px' id = "bagCleanWay">
                        <option value="none">不清理</option>
                        <option value="clean">售卖</option>
                        <option value="tidy">存仓&售卖</option>
                    </select>
                </span>
                <span style='border:solid 0px gray'>
                    <label for="cmdInterval">◆ 命令间隔时间: </label><select style='width:80px' id = "cmdInterval">
                        <option value=1000>1秒(推荐)</option>
                        <option value=2000>2秒</option>
                        <option value=3000>3秒</option>
                        <option value=4000>4秒</option>
                    </select>
                </span>
                <!--
                <span style='border:solid 0px gray'>
                    <label for="autoArm">自动换装备&技能: </label><select style='width:80px' id = "autoArm">
                        <option value="no">关闭</option>
                        <option value="yes">开启</option>
                    </select><button>更新为当前</button>
                </span>
                -->
                </br>
                <span class = "zdy-item settingBack" style="width:120px"> < 返回 </span>
            </div>`;
            messageAppend(html);

            $('#liaoshangInRaid').val(Config.hpThresholdInDungeon());
            $("#liaoshangInRaid").change(function () {
                Config.setHpThresholdInDungeon($("#liaoshangInRaid").val());
            });
            $('#waitSkillCD').val(Config.waitSkillCD());
            $("#waitSkillCD").change(function () {
                Config.setWaitSkillCD($("#waitSkillCD").val());
            });
            $('#bagCleanWay').val(Config.bagCleanWay());
            $("#bagCleanWay").change(function () {
                Config.setBagCleanWay($("#bagCleanWay").val());
            });
            $('#cmdInterval').val(Config.cmdInterval());
            $("#cmdInterval").change(function () {
                Config.setCmdInterval($("#cmdInterval").val());
            });

            $(".settingBack").on('click', function () {
                UI.home();
            });
        },
        wudaota: function() {
            messageClear();
            var html = `
            <div class = "item-commands" style="text-align:center">
                </br>🏯 <hio>武道塔</hio></br></br>
                <!--
                <span style='border:solid 0px gray;width:100%'>
                    <label>◆ 自动战斗到第 </label><input type="number" id="wudaotaAutoToFloor" style="text-align:center;width:60px"><label> 层,</label><label for="wudaotaFastCombatOpening">剩余层扫荡符处理 </label><select style='width:60px' id = "wudaotaFastCombatOpening">
                        <option value="no">关闭</option>
                        <option value="yes">开启</option>
                    </select><label>。</label>
                </span>
                <span style='border:solid 0px gray;width:100%'>
                    <label for="wudaotaHpThreshold">◆ 疗伤,当气血小于 </label><select style='width:60px' id="wudaotaHpThreshold">
                        <option value="100">100%</option>
                        <option value="90">90%</option>
                        <option value="80">80%</option>
                        <option value="70">70%</option>
                        <option value="60">60%</option>
                        <option value="50">50%</option>
                        <option value="40">40%</option>
                        <option value="30">30%</option>
                        <option value="20">20%</option>
                        <option value="10">10%</option>
                    </select><label>;从第 </label><input type="number" id="wudaotaWaitSkillCDFrom" style="text-align:center;width:60px"><label for="waitSkillCD"> 层开始,战前等待技能冷却。</label>
                </span>
                -->
                即将开放...
                </br>
                <span class = "zdy-item wudaoBack" style="width:120px"> < 返回 </span>
                <!--<span class = "zdy-item wudaoStart" style="width:120px"> 开始爬塔 </span>-->
            </div>`;
            messageAppend(html);

            $('#wudaotaAutoToFloor').val(Config.wudaota.autoToFloor());
            $('#wudaotaAutoToFloor').focusout(function () {
                let autoToFloor = $('#wudaotaAutoToFloor').val();
                if (autoToFloor >= 0 && autoToFloor <= 100) {
                    Config.wudaota.setAutoToFloor(autoToFloor);
                } else {
                    $('#wudaotaAutoToFloor').val(Config.wudaota.autoToFloor());
                }
            });
            $('#wudaotaFastCombatOpening').val(Config.wudaota.fastCombatOpening());
            $("#wudaotaFastCombatOpening").change(function () {
                Config.wudaota.setFastCombatOpening($("#wudaotaFastCombatOpening").val());
            });
            $('#wudaotaHpThreshold').val(Config.wudaota.hpThresholdInRaid());
            $("#wudaotaHpThreshold").change(function () {
                Config.wudaota.setHpThresholdInRaid($("#wudaotaHpThreshold").val());
            });
            $('#wudaotaWaitSkillCDFrom').val(Config.wudaota.waitSkillCDFrom());
            $('#wudaotaWaitSkillCDFrom').focusout(function () {
                let from = $('#wudaotaWaitSkillCDFrom').val();
                if (from >= 0 && from <= 100) {
                    Config.wudaota.setWaitSkillCDFrom(from);
                } else {
                    $('#wudaotaWaitSkillCDFrom').val(Config.wudaota.waitSkillCDFrom());
                }
            });

            $(".wudaoBack").on('click', function () {
                UI.home();
            });
            $(".wudaoStart").on('click', function () {
                Wudaota.run();
            });
        },
        xiangyang: function() {
            messageClear();
            var html = `
            <div class = "item-commands" style="text-align:center">
                </br>⚔ <hiy>守卫襄阳</hiy></br></br>
                <span class = "zdy-item xiangyangBack" style="width:120px"> < 返回 </span>
                <span class = "zdy-item xiangyangStart" style="width:120px"> <hiy>提开发建议=></hiy> </span>
            </div>`;
            messageAppend(html);

            $(".xiangyangBack").on('click', function () {
                UI.home();
            });
            $(".xiangyangStart").on('click', function () {
                window.open("https://gf.qytechs.cn/zh-CN/forum/discussion/48858/%E5%AE%88%E5%8D%AB%E8%A5%84%E9%98%B3%E5%BC%80%E5%8F%91%E5%BB%BA%E8%AE%AE/p1?new=1", '_blank').location;
            });
        },
        moreRaid: function() {
            messageClear();

            let wg_log = document.getElementsByClassName("WG_log")[0];
            let pre = wg_log.getElementsByTagName("pre")[0];
            pre.appendChild(MoreRaid.$el);
        },
        shortcut: function() {
            messageClear();
            var html = `
            <div class = "item-commands" style="text-align:center">
                </br>🚀 <hig>捷径</hig></br></br>
                <span class = "zdy-item shortcutBack" style="width:120px"> < 返回 </span>
                <span class = "zdy-item outMaze" style="width:120px"> 走出桃花林 </span>
                <span class = "zdy-item zhoubotong" style="width:120px"> 找到周伯通 </span>
            </div>`;
            messageAppend(html);

            $(".shortcutBack").on('click', function () {
                UI.home();
            });
            $(".outMaze").on('click', function () {
                WG.Send('stopstate');
                TaohuaIsland.outMaze();
            });
            $(".zhoubotong").on('click', function () {
                WG.Send('stopstate');
                TaohuaIsland.zhoubotong();
            });
        }
    };

    var ToRaid = { menu :UI.home };

    var TaohuaIsland = {
        outMaze: function() {
            if (!Role.atPath("taohua/haitan")) {
                messageAppend("只有在 桃花岛的海滩 才能使用此虫洞。");
                return;
            }

            let cmds = [
                "go south",
                "@look 1",
                "@look 5"
            ];
            let willStartExecute = function() {
                TaohuaIsland._monitorMaze();
            };
            let willPerformCmd = function(lastCmd, cmd) {
                if (cmd == "@look 1") {
                    if (TaohuaIsland._goCenterCmd) {
                       return TaohuaIsland._goCenterCmd;
                    } else {
                        return null;
                    }
                }
                if (cmd == "@look 5") {
                    if (TaohuaIsland._decodedMaze) {
                        return TaohuaIsland._outMazeCmd();
                    } else {
                        return null;
                    }
                }
                return cmd;
            };
            let executer = new CmdExecuter(
                cmds,
                willStartExecute,
                TaohuaIsland._cancelMonitorMaze,
                willPerformCmd,
                undefined,
                1000
            );
            executer.execute();
        },
        zhoubotong: function() {
            if (!Role.atPath("taohua/wofang")) {
                messageAppend("只有在 蓉儿的卧室 才能使用此虫洞。");
                return;
            }

            let cmds = [
                "go south;go west;go west;go west;go north;go north;go north",
                "go west;go east;go west;go east;go west",
                "go south",
                "@look 1",
                "@look 5",
                "@go 2",
                "@go 3",
                "@go 4",
                "@go 6",
                "@go 7",
                "@go 8",
            ];
            let willStartExecute = function() {
                TaohuaIsland._monitorMaze();
                TaohuaIsland._exitsHookIndex = WG.add_hook("exits", function(data) {
                    if (TaohuaIsland._lastCoord == undefined || TaohuaIsland._lastCoord == [0, 0]) return;
                    if (Object.keys(data.items).length != 4) return;
                    for(var key in data.items) {
                        if (data.items[key] != "桃花林") return;
                    }
                    let normalExistMap = [
                        [["north", "northeast", "east"], ["east", "north", "south"], ["east", "south", "southeast"],],
                        [["east", "north", "west"], [], ["west", "east", "south"],],
                        [["west", "northwest", "north"], ["west", "south", "north"], ["west", "southwest", "south"],]
                    ];
                    let x = TaohuaIsland._lastCoord[0] + 1;
                    let y = TaohuaIsland._lastCoord[1] + 1;
                    let normalExists = normalExistMap[x][y];
                    for(var key2 in data.items) {
                        if (normalExists.indexOf(key2) != -1) continue;
                        TaohuaIsland._goCave = "go " + key2;
                        return;
                    }
                });
            };
            let didFinishExecute = function() {
                TaohuaIsland._lastCoord = undefined;
                TaohuaIsland._lastGo = undefined;
                TaohuaIsland._goCave = undefined;
                TaohuaIsland._cancelMonitorMaze();
                WG.remove_hook(TaohuaIsland._exitsHookIndex);
            };
            let willPerformCmd = function(lastCmd, cmd) {
                if (TaohuaIsland._goCave) return TaohuaIsland._goCave + ";go west;[exit]";

                var number = 0;
                switch (cmd) {
                case "@look 1":
                    if (TaohuaIsland._goCenterCmd) {
                       return TaohuaIsland._goCenterCmd;
                    } else {
                        return null;
                    }
                    break;
                case "@look 5":
                    if (!TaohuaIsland._decodedMaze) return null;
                    break;
                case "@go 2":
                    TaohuaIsland._lastCoord = TaohuaIsland._mazeCoords[2];
                    TaohuaIsland._lastGo = TaohuaIsland._mazePath(TaohuaIsland._lastCoord);
                    return TaohuaIsland._lastGo;
                case "@go 3": number = 3; break;
                case "@go 4": number = 4; break;
                case "@go 6": number = 6; break;
                case "@go 7": number = 7; break;
                case "@go 8": number = 8; break;
                }
                if (number != 0) {
                    let back = TaohuaIsland._mazeBackPath(TaohuaIsland._lastGo);
                    TaohuaIsland._lastCoord = TaohuaIsland._mazeCoords[number];
                    TaohuaIsland._lastGo = TaohuaIsland._mazePath(TaohuaIsland._lastCoord);
                    return back + ";" + TaohuaIsland._lastGo;
                }
                return cmd;
            };
            let executer = new CmdExecuter(
                cmds,
                willStartExecute,
                didFinishExecute,
                willPerformCmd,
                undefined,
                1000
            );
            executer.execute();
        },

        _outMazeCmd: function() {
            var cmd = "";
            for (var i = 2; i <= 9; i++) {
                let coord = TaohuaIsland._mazeCoords[i];
                let go = TaohuaIsland._mazePath(coord);
                if (i == 9) {
                    cmd += go + ";" + go;
                } else {
                    cmd += go + ";" + TaohuaIsland._mazeBackPath(go) + ";";
                }
            }
            cmd += ";go south";
            return cmd;
        },
        _mazePath: function(coord) {
            let pathMap = [
                ["go southwest", "go west", "go northwest"],
                ["go south", "", "go north"],
                ["go southeast", "go east", "go northeast"]
            ];
            let x = coord[0] + 1;
            let y = coord[1] + 1;
            return pathMap[x][y];
        },
        _mazeBackPath: function(path) {
            let backMap = {
                "": "",
                "go southwest": "go northeast",
                "go west": "go east",
                "go northwest": "go southeast",
                "go south": "go north",
                "go north": "go south",
                "go southeast": "go northwest",
                "go east": "go west",
                "go northeast": "go southwest"
            };
            return backMap[path];
        },
        _monitorMaze: function() {
            TaohuaIsland._mazeCoords = [
                [2, 2], // unused
                [2, 2],
                [2, 2],
                [2, 2],
                [2, 2],
                [0, 0],
                [2, 2],
                [2, 2],
                [2, 2],
                [2, 2]
            ];
            TaohuaIsland._atFirst = false;
            TaohuaIsland._goCenterCmd = undefined;
            TaohuaIsland._decodedMaze = false;

            let index1 = WG.add_hook(["room", "exits"], function(data) {
                if (TaohuaIsland._goCenterCmd != undefined) return;

                if (data.type == "room") {
                    if (data.desc == undefined) return;
                    let patt = new RegExp("四周栽了大概有一棵桃树");
                    let result = patt.exec(data.desc);
                    if (result) TaohuaIsland._atFirst = true;
                } else if (data.type == "exits") {
                    if (data.items == undefined) return;
                    if (TaohuaIsland._atFirst) {
                        if (data.items.north && data.items.south) {
                            if (data.items.west) {
                                TaohuaIsland._mazeCoords[1] = [1, 0];
                                TaohuaIsland._goCenterCmd = "go west"
                            } else {
                                TaohuaIsland._mazeCoords[1] = [-1, 0];
                                TaohuaIsland._goCenterCmd = "go east"
                            }
                        } else if (data.items.west && data.items.east) {
                            if (data.items.north) {
                                TaohuaIsland._mazeCoords[1] = [0, -1];
                                TaohuaIsland._goCenterCmd = "go north"
                            } else {
                                TaohuaIsland._mazeCoords[1] = [0, 1];
                                TaohuaIsland._goCenterCmd = "go south"
                            }
                        }
                    }
                }
            });
            let index2 = WG.add_hook("room", function(data) {
                if (TaohuaIsland._decodedMaze) return;

                if (data.desc == undefined) return;
                let patt = new RegExp("能看到东南方向大概有.(?=棵桃树)");
                let count = patt.exec(data.desc);
                if (!count) return;
                let text = count.toString();
                switch (text.substring(text.length - 1)) {
                    case "二": TaohuaIsland._mazeCoords[2] = [1, -1]; break;
                    case "四": TaohuaIsland._mazeCoords[4] = [1, -1]; break;
                    case "六": TaohuaIsland._mazeCoords[6] = [1, -1]; break;
                    case "八": TaohuaIsland._mazeCoords[8] = [1, -1]; break;
                }

                TaohuaIsland._mazeCoords[9] = [-TaohuaIsland._mazeCoords[1][0], -TaohuaIsland._mazeCoords[1][1]];
                while (true) {
                    if (TaohuaIsland._mazeCoords[2][0] != 2) {
                        TaohuaIsland._mazeCoords[8] = [-TaohuaIsland._mazeCoords[2][0], -TaohuaIsland._mazeCoords[2][1]];
                    }
                    if (TaohuaIsland._mazeCoords[8][0] != 2) {
                        if (TaohuaIsland._mazeCoords[8][0] == TaohuaIsland._mazeCoords[1][0]) {
                            TaohuaIsland._mazeCoords[6] = [TaohuaIsland._mazeCoords[8][0], -TaohuaIsland._mazeCoords[8][1]];
                        } else {
                            TaohuaIsland._mazeCoords[6] = [-TaohuaIsland._mazeCoords[8][0], TaohuaIsland._mazeCoords[8][1]];
                        }
                    }
                    if (TaohuaIsland._mazeCoords[6][0] != 2) {
                        TaohuaIsland._mazeCoords[4] = [-TaohuaIsland._mazeCoords[6][0], -TaohuaIsland._mazeCoords[6][1]];
                    }
                    if (TaohuaIsland._mazeCoords[4][0] != 2) {
                        if (TaohuaIsland._mazeCoords[4][0] == TaohuaIsland._mazeCoords[9][0]) {
                            TaohuaIsland._mazeCoords[2] = [TaohuaIsland._mazeCoords[4][0], -TaohuaIsland._mazeCoords[4][1]];
                        } else {
                            TaohuaIsland._mazeCoords[2] = [-TaohuaIsland._mazeCoords[4][0], TaohuaIsland._mazeCoords[4][1]];
                        }
                    }
                    if (TaohuaIsland._mazeCoords[2][0] != 2
                        && TaohuaIsland._mazeCoords[4][0] != 2
                        && TaohuaIsland._mazeCoords[6][0] != 2
                        && TaohuaIsland._mazeCoords[8][0] != 2) {
                        break;
                    }
                }
                if (TaohuaIsland._mazeCoords[8][0] == TaohuaIsland._mazeCoords[4][0]) {
                    TaohuaIsland._mazeCoords[3] = [TaohuaIsland._mazeCoords[8][0], 0];
                } else {
                    TaohuaIsland._mazeCoords[3] = [0, TaohuaIsland._mazeCoords[8][1]];
                }
                TaohuaIsland._mazeCoords[7] = [-TaohuaIsland._mazeCoords[3][0], -TaohuaIsland._mazeCoords[3][1]];

                TaohuaIsland._decodedMaze = true;
            });
            TaohuaIsland._mazeHookIndexes = [index1, index2];
        },
        _cancelMonitorMaze: function() {
            for (var i = TaohuaIsland._mazeHookIndexes.length - 1; i >= 0; i--) {
                let index = TaohuaIsland._mazeHookIndexes[i];
                WG.remove_hook(index);
            }
        },
    };

    let ChineseToNumber = {
        run: function(text) {
            var rtn = 0;
            var section = 0;
            var number = 0;
            var secUnit = false;
            var str = text.split('');

            for (var i = 0; i < str.length; i++) {
                var num = ChineseToNumber.chnNumChar[str[i]];
                if (typeof num !== 'undefined') {
                    number = num;
                    if (i === str.length - 1){
                        section += number;
                    }
                }else{
                    var unit = ChineseToNumber.chnNameValue[str[i]].value;
                    secUnit = ChineseToNumber.chnNameValue[str[i]].secUnit;
                    if (secUnit){
                        section = (section + number) * unit;
                        rtn += section;
                        section = 0;
                    } else {
                        section += (number * unit);
                    }
                    number = 0;
                }
            }
            return rtn + section;
        },
        chnNumChar: {
            零:0,
            一:1,
            二:2,
            三:3,
            四:4,
            五:5,
            六:6,
            七:7,
            八:8,
            九:9
        },
        chnNameValue: {
            十:{value:10, secUnit:false},
            百:{value:100, secUnit:false},
            千:{value:1000, secUnit:false},
            万:{value:10000, secUnit:true},
            亿:{value:100000000, secUnit:true}
        }
    };

    $(document).ready(function () {
        WG = unsafeWindow.WG;
        messageAppend  = unsafeWindow.messageAppend;
        messageClear =  unsafeWindow.messageClear;
        unsafeWindow.ToRaid = ToRaid;
        unsafeWindow.Role = Role;
        Role.init();
    });
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址