wsmud_Raid

武神传说 MUD

As of 2018-12-26. See the latest version.

// ==UserScript==
// @name         wsmud_Raid
// @namespace    cqv
// @version      0.2.1
// @date        23/12/2018
// @modified     25/12/2018
// @homepage     https://greasyfork.org/zh-CN/scripts/375851
// @description  武神传说 MUD
// @author       Bob.cn
// @match        http://game.wsmud.com/*
// @match        http://www.wsmud.com/*
// @run-at       document-end
// @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,

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

        status: [],

        equipments: [],

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

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

        atPath: function(p) {
            return p == Role._roomPath;
        },
        atRoom: function(n) {
            return n == Role._roomName;
        },

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

            switch (Role._renewStatus) {
            case "resting":
                messageAppend("正在为角色回复状态...");

                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);
        },

        clearBag: function(callback) {
            Role._clearBag(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;
        },

        _renewHookIndex: undefined,
        _renewStatus: "resting",

        _coolingSkills: [],

        _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;
                        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;
            });
        },

        _clearBag: 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._clearBag(counter + 1, callback); }, 1000);
        },
    };

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

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

    var Executer = {

        interval: undefined,
        cmds: [],

        willExecuteCmd: undefined,      // optional: function(lastCmd, cmd)
        didExecuteCmd: undefined,       // optional: function(cmd)

        didFinishExecute: undefined,    // optional: function()

        isWorking: false,

        execute: function() {
            if (Executer.isWorking) return false;
            Executer.isWorking = true;

            if (Executer.interval == undefined) Executer.interval = Executer._defaultInterval;

            Executer._executeCmd(0);
            return true;
        },

        _defaultInterval: 2000,

        _executeCmd: function(index) {
            if (index >= Executer.cmds.length) {
                Executer._finish();
                return;
            }

            if (!Role.isFree()) { Executer._delayExecuteCmd(index); return; }

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

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

            // [exit] 保留虚命令,立即退出执行器
            if (cmd.indexOf("[exit]") != -1) {
                Executer._finish();
                return;
            } else {
                Executer._delayExecuteCmd(index + 1);
            }
        },
        _delayExecuteCmd: function(index) {
            window.setTimeout(function() { Executer._executeCmd(index); }, Executer.interval);
        },

        _finish() {
            Executer.interval = Executer._defaultInterval;
            Executer.cmds = [];
            Executer.willExecuteCmd = undefined;
            Executer.didExecuteCmd = undefined;
            let finish = Executer.didFinishExecute;
            Executer.didFinishExecute = undefined;
            Executer.isWorking = false;
            if (finish) finish();
        },
    };

    var Raid = {

        /* public */

        name: "副本名称",
        cmds: [],
        enemyNames: [],

        willStartRun: undefined,        // optional: function()
        didFinishRun: undefined,           // optional: function()

        willStartOnceRun: undefined,    // optional: function(number)
        didEndOnceRun: undefined,       // optional: function(number)

        willExecuteCmd: undefined,      // optional: function(lastCmd, cmd)
        didExecuteCmd: undefined,       // optional: function(cmd)

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

            messageClear();
            messageAppend("正在运行自动 " + Raid.name + "...");
            messageAppend("* 自动副本期间会暂时关闭 自动Boss 和 自动婚宴。");
            messageAppend("* 如需要立即中断,请刷新网页。");

            WG.stopAllAuto();

            Raid._cleanForRepeatRun();

            Raid._indexes.push(Raid._monitorEnemy());
            Raid._indexes.push(Raid._monitorAddEnemy());
            Raid._indexes.push(Raid._monitorEnemyDie());

            if (Raid.willStartRun) { Raid.willStartRun(); }

            Raid._onceRun();
        },

        /* private */

        _indexes: [],
        _repeatTotal: 1,
        _repeatCounter: 0,

        _liaoshangDoing: false,

        _waitingSkillCD: false,

        _noneEnemy: false,

        _enemyIds: undefined,    // 当前房间的敌人id
        _enemyCounter: 0,        // 当前房间剩余的敌人

        _onceRun: function() {
            Raid._cleanForOnceRun();

            messageAppend("正在第 " + (Raid._repeatCounter + 1) + "/"+ Raid._repeatTotal +" 次运行自动 " + Raid.name + "...");

            let clearBagCallback = function() {
                window.setTimeout(function() {
                    messageAppend("元气满满,进入副本...");

                    if (Raid.willStartOnceRun) { Raid.willStartOnceRun(Raid._repeatCounter); }

                    var totalCmds = Raid.cmds.slice(); totalCmds.push("cr;cr over");
                    Executer.cmds = totalCmds;
                    console.log(Executer.cmds);
                    Executer.willExecuteCmd = function(lastCmd, cmd) {
                        return Raid._willExecuteCmd(lastCmd, cmd);
                    };
                    Executer.didExecuteCmd = Raid.didExecuteCmd;
                    Executer.didFinishExecute = Raid._finishOnceRun;
                    Executer.execute();
                }, 1000);
            };
            let renewCallback = function() {
                window.setTimeout(function() {
                    Role.clearBag(clearBagCallback);
                }, 1000);
            };
            Role.renew(renewCallback);
        },
        _cleanForRepeatRun: function() {
            for (var i = Raid._indexes.length - 1; i >= 0; i--) {
                let index = Raid._indexes[i];
                WG.remove_hook(index);
            }
            Raid._indexes = [];
            WG.reSetAllAuto();
        },
        _cleanForOnceRun: function() {
            Raid._enemyIds = undefined;
            Raid._enemyCounter = 0;
            Raid._liaoshangDoing = false;
            Raid._waitingSkillCD = false;
            Raid._noneEnemy = false;
        },
        _finishOnceRun: function() {
            if (Raid.didEndOnceRun) { Raid.didEndOnceRun(Raid._repeatCounter); }
            messageClear();
            messageAppend("已完成第 " + (Raid._repeatCounter + 1) + "/"+ Raid._repeatTotal + " 次自动 " + Raid.name);

            Raid._repeatCounter += 1;
            if (Raid._repeatCounter < Raid._repeatTotal) {
                window.setTimeout(Raid._onceRun, 2000);
            } else {
                Raid._cleanForRepeatRun();

                if (Raid.didFinishRun) { Raid.didFinishRun(); }

                window.setTimeout(function() {
                    WG.go("练功房");
                    WG.Send("stopstate;dazuo");
                    messageAppend("已经全部完成自动 " + Raid.name + "。");
                }, 1000);
            }
        },

        _willExecuteCmd: function(lastCmd, cmd) {
            if (Raid._enemyCounter > 0) {
                Raid._killEnemy();
                return null;
            }

            if (Raid._liaoshangDoing) {
                if (Role.hp/Role.maxHp < 0.99) {
                    return null;
                } else {
                    Raid._liaoshangDoing = false;
                    WG.Send("stopstate");
                }
            }
            if (Role.hp/Role.maxHp < Config.hpThresholdInRaid()/100 && !Raid._noneEnemy) {
                WG.Send("liaoshang");
                Raid._liaoshangDoing = true;
                return null;
            }

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

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

            if (Raid.willExecuteCmd) cmd = Raid.willExecuteCmd(lastCmd, cmd);
            return cmd;
        },
        
        _killEnemy: function() {
            if (Raid._enemyIds == undefined) { return }
            for (var i = 0; i < Raid._enemyIds.length; i++) {
                let enemyId = Raid._enemyIds[i];
                WG.Send("kill " + enemyId);
            }
        },

        _monitorEnemy: function() {
            let index = WG.add_hook("items", function(data) {
                if (data.items == undefined) return;
                var enemyIds = [];
                for (var i = 0; i < data.items.length; i++) {
                    let item = data.items[i];
                    if (item.id == undefined || item.name == undefined) continue;
                    if (Raid.enemyNames.indexOf(item.name) >= 0) {
                        enemyIds.push(item.id);
                    }
                }
                Raid._enemyIds = enemyIds;
                Raid._enemyCounter = enemyIds.length;
            });
            return index;
        },
        _monitorAddEnemy: function() {
            let index = WG.add_hook("itemadd", function(data) {
                if (data.name == undefined) return;
                if (Raid.enemyNames.indexOf(data.name) == -1) return;
                if (Raid._enemyIds) {
                    Raid._enemyIds.push(data.id);
                    Raid._enemyCounter += 1;
                } else {
                    Raid._enemyIds = [data.id];
                    Raid._enemyCounter = 1;
                }
            });
            return index;
        },
        _monitorEnemyDie: function() {
            let index = WG.add_hook("sc", function(data) {
                if (data.id == undefined || !Raid._enemyIds) return;
                if (WG.inArray(data.id, Raid._enemyIds) && data.hp == 0) {
                    Raid._enemyCounter -= 1;
                }
            });
            return index;
        },
    };

    var ToRaid = {
        menu :function(){
            messageClear();
            var html = `
            <div style='text-align:center;width:100%'>
                ⛑ 欢迎前往 <a href="https://greasyfork.org/zh-CN/scripts/375851-wsmud-raid/feedback">Greasy Fork</a> 反馈意见和建议。(版本: ${GM_info.script.version})
                <div class = "item-commands" >
                <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>
                </div>
                <div class = "item-commands" >
                    <span class = "zdy-item yihua" style="width:120px"> 移花宫(简单) </span>
                    <span class = "zdy-item yihuaH" style="width:120px"> 移花宫(困难) </span>
                    <span class = "zdy-item whiteteam" style="width:120px"> 白驼山(组队) </span>
                    <span class = "zdy-item yanziwu" style="width:120px"> 燕子坞(简单) </span>
                    <span class = "zdy-item yanziwuH" style="width:120px"> 燕子坞(困难) </span>
                    <span class = "zdy-item lunjian" style="width:120px"> 华山论剑<hig>(测试)</hig> </span>
                    <span class = "zdy-item outMaze" style="width:120px"> 走出桃花林 </span>
                    <span class = "zdy-item zhoubotong" style="width:120px"> 找到周伯通 </span>
                    <!--<span class = "zdy-item test" style="width:120px"> 测试专用 </span>-->
                </div>
            </div> `;
            messageAppend(html);

            $('#liaoshangInRaid').val(Config.hpThresholdInRaid());
            $("#liaoshangInRaid").change(function () {
                Config.setHpThresholdInRaid($("#liaoshangInRaid").val());
            });
            $('#waitSkillCD').val(Config.waitSkillCD());
            $("#waitSkillCD").change(function () {
                Config.setWaitSkillCD($("#waitSkillCD").val());
            });

            $(".yihua").on('click',function(){
                WG.Send('stopstate');
                ToRaid.yihua(false);
            });
            $(".yihuaH").on('click', function () {
                WG.Send('stopstate');
                ToRaid.yihua(true);
            });
            $(".whiteteam").on('click', function () {
                WG.Send('stopstate');
                ToRaid.baituo();
            });
            $(".yanziwu").on('click',function(){
                WG.Send('stopstate');
                ToRaid.yanziwu(false);
            });
            $(".yanziwuH").on('click', function () {
                WG.Send('stopstate');
                ToRaid.yanziwu(true);
            });
            $(".lunjian").on('click', function () {
                WG.Send('stopstate');
                ToRaid.huashanlunjian();
            });
            $(".outMaze").on('click', function () {
                WG.Send('stopstate');
                TaohuaIsland.outMaze();
            });
            $(".zhoubotong").on('click', function () {
                WG.Send('stopstate');
                TaohuaIsland.zhoubotong();
            });
            $(".test").on('click', function () {
                // Role.renew(function() {
                //     alert("Hello, Bob!");
                // });
                console.log(Role._coolingSkills);
                console.log(Role.hasCoolingSkill());
            });
        },
        yihua: function(hard) {
            if (hard) {
                Raid.name = "移花宫(困难)";
                Raid.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",
                    "go south;go south",
                    "go south",
                    "#go southeast",
                    "go northwest;go southwest",
                    "look hua",
                    "go southeast",
                    "look bed",
                    "go down",
                    "fire;go west$",
                    "look xia;open xia"
                ];
            } else {
                Raid.name = "移花宫(简单)";
                Raid.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",
                    "go south;go south",
                    "go south",
                    "#go southeast",
                    "#go northwest;go southwest",
                    "look hua",
                    "go southeast",
                    "look bed",
                    "go down",
                    "fire;go west$",
                    "look xia;open xia"
                ];
            }
            Raid.enemyNames = [
                "花月奴",
                "移花宫女弟子",
                "移花宫二宫主 涟星",
                "移花宫大宫主 邀月",
                "移花宫少宫主 花无缺"
            ];
            Raid.willStartOnceRun = function(number) {
                ToRaid.leftPush = 0;
                ToRaid.didFindSecretPath = false;
            };
            Raid.willStartRun = 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];
            };
            Raid.didFinishRun = function() {
                for (var i = ToRaid.indexes.length - 1; i >= 0; i--) {
                    let index = ToRaid.indexes[i];
                    WG.remove_hook(index);
                }
            };
            Raid.willExecuteCmd = 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
            };
            Raid.repeatRun();
        },
        hardTaohua: function() {
            Raid.name = "桃花岛-困难";
            Raid.cmds = [
                "jh fb 18 start2;cr taohua/haitan 1 0",
                "go south",
                "@look 1",
                "@look 5",
                "go south;go south",
                "go east;go east",
                "#go east;go north$",
                "@wait"
            ];
            Raid.enemyNames = [
                "桃花岛四弟子 陆乘风",
                "桃花岛大弟子 曲灵风",
                "<hiy>东邪</hiy> 黄药师"
            ];
            Raid.willStartOnceRun = function(number) {
                ToRaid.mazeCoords = [
                    [2, 2],
                    [2, 2],
                    [2, 2],
                    [2, 2],
                    [2, 2],
                    [0, 0],
                    [2, 2],
                    [2, 2],
                    [2, 2],
                    [2, 2]
                ];
                ToRaid.foundFirst = false;
                ToRaid.goCenterCmd = undefined;
                ToRaid.paths = undefined;
            };
            Raid.willStartRun = function() {
                let index1 = WG.add_hook(["room", "exits"], function(data) {
                    if (ToRaid.foundFirst) return;

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

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

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

                    let pathMap = [
                        ["go southwest", "go west", "go northwest"],
                        ["go south", "", "go north"],
                        ["go southeast", "go east", "go northeast"]
                    ];
                    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"
                    };
                    var paths = "";
                    for (var i = 2; i <= 9; i++) {
                        let j = ToRaid.mazeCoords[i][0] + 1;
                        let k = ToRaid.mazeCoords[i][1] + 1;
                        let path = pathMap[j][k];
                        if (i == 9) {
                            paths += path + ";" + path;
                        } else {
                            paths += path + ";" + backMap[path] + ";";
                        }
                    }
                    ToRaid.paths = paths;
                });
                ToRaid.indexes = [index1, index2];
            };
            Raid.didFinishRun = function() {
                for (var i = ToRaid.indexes.length - 1; i >= 0; i--) {
                    let index = ToRaid.indexes[i];
                    WG.remove_hook(index);
                }
            };
            Raid.willExecuteCmd = function(lastCmd, cmd) {
                if (cmd == "@look 1") {
                    if (ToRaid.foundFirst) {
                       return ToRaid.goCenterCmd;
                    } else {
                        return null;
                    }
                }
                if (cmd == "@look 5") {
                    if (ToRaid.paths) {
                        return ToRaid.paths;
                    } else {
                        return null;
                    }
                }
                return cmd;
            };
            Raid.repeatRun();
        },
        baituo: function() {
            Raid.name = "白驼山(组队)";
            Raid.cmds = [
                "jh fb 19 start3;cr baituo/damen 2 0",
                "go north;go north;go north",
                "#go north",
                "go south;go south;go south;go west;go west;go west",
                "go north",
                "go north;go north$",
                "@wait",
            ];
            Raid.enemyNames = [
                "白驼山少庄主 欧阳克",
                "白衣少女",
                "<hiy>西毒</hiy> 欧阳锋",
                "毒蛇",
                "蟒蛇"
            ];
            Raid.repeatRun();
        },
        yanziwu: function(hard) {
            if (hard) {
                Raid.name = "燕子坞(困难)";
                Raid.cmds = ["jh fb 23 start2;cr murong/anbian 1 0"];
            } else {
                Raid.name = "燕子坞(简单)";
                Raid.cmds = ["jh fb 23 start1;cr murong/anbian"];
            }
            Raid.cmds.push(
                "go east;go east",
                "go east;go south;go east;go south;go south",
                "go north;go north;go west;go north",
                "#go east;go east;go east",
                "go west;go north",
                "look pai;bai pai;bai pai;bai pai",
                "go north",
                "search",
                "#go south$",
                "@wait",
            );
            Raid.enemyNames = [
                "金凤庄庄主 包不同",
                "曼佗罗山庄庄主 王夫人",
                "姑苏慕容公子 慕容复",
                "慕容博"
            ];
            Raid.repeatRun();
        },
        huashanlunjian: function() {
            Raid.name = "华山论剑(测试)";
            Raid.cmds = [
                "jh fb 30 start1;cr huashan/lunjian/leitaixia",
                "go up",
                "jump bi",
                "@get box",
            ];
            Raid.enemyNames = [
                "<hiy>东邪</hiy> 黄药师",
                "<hiy>南帝</hiy> 一灯大师",
                "<hiy>西毒</hiy> 欧阳锋",
                "<hiy>北丐</hiy> 洪七公",
                "<hiy>中神通</hiy> 王重阳"
            ];
            Raid.willStartOnceRun = function(number) {
                ToRaid.remainingBossCount = 5;
            };
            Raid.willStartRun = function() {
                let index = WG.add_hook("itemadd", function(data) {
                    let corpses = [
                        "<wht>黄药师的尸体</wht>",
                        "<wht>一灯大师的尸体</wht>",
                        "<wht>欧阳锋的尸体</wht>",
                        "<wht>洪七公的尸体</wht>",
                        "<wht>王重阳的尸体</wht>"
                    ];
                    if (data.name != undefined && corpses.indexOf(data.name) != -1) {
                        ToRaid.remainingBossCount -= 1;
                    }
                });
                ToRaid.indexes = [index];
            };
            Raid.didFinishRun = function() {
                for (var i = ToRaid.indexes.length - 1; i >= 0; i--) {
                    let index = ToRaid.indexes[i];
                    WG.remove_hook(index);
                }
            };
            Raid.willExecuteCmd = function(lastCmd, cmd) {
                if (cmd == "jump bi" && ToRaid.remainingBossCount != 0) return null;
                if (cmd == "@get box") WG.get_all();
                return cmd;
            };
            Raid.repeatRun();
        }
    };
    var TaohuaIsland = {
        outMaze: function() {
            if (!Role.atPath("taohua/haitan")) {
                messageAppend("只能在 桃花岛的海滩 才能使用此虫洞。");
                return;
            }

            Executer.interval = 1000;
            Executer.cmds = [
                "go south",
                "@look 1",
                "@look 5"
            ];
            Executer.willExecuteCmd = 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;
            };
            Executer.didFinishExecute = TaohuaIsland._cancelMonitorMaze;

            TaohuaIsland._monitorMaze();
            Executer.execute();
        },
        zhoubotong: function() {
            if (!Role.atPath("taohua/wofang")) {
                messageAppend("只能在 蓉儿的卧室 才能使用此虫洞。");
                return;
            }

            Executer.interval = 1000;
            Executer.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 index = 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;
                }
            });
            Executer.willExecuteCmd = 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;
            };
            Executer.didFinishExecute = function() {
                TaohuaIsland._lastCoord = undefined;
                TaohuaIsland._lastGo = undefined;
                TaohuaIsland._goCave = undefined;
                TaohuaIsland._cancelMonitorMaze();
            };

            TaohuaIsland._monitorMaze();
            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;
                switch (count.toString()) {
                    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);
            }
        },
    };
    $(document).ready(function () {
        WG = unsafeWindow.WG;
        messageAppend  = unsafeWindow.messageAppend;
        messageClear =  unsafeWindow.messageClear;
        unsafeWindow.ToRaid = ToRaid;
        unsafeWindow.Role = Role;
        Role.init();

    });
})();