// ==UserScript==
// @name wsmud_Raid
// @namespace cqv
// @version 0.4.1
// @date 23/12/2018
// @modified 28/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
// @downloadURL none
// ==/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();
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();
},
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) {
console.log(Role._itemsInRoom);
return Role._itemsInRoom[name];
},
_renewHookIndex: undefined,
_renewStatus: "resting",
_coolingSkills: [],
_itemsInRoom: {},
_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"], 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[item.name] = item.id;
}
break;
case "itemadd":
if (data.name == undefined || data.id == undefined) break;
Role._itemsInRoom[data.name] = data.id;
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);
},
/*
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 : 2000;
}
CmdExecuter.prototype.execute = function() {
if (this.isWorking) return;
this.isWorking = true;
if (this.willStartExecute) this.willStartExecute();
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;
}
// @开头,虚命令,不真正执行
if (cmd.indexOf("@") == -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._finishExecute = function() {
this.isWorking = false;
if (this.didFinishExecute) this.didFinishExecute();
};
/* ------------------------ Workflow ------------------------ *\
\* ---------------------------------------------------------- */
function WorkItem(identifier, run) {
this.identifier = identifier;
this.run = run;
}
WorkItem.prototype.setDidFinish = function(didFinish) {
this.didFinish = didFinish;
};
var WorkItemCenter = {
register: function(value, key) {
if (typeof value == "function") {
WorkItemCenter._items[key] = value;
} else {
WorkItemCenter._items[value.identifier] = value;
}
},
take: function(key) {
return WorkItemCenter._items[key];
},
allKey: function() {
return []; // ...
},
_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() {
WG.go("练功房");
let item = this;
window.setTimeout(function() {
WG.Send("stopstate;dazuo");
item.didFinish();
}, 2000);
}));
WorkItemCenter.register(new WorkItem("等待技能冷却", function() {
var timer;
let item = this;
timer = window.setInterval(function() {
if (!Role.hasCoolingSkill()) {
window.clearInterval(timer);
item.didFinish();
}
}, 1000);
}));
var Workflow = {
name: undefined,
items: [],
willStartWorkflow: undefined,
didFinishWorkflow: undefined,
willStartWorkItem: undefined, // optional: function(item.identifier)
didFinishWorkItem: undefined, // optional: function(item.identifier)
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];
item.setDidFinish(function() {
if (Workflow.didFinishWorkItem) Workflow.didFinishWorkItem(item.identifier);
messageAppend("▶ " + item.identifier + "执行完毕。");
window.setTimeout(function() {
Workflow._start(number + 1);
}, 2000);
});
if (Workflow.willStartWorkItem) Workflow.willStartWorkItem(item.identifier);
messageAppend("▷ 正在运行 " + item.identifier + "...");
item.run();
},
_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, enemyNames, willStartDungeon, didFinishDungeon, willPerformCmd, didPerformCmd) {
this.name = name;
this.description = description;
this.cmds = cmds;
this.enemyNames = enemyNames;
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,
2000
);
}
Dungeon.prototype.enter = function() {
this._reset();
this._hookIndexes.push(this._monitorEnemy());
this._hookIndexes.push(this._monitorAddEnemy());
this._hookIndexes.push(this._monitorEnemyDie());
if (this.willStartDungeon) { this.willStartDungeon(); }
this.executer.execute();
};
Dungeon.prototype._reset = function() {
this._hookIndexes = [];
this._liaoshangDoing = false;
this._waitingSkillCD = false;
this._noneEnemy = false;
this._enemyIds = undefined; // 当前房间的敌人id
this._enemyCounter = 0; // 当前房间剩余的敌人
};
Dungeon.prototype._willPerformCmd = function(lastCmd, cmd) {
if (this._enemyCounter > 0) {
this._killEnemy();
return null;
}
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(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.prototype._killEnemy = function() {
if (this._enemyIds == undefined) { return }
for (var i = 0; i < this._enemyIds.length; i++) {
let enemyId = this._enemyIds[i];
WG.Send("kill " + enemyId);
}
};
Dungeon.prototype._monitorEnemy = function() {
let dungeon = this;
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 (dungeon.enemyNames.indexOf(item.name) >= 0) {
enemyIds.push(item.id);
}
}
dungeon._enemyIds = enemyIds;
dungeon._enemyCounter = enemyIds.length;
});
return index;
};
Dungeon.prototype._monitorAddEnemy = function() {
let dungeon = this;
let index = WG.add_hook("itemadd", function(data) {
if (data.name == undefined) return;
if (dungeon.enemyNames.indexOf(data.name) == -1) return;
if (dungeon._enemyIds) {
dungeon._enemyIds.push(data.id);
dungeon._enemyCounter += 1;
} else {
dungeon._enemyIds = [data.id];
dungeon._enemyCounter = 1;
}
});
return index;
};
Dungeon.prototype._monitorEnemyDie = function() {
let dungeon = this;
let index = WG.add_hook("sc", function(data) {
if (data.id == undefined || !dungeon._enemyIds) return;
if (WG.inArray(data.id, dungeon._enemyIds) && data.hp == 0) {
dungeon._enemyCounter -= 1;
}
});
return index;
};
// Register Dungeon Work Item
WorkItemCenter.register(function(name, subname) {
let config = DungeonConfig[name]["universal"];
var totalName = name;
var description = config.description;
var cmds = config.cmds;
var enemyNames = config.enemyNames;
var willStartDungeon = config.willStartDungeon;
var didFinishDungeon = config.didFinishDungeon;
var willPerformCmd = config.willPerformCmd;
var didPerformCmd = config.didPerformCmd;
let subconfig = DungeonConfig[name][subname];
if (subconfig) {
totalName = name + "(" + subname + ")";
if (subconfig.description) description = subconfig.description;
if (subconfig.cmds) cmds = subconfig.cmds;
if (subconfig.enemyNames) enemyNames = subconfig.enemyNames;
if (subconfig.willStartDungeon) willStartDungeon = subconfig.willStartDungeon;
if (subconfig.didFinishDungeon) didFinishDungeon = subconfig.didFinishDungeon;
if (subconfig.willPerformCmd) willPerformCmd = subconfig.willPerformCmd;
if (subconfig.didPerformCmd) didPerformCmd = subconfig.didPerformCmd;
}
let dungeon = new Dungeon(
totalName,
description,
cmds,
enemyNames,
willStartDungeon,
undefined,
willPerformCmd,
didPerformCmd
);
let result = new WorkItem(dungeon.name, function() {
if (this.description) messageAppend(this.description);
let item = this;
dungeon.didFinishDungeon = function() {
if (didFinishDungeon) didFinishDungeon();
item.didFinish();
};
dungeon.enter();
});
return result;
}, "扫荡副本");
// Dungeon Config
let DungeonConfig = {
"移花宫": {
"universal": {
enemyNames: [
"花月奴",
"移花宫女弟子",
"移花宫二宫主 涟星",
"移花宫大宫主 邀月",
"移花宫少宫主 花无缺"
],
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;
},
},
"困难": {
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"
],
},
"简单": {
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"
],
},
},
"白驼山": {
"universal": {
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",
],
enemyNames: [
"白驼山少庄主 欧阳克",
"白衣少女",
"