|
本帖最后由 houyi80 于 2012-10-25 16:15 编辑
一直答应胖胖写一个入门教程,但是看到论坛有Mush Client(以下简称MC)的机器人下载,而且有一定的说明,所以放弃了。
近来看到有些同学想自己写MC的机器人,想必是不知道怎么开始吧。所以我这个小贴子主要讲写MC的准备工作,及其一个简
单的自动学习机器人例子。
一、 MC的配置工作
假设MC的安装路径为E:\Mush Client,脚本存放目录为:E:\Mush Client\Scripts\。
虽然MC不用脚本也能写一些简单的机器人,但是功能太弱小了,所以我们必须要掌握一种脚本语言。 不要害怕要学习一门语言,实际上在初期我们用到的语言只是很少一部分,你能在半天不到的时间就能完全掌握。
LUA在MC 3.52开始支持,而且不通过COM,能移植到Linux等环境,所以我推荐大家使用该脚本语言。 当然如果你对C,Java,VB,Perl等语言很熟悉的话,采用相对应的脚本语言也行。事实上我认为这样的同学通常不需要看我的文章了。
好了言归正传,我们在E:\Mush Client\Scripts\下面建立一个test.lua的文件。
如图一所示,点击红色箭头所指的图标,弹出脚本配置的窗口。在蓝色矩形框里的"enable scripts(active)"的前面勾上勾,这是你能使用脚本语言的前提。
点击绿色框的按钮,选择我们刚才建立的test.lua文件,按确定。黄色框里的按钮可以配置你喜欢用的脚本编辑工具,软件默认的是内建的记事本。我习惯了Ultra Edit,所以里面选择的是Ultra Edit的运行程序。
至此我们的准备工作完成了。下面就可以开始写脚本了。
二、 MC的Trigger
1.MC的正则表达式
MC设计的年代较为久远,所以对双字节没做支持,还好在后面的版本加入正则表达式匹
配时增加了对双字节的支持。
所以我们无法采用像Zmud一样的普通匹配模式来完成我们的匹配任务了。虽然正则表达
式对初接触程序的人来说可能会有些困难。
但是在这里我们只需掌握很少一部分规则就能完成大部分的工作,而且会得到更大的好处
就是能多行触发。这个功能可以避免我们很多的误触发。我觉得MC比Zmud好的地方,这个能
算 一个。Zmud里是无法实现真正的多行触发(或许是我没有发现),他的高版本里面的多行
触发其实是把过去我们用的#T+ #T-放在一个地方而已。好了下面介绍一下MC常用的正则表
达式。
. 表示匹配任何的字符,除了换行符。
* 表示匹配前面字符0个或者多个。
^ 表示匹配一行的开始。
$ 表示匹配一行的结束。
这两个是最常见的用法,.*相当于普通模式下面的*,即匹配任何数量任意字符。
比如我们要对这样的语句进行触发:
"你的基本轻功进步了!"
然后我们想看看此时的轻功等级,在泥潭II里面我们可以用skills查看。那么可以如图二所
示的那样添加一个trigger,
如图三所示输入我们要匹配的信息。这里再介绍几个常用的正则表达式:
[] 匹配里面包含的字符,比如[>]表示匹配">",[a-z]表示匹配从a到z的字母。
\s 表示匹配空格字符,包括制表符。
我们联合前面介绍的.和*可以得到一些有用的表达式。比如[>]*\s*这个表示匹配任意多
个">"后面再匹配任意多个空格。
我们举这个例子的目的是做一个防止误触发的表达式。上面的"^.*你的基本轻功进
步 了!$"的表达式很容易被其他人给误触发了,因为它的前面是匹配任何的字符。虽然很多情
况下,我们可以去掉前面.*,但是MC经常会出现"> "的提示符。所以我们要把这两个给匹
配上。于是我们改写成"^[>]*\s*你的基本轻功进步了!$"。对所有的武功进行触发的话,我们
只需要把"基本轻功"改成.*" 就可以了。
其他常用的正则表达式有:
\n 表示匹配换行,在多行触发的时候非常有用。
\d 表示匹配数字。
\w 表示匹配英文单词。
\D 表示匹配非数字的。
\W 表示匹配非英文单词的。
| 表示匹配两个中的任意一个。用法和zmud的差不多,不过是用"()"而不是"{}"。这个
在QM里和UQ里面会常用到。比如用"^[>]*\s*(束|根|个|颗).*"可以匹配"你得到一颗玄黄紫
清丹!","你得到一束冰蚕丝!"。
? 表示匹配零或者一个。
+ 表示匹配一个或者多个。
\ 表示转义,比如"("这样的字符是正则表达式里面的关键词,如果要匹配就必须用\(来
表示。
对于需要我们再次使用或者处理的关键字,我们可以用%i来表示,i可以是0到999。
比如我们对于这样的触发:"基本招架 (parry) - 300/ 5%" 写成正
则表达式为:
"^.*\(.*\).*\-\s*\d*\/.*$"
我们需要处理的是"parry"和"300"。那么代替它们的通配符就是%1和%5(顺序从0
开始)。
显然这样非常难看,而且容易弄错它们的序号。类似于赋予变量名称的Zmud里面
的"&name",MC的是?P<name>(说实话也挺难看的,不过顺序我们就不会弄
错了)。上面的表达式可以改写成:
"^.*\((?P<skill_name>.*)\).*\-\s*(?P<skill_level>\d*)\/.*$"
好了,我们终于可以进行对匹配成功的语句作相对应的动作了。
2. MC的几种常见的触发动作。
1) 直接发送到MC(直接执行)
MC默认的就是直接发送,这也是常用的方式。我们只要在"Send"的框里填上我们想要作
的动作即可。例如在游戏里有人向你打招呼,你可以做出反应。
"> 【论道江湖】草籽双手抱拳,对蜀江春水作了个揖道:这位道长请了!"
我们可以写如下的触发条件:
"^.*【论道江湖】.*双手抱拳,对蜀江春水作了个揖道:这位道长请了!$"
在"Send"框里我们就写上"chat* hi"。如图四所示。
2) 发送到Script
这个是非常重要的。我们大部分复杂的操作都不需要用到这种方式。比如需要实现像
Zmud里面的#wa延时命令,基本上只有这个选择。例如我们要在向师傅学习了N次以后需要等
待2秒才能再次学习。触发条件是:
^.*你开始向.*请教.*句有关「.*」的疑问。$
这里我们需要在如图五的绿色框标注的地方选择发送方式为:script。
在黄色框里我们可以直接引用函数DoAfter(duration,action),duration这里填入你想
延时的时间,单位为秒,action是表示你想干的事。这里我们是继续学习,假设师傅是无涯
子,学习北冥神功200次,填上:DoAfter(2,"learn zi beiming-shengong 200")。注意你
的动作必须用双引号引起来,因为语言里字符不用引号会被认为是变量。
在图示里面我们采用的是另外一种方式,就是在前面我们建立的脚本文件test.lua里写一
个函数"study()":
---------学习函数---------------------------------
function study()
DoAfter(2,"learn zi beiming-shengong 200");
end
--------------------------------------------------
这样有个好处,以后引用方便,还容易被其他函数引用。修改也容易些,不用跑到MC一
个一个的找。
这里再解释一下DoAfter()函数,这个函数实际上是生成一个只执行一次的临时计时器。
使用计时器方式比Zmud里的#wa优越的地方就是不会因为一些不好的触发条件导致flood,因
为它每次是替换上一个,所以总是会隔固定的时间执行命令,而#wa它是独立。比如#wa
2000,表示2秒内执行一次动作,但是如果被连续误触发了20次,那么他会在2秒内执行
20 次,很容易就被雷给劈晕了。
在脚本和MC打交道的常用函数有:
Send(string) 直接执行string的内容,像第一个例子其实也可以用发送到Script的
方式实现。即:把发送方式改为script,然后在"Send"框里写上"Send("chat hi")"。
在同一类事情中,比如学习,QM,UQ等,我们希望在做QM的时候,学习的触发都要关
闭了,以免引起误触发,这是我们需要给trigger分类,在图五的红色标示的地方填入你的类
名称,MC称为Group。于是另一个有用的函数就是:
EnableTriggerGroup(GroupName,flag) GroupName表示你想操作的类名,
比如我们学习的是Study;flag表示你要设置的类的状态,有两个值,true和false,true表示
开启该类,false表示关闭该类。
例如我们达到了我们期望的学习目的时结束Study类的trigger,我们就用:
EnableTriggerGroup("Study",false);
GetVariable(name) 从MC里得到变量名为:name的值
SetVariable(name,value) 设置MC的变量名为:name的值为value。
因为其他的发送方式很少用到,这里就不介绍了。
三、 Lua的一些介绍
这节的主要的对象是学过编程但是实际编程经验很少的同学,如果要深入了解Lua,可以参考Programming in Lua这本书,有中文版的。还有主要是和做机器人关系密切的内容,其它的概不介绍。
1. 变量的类型
Lua有8种变量类型,nil,blooen,string,number,userdata,function,thread and
table。
我们在机器人里经常用到的是string和number型的,但是要注意nil和table型。我们在编
写脚本的时候,通常不能一次成功,这个时候MC的提示信息就很重要了。比如我们的函数有问
题,那么MC给出的信息会提示我们该函数值为nil。table类型可以完成很多复杂的结构,是以后
编制复杂的机器人很重要的手段。
由于Lua是一种动态语言,所以变量不用显式的申明其类型。这是一个优点也是一个缺
点。我们要注意在MC传送参数过来的时候,一定是string类型的。虽然对变量进行计算时,Lua
会自动转换变量类型,但是有时候我们必须对它进行转化才能获得正确的结果。常见的就是用
tonumber()函数转化为数字型的。例如对一个学习列表:
study_base_list = {
"force",
"beiming-shengong",
"dodge",
"parry",
"hand",
"strike",
"sword",
"blade"
}; --逍遥派学习的顺序。
这是一个数组,study_base_list[1] 就表示 "force" 这个字符串,同理
study_base_list[7]就表示"blade"。我们的这个序号从MC得到的话,它表现出来就是
study_base_list["1"],study_base_list["7"]等等。虽然Lua的table类型可以用字符串来
作为下标,但是在这里就出现错误了。因为study_base_list["1"]并没有一个值。MC就会提示
为:study_base_list["1"]的值为nil。
Lua里默认申明的变量都为全局变量,为了避免出错,我们还是尽量申明局部变量。语法是:
local 变量名
2. 控制结构
在我们的机器人里,主要是if 表达式 then 语句块 else 语句块 end这样的结构最为常
见,循环语句在很复杂的情况下才会出现。所以这里仅仅介绍一下if语句。
例子:
function study()
study_index = tonumber(GetVariable("study_index"));
if study_list[study_index] then
DoAfter(2,"learn ".. master .." " .. study_list[study_index] .. " 200");
else
te("Study");
SetVariable("study_index",1);
print("学习完毕,请指示下一项工作!");
end --if
end --function
这里判断study_list[study_index]是否有值,有值则学习,没有说明已经学完了,或者
study_index的值非法,直接结束。
Lua只有当表达式为false和nil才认为是假,其它认为是真,比如0就是真,这个和C不一样。
3. 函数
其实上面的例子已经可以很明了的知道函数的结构了。下面再举一个带有参数的函数以便更
好的说明。
function study_check(skill_name,skill_level)
local dstlv = tonumber(GetVariable("dstlv"));
if skill_name == study_list[study_index] then
if skill_level >= dstlv then
skill_index = GetVariable("study_index") + 1;
SetVariable("study_index",skill_index);
end --if
end --if
end --function
细心的同学可能已经发现这个函数的skill_name,skill_level两个参数的名字和我们在正则
表达式里的最后一个例子的trigger里的两个变量名一模一样。对的,这个函数就是处理由
trigger获取的那两个变量的值的。local dstlv是申明一个局部变量,然后把MC的变量
"dstlv"的值赋给它。接下来判断trigger里技能名称和我们现在学习的技能名称是否相等,如果
相等则检查它的等级是不是大于我们学习的目标等级(dstlv)。如果大于等于目标等级,我们就应
该学习下一项技能了,所以学习的序列应该向下移一位,所以更新MC的变量"skill_index"的值
为以前的值加上一。
四、 一个完整的自动学习机器人
-------------------------------------------------------------------------------------------------------------------------
<triggers>
<trigger
group="Study"
match="^.*\((?P<skill_name>.*)\).*\-\s*(?P<skill_level>\d*)\/.*$"
regexp="y"
send_to="12"
sequence="100"
>
<send>study_check("%<skill_name>",%<skill_level>)</send>
</trigger>
<trigger
group="Study"
lines_to_match="2"
match="^.*你的.*基础不够,再学下去会走火入魔的。$\n^.*你现在精气旺盛。$"
multi_line="y"
regexp="y"
send_to="12"
sequence="100"
>
<send>study_failed_do()</send>
</trigger>
<trigger
group="Study"
match="^.*你的「.*」进步了!$"
regexp="y"
send_to="12"
sequence="100"
>
<send>study_skills()</send>
</trigger>
<trigger
group="Study"
match="^.*你的基本内功基础不够,再学下去会走火入魔的。$"
regexp="y"
send_to="12"
sequence="100"
>
<send>study_skip()</send>
</trigger>
<trigger
expand_variables="y"
group="Study"
match="^.*你刚刚才学习过(如果你要连续学习,可以指明学习的次数)。$"
regexp="y"
send_to="12"
sequence="100"
>
<send>study();</send>
</trigger>
<trigger
group="Study"
match="^.*你今天太累了,.*学习了.*$"
regexp="y"
sequence="100"
>
<send>sleep</send>
</trigger>
<trigger
group="Study"
match="^.*你开始向.*请教.*句有关「.*」的疑问。$"
regexp="y"
send_to="12"
sequence="100"
>
<send>study()</send>
</trigger>
<trigger
group="Study"
match="^.*你一觉醒来,只觉精力充沛。该活动一下了。$"
regexp="y"
send_to="12"
sequence="100"
>
<send>wake_study()</send>
</trigger>
<trigger
group="Study"
lines_to_match="2"
match="^.*也许是缺乏实战经验,你对.*的回答总是无法领会。$\n^.*你现在精气旺盛。$"
multi_line="y"
regexp="y"
send_to="12"
sequence="100"
>
<send>study_failed_do()</send>
</trigger>
<trigger
group="Study"
match="^.*这项技能你恐怕必须找别人学了。$"
regexp="y"
send_to="12"
sequence="100"
>
<send>study_skip()</send>
</trigger>
</triggers>
----------------------------------------------------------------------------------------------------------------------- 上面这一段是MC里自动学习机器人的xml形式,同学们可以复制它,然后打开MC的trigger(快捷键是:Ctrl+Shift+8)。点弹出的窗口的右下角的"Paste",或者用快捷键Ctrl+V就可以得到这些trigger了。如果熟悉xml的,可以打开MC的world文件直接复制到里面也行。下面的脚本和MC里的变量"study_index","dstlv"有交互,大家可以自行在MC里添加,快捷键是Ctrl+Shift+7。
-----------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Alias
--全局变量
-------------------------------------------------------------------------------
master = "wuya zi"; --师傅ID
times = 200; --学习次数 因为MC解析的问题,和我Kingwar的机器人有冲突,所以写
死到函数里了。
food = "eat gan liang\ndrink shui";
-------------------------------------------------------------------------------
-- Function
-------------------------------------------------------------------------------
function te(group_name)
EnableTriggerGroup(group_name, false);
end --function
function ts(group_name)
EnableTriggerGroup(group_name, true);
end --function
--这两个是简化关闭一类trigger的函数,下面有用到。
-------------------------------------------------------------------------------
-- Study
-------------------------------------------------------------------------------
study_begin_list = {
"literate",
"force",
"xiaowuxiang"
}; --逍遥派初始学习的顺序。
study_base_list = {
"force",
"beiming-shengong",
"dodge",
"parry",
"hand",
"strike",
"sword",
"blade"
}; --逍遥派学习的顺序。
study_other_list = {
"literate",
"medical",
"xiaoyao-qixue",
"mathematics",
"qimen-wuxing",
"drawing",
"calligraphy",
"chess",
"chuixiao-jifa",
"tanqin-jifa",
"training" --向戚长发学习的,下面睡醒函数有个往南的动作和上面的睡觉触发有个往
北的动作,与此有关。
}; --逍遥派杂学列表
--这里只是逍遥派的武功,大家可以根据自己的需要更改上面的列表。
study_list = study_base_list; --更改学习列表
function study()
study_index = tonumber(GetVariable("study_index"));
if study_list[study_index] then
DoAfter(2,"learn ".. master .." " .. study_list[study_index] .. " 200");
else
te("Study");
SetVariable("study_index",1);
print("学习完毕,请指示下一项工作!");
end --if
end --function
function study_skills()
if study_list[study_index] then
Send("skills".." "..study_list[study_index]);
end --if
end --function
function study_check(skill_name,skill_level)
local dstlv = tonumber(GetVariable("dstlv"));
if skill_name == study_list[study_index] then
if skill_level >= dstlv then
skill_index = GetVariable("study_index") + 1;
SetVariable("study_index",skill_index);
end --if
end --if
end --function
function study_skip()
if study_list[study_index] then
-- skill_index = GetVariable("study_index") + 1;
-- SetVariable("study_index",skill_index);
study();
end
end
function study_failed_do()
study_skills();
study();
end --function;
function wake_study()
study();
if study_list[study_index] then
Send(food .. "\ns\nskills "..study_list[study_index]);
end;
end
-----------------------------------------------------------------------------------------------------------------------
这一段复制到test.lua里面即可。
五、 结语
到这里我们自己应当能做一个简单的机器人了,为了进一步的提高,我们需要多看MC的帮助文件,和对脚本的语言的深入了解。最重要的还是要对游戏多了解,这样我们才能选择合适的触发条件和做出合理的反应来。比如上面的学习机器人实际上还不完善,在潜能花完的时候,并没有任何的动作。我们可以关闭该类触发,然后做其它事情。最后祝愿同学们能早日做出适合自己的完美机器人来。
这是我转的泥巴创世纪的,希望对大家有用
|
|