常言笑的家

Spring, Hibernate, Struts, Ajax, RoR

ZMUD机器人制作(上篇)

目录

第一章 聊聊zmud和机器人

基础篇
第二章 机器人的核心灵魂
第三章 variable和alias的初级应用以及#if
第四章 实际例子-打坐吐纳机器人

中级篇
第五章 按钮﹑系统变量和函数
第六章 variable和alias的更多应用
第七章 掌控时间
第八章 提高机器人稳定性和适用面
第九章 丰富你的机器人
第十章 实际例子--朝廷守门机器人
补充章 Path的用法

高级篇
第十一章 variable和alias的进一步应用
第十二章 遍历实现的一种方法
第十三章 胡一刀和推车全自动的可行性分析
第十四章 实际例子--解决乱入的2种办法

 

第一章 聊聊zmud和机器人

本章不涉及机器人制作的实际方法,但是却是最重要的,所以我想放在第一位置来说。

1.1 Zmud的优缺点

如果你要问我几款主流mud工具zmud,cmud,mush哪个最好?我的回答肯定是mush。

mush拥有强大的脚本功能,即使不用脚本,也能做出很复杂的机器人,从稳定性上来说,mush也明显比zmud好。可以说,凡是zmud能做到的,用mush都能做到。可是mush不适合大多数人,如果你完全不懂mush,简单的正则表达式都能让你头晕,更不用说它的脚本语言了。

而zmud最大的优点就是简单,简单就容易流行。即使你随便乱写,它也不会报错,它总有办法执行下去,实在执行不了,最多也就zmud程序本身崩溃掉。他的语言相当灵活,你可能会有多种办法来实现你的计划,这点我会在后面章节的实际例子中尽量涉及到。就目前来说,zmud提供的功能已经足够玩mud游戏。

zmud最让人头疼的问题就是对中文的支持不好,某些汉字的截取或者显示会变成乱码,给机器人的制作带来麻烦。出现了乱码问题,只能自己想办法解决了,绝大多数还有办法对付的。
另外zmud462和555的tick timer会在多个窗口之间发生干扰,所以设置tick timer的时候要统一,等讲到tick timer的时候再详细说。

1.2 优秀机器人的充分必要条件

好的机器人应该具备几个条件,从游戏本身来考虑,非要做个排名的话,我认为应该是安全稳定效率适用面广,当然如果你还能做到界面丰富生动简洁易修改等等那更好了。

从另外一个方面考虑,好的机器人应该具备一些职业道德。响应冰冰同学的号召,不要在chat,rumor等公共频道发无聊的信息,也不要随意say和emote,不要随意的get all,不要对别人做表情,不要刷屏等等。游戏环境还是要靠大家共同维护

1.3 机器人制作的良好习惯

如果你养成了良好的习惯,将会节约你大量的时间,避免重复劳动。
使用variable和alias的时候,为变量取名字一定要含义清楚,否则时间长了连你自己都会忘记,不易于修改。

尽量为每个trigger设置class,相近的trigger归到一个class,class取名同样要含义清楚,以方便其他的机器人加载。

对于用的多的一些trigger,variable和alias,可以点击菜单Settings->Export专门保存起来,要用的时候再点击Settings->Import->Script加载就可以了。

1.4 寻求帮助
语法格式忘记了,或者不知道用法,请优先使用zmud自带的help文档,这才是最全面最权威的教程。在命令栏输入#help就能打开了。还可以有针对性地输入比如#help while,#help exe,#help if来查找。
第二章 机器人的核心灵魂

2.1 触发triggers

一个机器人什么都可以没有,就是triggers不能没有,所以称它为机器人的灵魂一点没错.机器人都是靠triggers来作出反应的.

你可以从菜单里打开你的triggers,也可以点击那个手枪的图标来打开triggers.然后点击new来新建一个trigger,你会发现trigger有四个要素:Pattern﹑Commands﹑Enable/Disable和Class

Zmud根据Pattern的内容来做出反应,简单点说只要你的屏幕上出现了跟Pattern一致的内容,Commands里的内容就作为命令发送出去了。Enable表示这个trigger是激活状态,Disable表示trigger不激活,处于无效状态。Class表示这个trigger所属的类,可以通过操作整个类来达到一次性同时操作多个trigger的目的。

另外trigger还有Options和Test,Zmud721中还有States
Test是用来测试触发的,实际用处不大,trigger制作熟练了很少会测试。
Options里默认的勾上的几个选项就是平时用到的。没勾上的几个除了alarm之外用处都不大,alarm我会在以后讲到;Temporary是用来建立一个一次性trigger,用完就扔的那种,命令是#temp,但是我更喜欢用#t+ #t-来达到这个目的;Expression是建立一个表达式trigger,没什么用;颜色触发也不像期待中的那样强大,有兴趣可以自行研究。

2.2 字符串匹配
简单点说就是通过几个特殊的符号来代表具有某些共同之处的一类信息。只被用于Pattern。
zmud提供的字符号串匹配有:
    *     通配任何数字、字符或空白
    %d 通配任何数字(0-9)
    ?  通配任何单个字符
    %s 通配任何空白(spaces,tabs)
    %w 通配任何字母(a-z)
    %a 通配任何数字和字母(等于%d+%w)
    %n 通配任何以+或-开头的数字
    %x 通配任何非空白
    %p 通配一个方向
    [range] 通配任何在range范围内的字符
    ^ 表示一行的开头,想要你的trigger在一行的开头才有效,就在最前面加上这个
    $ 表示一行的结尾。
    (pattern)将括号里面的内容依次存放在参数%1~%99里
    {val1|val2|val3|……} 通配val1,val2或者val3
    还有一个特殊字符就是~,用在特殊字符前面。因为有些特殊字符被zmud理解为其他意思了,所以为了通配这些特殊字符,前面要加~
    比如~{通配{     ~?通配?      ~~通配~
在命令行输入#say %def,默认显示为#;@!%.:~>&,再加上括号{}()[],这些都是有特殊含义的特殊字符了,要匹配这些字符,前面都要加上~

关于字符串匹配,主要就是这些了,而真正常用的就几个,上面标记为蓝色的是我用过的,这些就够了,其余的一次都没用过。所有的这些都可以在zmud帮助文档里查到。

实例一:#tri {□手持一个圣火令~(Shenghuo ling~)} {#say 明教武士}
//#tri表示用命令新建一个trigger,第一个花括号里的内容作为Pattern,第二个花括号里的内容作为Commands
//这里用到~(来通配(

实例二:#tri {【%s气血%s】%s(%d)%s/%s(%d)%s~[(*)~%~]%s【%s内力%s】%s(%d)%s/%s(%d)} {#show %1 %2 %3 %4 %5}
//依次显示出你的气血 气血最大值 受伤状况 内力 内力最大值
//如上面讲过的,()的作用是把里面的内容按顺序依次保存在临时参数%1~%99里

上面2个例子分别用到了zmud提供的2个命令#say#show,功能差不多颜色不同,都只是在屏幕上显示某些内容用的,这些内容不作为命令发送给服务器执行,但是zmud会用这些内容来触发trigger,所以做机器人的时候可以用这个来做触发,好处可以自行体会。

建议:做Pattern的时候不要手动输入,因为中文英文半角全角各种特殊字符还有空格符,你的手动输入很可能不正确,等整个机器人做成功了查错又不容易。所以尽量用粘贴的,然后先把里面的空格全部换成%s,把所有的特殊字符前面都加上~,要怎么通配的就换成什么通配,最后看要不要顶行触发或者结尾触发,这样一般不会有错了。

2.3 Class和#t+ #t-的用途

Class有好几个用途,比如为了方便Import,点击Settings->Import->settings,然后选择一个机器人,再选择一个Class点击Add-OK就可以把另外一个机器人里面所有归为某个Class的triggers,variablde,alias等都添加到当前机器人里面。如果删除某个Class,那么该Class下的所有元素全部删除。

#t+和#t-分别是激活和关闭某类trigger。#t+和#t-都是针对Class的名字来操作,所有的variable,alias,trigger,button都可以取一个class名字,但是只有trigger才有Enable/Disable状态,所有#t+/#t-只对trigger有效。这2个命令被广泛的应用与机器人制作中,非常重要。

实例三:某些trigger只希望它在特定的时候激活,其他时候都处于关闭状态,用来防止误触发,这时候用#t+ #t-是最适合不过的了。
#tri {你向林震南打听有关『*镖』的消息。} {#t+ job}
#tri {林震南说道:「你上次运镖太辛苦了,下去休息休息吧。」} {#t- job;#wa 5000;ask lin about job} {job}
//这里第2个trigger设置了一个class:job,平时都处于disable状态,只有当你向林要任务的时候才开启,并且在trigger触发之后立刻关闭。
如果你不这样做,当别人来要任务也要不到的时候会出现同样的触发信息从而误触发。游戏里好多机器人同时跟林要任务,互相干扰,结果就变成要任务比赛了。那么恭喜你,你的机器人是个流氓,刷屏!
第三章 variable和alias的初级应用以及if语句

3.1 Variable和Alias

相关命令 #var,#math,#add,,#alias
北侠众应该都知道Alias的用法吧,论坛和主页下载的zmud包里都有前辈做好的路径,要去哪里简单的输入地名的拼音就可以了。
Alias就是别名,用一个短命令替代一串长命令

另外Alias还可以带参数使用
实例一:#alias {chan} {wield sword;perform sword.chan %1 %2;unwield sword}
这样就制作一个alias,下面分别是4种使用结果
A: chan          相当于输入wield sword;perform sword.chan;unwield sword
B: chan wushi    相当于输入wield sword;perform sword.chan wushi;unwield sword
C: chan zhang san相当于输入wield sword;perform sword.chan zhang san;unwield sword
D: chan 1 2 3    相当于输入wield sword;perform sword.chan 1 2;unwield sword 3
可以看到,无论你带不带参数,带几个参数,zmud都不报错,他都按照他自己固定的理解来执行你的命令,2个参数以内的依次替换成%1 %2,多余的参数全部放在最后,没有就直接无视,相当的灵活。

Variable就是变量,只有用到了变量,才能进行各种运算,并且根据运算结果作出不同的反应。
#var为一个变量赋值,而不管这个变量存不存在。变量不存在时新建变量,存在时改变它的值。
比如#var abc 100,将100赋值给变量abc,或者说abc的值就是100了。另一种赋值方法是abc=100

变量的值用@加上变量名来使用和表示。
例如: #show abc的值是@abc   
             显示为 abc的值是100
#math 用来计算,将结果赋值给变量,zmud只支持+ - * /和()的四则混合运算,运算优先权是()大于*/大于+ -,并且仅支持整数,如果运算结果不是整数,自动进行取整运算。

小技巧一:[ ]的运用

[ ]表示运算之后结果,举例来说
#var a 100/5;#var b [100/3];#math c 100/3
#show @a,@b,@c
显示结果为100/3,33,33
没错,#var不负责运算,[]负责运算,起到了#math的作用。

小技巧二:&variable和@variable直接用在trigger的Pattern里面

实例:如何使用大米a
#tri {胡一刀说道:『我收到消息,听说&hydaddress有盗宝人&dbrname~(&dbr~)找到了闯王宝藏的地图} {}
#tri {你有种去&hydaddress找我兄弟&dbrname~(&dbr~),他会给我} {}
#alias ddd {act hydjob 地点=@hydaddress,name=@dbrname,id=@dbr}
#tri {盗 宝 人*@dbrname~(@dbr~)} {follow @dbr;hit @dbr}
可以看到第一个trigger和第二个trigger,只有Pattern,没有任何Commands
没错,&hydaddress,&dbrname,&dbr被用来做通配了,通配之后被通配的内容已经自动保存到相应变量里了,不需要任何赋值操作可以直接使用了。
命令行输入ddd就可以在动作频道发出信息让大米a去寻找盗宝人了。
最后一个trigger,@dbrname,@dbr也被用来做通配了,只有找到自己的盗宝人才会触发跟随攻击命令。
这种方法又体现了zmud的灵活性,可以省去赋值,也可以省去判断是否是自己的盗窃人
另外&和变量名中间可以再加入%d或者%w等特殊字符。比如&{%w}dbr用来表示仅仅通配字母,并且赋值给变量,不匹配的不会触发并且不会赋值。

小技巧三:现在已经讲了trigger,variable和alias,后面还会讲到button,还有macro,这些都可以互相糅合互相嵌套,任何一个都可以包含另外一个,也可以自己包含自己,再次体现了zmud的灵活。

实例:#Alias jiaxing {do 6 e;se;s;e;e;#var back jiaxingb}
    #Alias jiaxingb {w;w;n;nw;#6 w}
    #ALIAS taohua {#6 e;se;s;e;e;nd;e;e;e;e;n;enter;enter boat;#var back taohuab}
    #ALIAS taohuab {out;s;w;w;w;w;su;w;w;n;nw;#6 w}
    #alias ddd {@back}
可以看到alias里包含了变量,变量里包含alias,以上4个alias分别是嘉兴和桃花去和回的路径,但是嫌回来时后面加个b太麻烦了,就用上面这个方法。不管你是在嘉兴还是在桃花,只要去的时候是用alias去的,回来的时候统一用ddd返回。并且不影响jiaxingb,taohuab的使用。

3.2 if语句 相关命令#if

格式:#if 表达式 {commands1} {commands2}
当表达式的结果运算为真时,执行commands1,否则执行commands2

另2种变化了的格式都可以用
#if 表达式 {} {commands2}
#if 表达式 {commands1}

比如:#tri {【%s气血%s】%s(%d)%s/%s(%d)%s~[(*)~%~]%s【%s内力%s】%s(%d)%s/%s(%d)} {#if (%1>500) {dazuo 500} {exert recover;dazuo 500}}
简单的判断一下气血值,如果大于500就dazuo 500,否则就先吸气然后dazuo 500

#if 可以多重使用,commands1和commands2里面都可以包含一个或多个#if
表达式也可以是多个表达式的组合,多个表达式之间用&或者|来连接。&表示“并且”,|表示“或者”。例如
((表达式1&表达式2)|表达式3) 只要表达式3为真,总表达式就为真;只要表达式12同时为真,总表达式就为真;其他情况都不真。
注意事项:去掉中间的子括号,(表达式1&表达式2|表达式3)会造成歧义,zmud不会报错,具体结果请自行试验。

表达式里通常用到的比较符号有“>” “<” “=” “!=” “<>” “<=” “>=”,其中!=表示不等于,<>表示大于或者小于
注意事项:表达式不仅仅是比较数字,也能比较字符串甚至中文,如有需要可以加上" "
         比如(@dbr="mengmian shashou"|@dbrname="张三")

实例:做一个机器人,明教弟子碰到盗宝人先夺兵器直至成功夺掉兵器,然后用阴风刀,成功用出1次阴风刀之后用tisha来busy盗宝人,一直到将其杀掉。
ps.只是举个例子,不讨论这个打法的正确性。
需要解决的困难:自己在busy的时候,任何perform的结果都是( 你上一个动作还没有完成,不能施用外功。),无法判断接下来的动作。
假设你已经将盗宝人的名字储存到@dbrname中,将盗宝人的id储存到@dbr中。
实际给出2种制作方案:

A:#tri {盗 宝 人*@dbrname~(@dbr~)} {follow @dbr;hit @dbr;perform sword.duo @dbr;#var pfm 1}
//见面夺兵器,注意pfm的值
  #tri {可是@dbrname的看破了你的企图,立刻采取守势,使你没能夺下兵刃。} {hit @dbr;perform sword.duo @dbr}
//失败了继续夺
  #tri {你使出圣火神功的夺字诀, @dbrname顿时觉得眼前一花,手腕一麻,手中兵刃脱手而出!} {killall @dbr;perform sword.yinfeng @dbr;#var pfm 2}
//夺掉了武器,阴风刀,改变pfm的值
  #tri {你悄悄使出阴风刀,运起一股无形无质的阴寒之气,直插@dbrname胸口玉堂要穴!} {wa 3000;killall @dbr;perform dodge.tisha @dbr;#var pfm 3}
//继续改变pfm的值
#tri {却不敢出声,闭紧双眼运起铁屁股神功,假装是一块石头一动不动!} {#wa 3000;perform dodge.tisha}
  #tri {@dbrname双目难睁,无法攻击。} {#wa 3000;perform dodge.tisha @dbr}
  #tri {神目如电来去悠闲,根本没有受到影响.} {#wa 3000;perform dodge.tisha @dbr}
  #tri {~( 你上一个动作还没有完成,不能施用外功。~)} {#wa 3000;#if (@pfm=1) {perform sword.duo @dbr};#if (@pfm=2) {perform sword.yinfeng @dbr};#if (@pfm=3) {perform dodge.tisha @dbr}}
//根据pfm的值的不同来发出不同的perform

B:#tri {盗 宝 人*@dbrname~(@dbr~)} {follow @dbr;hit @dbr;#var pfm "perform sword.duo";@pfm @dbr}
  #tri {你使出圣火神功的夺字诀, @dbrname顿时觉得眼前一花,手腕一麻,手中兵刃脱手而出!} {killall @dbr;#var pfm "perform sword.yinfeng";@pfm @dbr}
  #tri {可是@dbrname的看破了你的企图,立刻采取守势,使你没能夺下兵刃。} {hit @dbr;@pfm @dbr}
  #tri {你悄悄使出阴风刀,运起一股无形无质的阴寒之气,直插@dbrname胸口玉堂要穴!} {#var pfm "perform dodge.tisha";#wa 3000;killall @dbr;@pfm @dbr}
  #tri {@dbrname双目难睁,无法攻击。} {#wa 3000;@pfm @dbr}
  #tri {神目如电来去悠闲,根本没有受到影响.} {#wa 3000;@pfm @dbr}
  #tri {~( 你上一个动作还没有完成,不能施用外功。~)} {#wa 3000;@pfm @dbr}
  
  可以看到方案B明显优于方案A,巧妙的运用了一个变量pfm储存要发的perform,而不是简单的赋值为一个数字,省去了#if语句,更加简洁直观。制作机器人的时候能简洁坚决不要复杂,会使你的机器人更加容易修改。
  #wa 3000表示等待3秒后执行后面的命令,有关#wa命令后面会讲到。
第四章 机器人实例-打坐吐纳机器人

这个机器人的制作非常简单,仅仅需要第二章和第三章的知识就足够了.但是一个新手做出来的机器人和高手做的比起来就会显得简陋.

实际制作过程

希望机器人具备的特点:稳定,效率最大化,适用面最大化,打坐吐纳完毕自动下线

A 稳定方面:防发呆,断线自动重连

B 率最大化方面: (a)1000内力或者精力以下充分利用打坐或者吐纳双倍
                          (b)气血或精神如果够,刚好打坐吐纳至内力精力增加;如果不够,将气血精神用完,然后使用恢复手段
                          (c)利用休息周30%的提速,并且对付恶心的噩梦周

C 适用面方面:打坐吐纳统一为一个机器人,由于内力比精力重要,选择优先打坐,内力达到设定目标时自动转化为吐纳,同时可以设定精力优先于内力。对还没有脱离发呆室的未成年人同样适用。

步骤一:如果你听从了我的建议,相信你一定制作了4个专门的trigger来抓取hp的信息,然后给它们统一取一个class名并且保存到一个专门的文件,那么恭喜你,你现在要做的仅仅就是加载这个文件,去掉经验值和潜能抓取的2个trigger,这一步就完成了。

#tri {【%s气血%s】%s(%d)%s/%s(%d)%s~[(*)~%~]%s【%s内力%s】%s(%d)%s/%s(%d)}
         {#var qixue %1
          #var qixue_max %2
          #var qixue_health %3
          #var neili %4
          #var neili_max %5} {hp}
#tri {【%s精神%s】%s(%d)%s/%s(%d)%s~[(*)~%~]%s【%s精力%s】%s(%d)%s/%s(%d)}
         {#var jingshen %1
          #var jingshen_max %2
          #var jingshen_health %3
          #var jingli %4
          #var jingli_max %5} {hp}


步骤二:开始对内力,精力,气血,精神进行计算,来确定dazuo tuna的数值

先计算打坐
#math need [@neili_max*2+1-@neili]
//计算还需要多少内力才能使内力上限增加
#if (@neili_max<1000) {#math need (@need+1)/2}
//内力不足1000,需要减半,注意(@need+1)是为了防止/2之后自动取整造成结果不足以使内力上限增加
#var @dazuo [@qixue-(@qixue_max+9)/10]
//最多能供使用的气血是@qixue-@qixue_max/10,加9也是为了对付取整
#var need [(@need*100+129)/@sudu]
//通过一个变量@sudu来调整需要打坐的数值,+129对付取整
#var need %min(@dazuo,@need)
//%min表示2者之间小的那个
#var need %max(@need,10)
//%max表示2者之间大的那个,最终need的值就是你dazuo的数值了。注意@dazuo的值可能小于10
#if (@dazuo<10) {恢复} {dazuo @need}}
//气血不够恢复,否则dazuo

再计算吐纳,只需要做少量的修改,用同样的变量就可以了,尽量减少使用变量的数目
#var need [@jingli_max*2-@jingli+1]
#var need [(@need*100+129)/@sudu]
#math dazuo @jingshen-(@jingshen_max+9)/10]
#math need %min(@need,@dazuo)
#var need %min(@need,10)
#if (@dazuo<10) {恢复} {#if ((@neili>500)|(@fadai=0))
                                           {tuna @need} {dazuo 500}}
//与打坐的时候不同,当你脱离了发呆室并且内力不够时,选择打坐,以确保有足有的内力恢复精神

步骤三:根据time的信息来修改@sudu的值,需要2个trigger
#tri {本周为噩梦之周*降低*降低(%d)~%} {#var sudu [100-%1]}
//抓取打坐吐纳速度降低的数值到%1,对@sudu进行修改
#tri {本周为(*)之周} {#if (%1!="噩梦") {#if (%1="休息") {#var sudu 100} {#var sudu 130}}}
//最前面判断%1是否=噩梦,以避免跟第一个trigger发生干扰

步骤四:把步骤二和三合起来
#tri {本周为噩梦之周*降低*降低(%d)~%}
  {#var sudu [100-%1]
   #if (@neili_max>=@neili_aim) {#var action 0}
//用@action的值来控制行为,值为1就打坐,值为0就吐纳
   #if @action {#math need [@neili_max*2+1-@neili]
                   #if (@neili_max<1000) {#math need (@need+1)/2}
                   #var @dazuo [@qixue-(@qixue_max+9)/10]
                   #var need [(@need*100+129)/@sudu]
                   #var need %min(@dazuo,@need)
                   #var need %max(@need,10)
                   #if (@dazuo<10) {恢复} {dazuo @need}
                }
               {#var need [@jingli_max*2-@jingli+1]
                #var need [(@need*100+129)/@sudu]
                #math dazuo @jingshen-(@jingshen_max+9)/10]
                #math need %min(@need,@dazuo)
                #var need %min(@need,10)
                #if (@dazuo<10)
                  {恢复} {#if (@jingli_max<@jingli_aim)
                           {#if ((@neili>500)|(@fadai=0)) {tuna @need} {dazuo 500}}
                           {#var zhanghao {};#var mima {};quit}
//再加入一个if语句,判断精力最大值如果没有达到目标,就吐纳,否则清空变量zhanghao和mima的值,然后退出,其中zhanghao和mima中保存着你id的帐号和密码,这样做是为了阻止自动连线
                        }
               }
    }
#tri {本周为(*)之周}
  { #if (%1!="噩梦") {#if (%1="休息") {#var sudu 100} {#var sudu 130}}
    #if (@neili_max>=@neili_aim) {#var action 0}
    #if @action {#math need [@neili_max*2+1-@neili]
                   #if (@neili_max<1000) {#math need (@need+1)/2}
                   #var @dazuo [@qixue-(@qixue_max+9)/10]
                   #var need [(@need*100+129)/@sudu]
                   #var need %min(@dazuo,@need)
                   #var need %max(@need,10)
                   #if (@dazuo<10) {恢复} {dazuo @need}
                }
               {#var need [@jingli_max*2-@jingli+1]
                #var need [(@need*100+129)/@sudu]
                #math dazuo @jingshen-(@jingshen_max+9)/10]
                #math need %min(@need,@dazuo)
                #var need %min(@need,10)
                #if (@dazuo<10)
                  {恢复} {#if (@jingli_max<@jingli_aim)
                           {#if ((@neili>500)|(@fadai=0)) {tuna @need} {dazuo 500}}
                           {#var zhanghao {};#var mima {};quit}
   
                      }
               }
    }


步骤五:制作恢复的方法,dazuo和tuna时恢复的方法不一样,需要if语句来判断
#alias 恢复 {#if @action {#if @fadai {out;fadai} {exert recover;hp;time}}
//打坐时的恢复方法,@fadai用来指示是否脱离发呆室,分别采用发呆和吸气的方法来恢复
                {#if @fadai {out;fadai} {#if (@neili<500)
                                            {dazuo 500} {exert regenerate;hp;time}
                                         }
                 }
//吐纳的恢复方法,exert regenerate之前要判断内力是否够用
            }
#tri {你发呆了一会儿,发现自己居然精神百倍!} {#wa 1500;enter;hp;time}
#tri {你吐纳完毕,睁开双眼,站了起来。}
               {#if @fadai {hp;time} {exert regenerate;hp;time}}
//能发呆就不用内力恢复,由于吐纳之前确定内力够用,是可以吸气的
#tri {你运功完毕,深深吸了口气,站了起来。}
               {#if @fadai {hp;time} {exert recover;hp;time}}


步骤六:制作数个按钮来修改zhanghao,mima,fadai,action,jingli_aim,neili_aim按钮制作下一章再讲,制作技巧则在第八章讲到。不制作按钮也可以手动修改。
      
加入断线自动重连,手动修改一个zmud系统自带alias
#alias {atconnect} {@zhanghao;@mima;yes} {System}
#tri {欢迎来到北大侠客行!} {w;s;d;#if @fadai {out;fadai} {exert recover;exert regenerate;hp;time}}
由于网络速度的原因,用完发呆室之后的#wa 1500并不能消除busy
#tri {你的动作还没有完成,不能移动。} {#wa 1500;enter;hp;time}
另外机器人会由于某些原因中断,需要一个办法来让它重新运转。这是为了对付地痞以及网络速度等不确定因素,具体办法我会在第七章讲到。

至此机器人已经基本制作完成了。这个实例旨在抛砖引玉,完全面对新手,注释得也特别详细。
本例为了让新手能学得更多才做得复杂些,其实将未成年人和成年人混在一起并不是很好,会使机器人制作难度加大很多,看起来也很复杂。
另外在下一章中会用函数的方法将本例的计算大大简化。

基础篇至此已经结束了,后面将转到中级篇,更多的应用技巧和实例将奉献给大家,请继续保持关注,谢谢!
中级篇

第五章 buttons﹑系统变量﹑function

5.1 button

按钮(buttons)是很实用的一个功能,不光应用于半自动的机器人让游戏过程更加便捷,也用于全自动机器人,让人为修改某些变量更加简单.

按钮有3个要素:Caption,Commands(zmud721中这个要素被称为Value)和Class.其中Caption是按钮的名字,显示在按钮中间;Commands保存有一系列命令,当你点击按钮的时候,Commands中的内容就作为指令发送出去;Class是按钮所属的类,只是为了加载的时候方便。

Caption后面的颜色图标可以更改按钮的颜色,主选项卡Button States里可以选择按钮的种类;Postion/Size选项卡里的内容可以更改按钮的大小和位置;Advanced选项卡里Bitmap File可以选择一个自定义图标显示在按钮上,并且zmud721还有自带图标供选择。

按钮的种类:一共四种Push﹑Toggle﹑Separator和Multistate
Push:点击一下执行一次Commands中的命令
Toggle:分为button up和button down2种状态,分别储存有2个命令串;每次点击都会执行对应状态中储存的命令串并且从一种状态改变为另一种状态。简单点说就相当于2个按钮。
Separator:仅仅作为分隔其他按钮的工具来使用,不包含任何命令。
Multistate:一个下拉式按钮,点击之后会出现下拉列表,然后选择,不同的选择执行不同的命令。

在Buttons列表里有button的序号,#button 序号 这个命令等同于鼠标点击一次按钮,这个命令可以用于alias,variable,marco和trigger中。其他与button有关的命令有#yesno,#pr和#pick,这3个命令将在第八章中介绍。另外#button也可用于新建按钮,鉴于语法格式过于复杂,这里略过了。

按钮的功能可以说非常丰富,可以做出很漂亮的界面。
这里要推荐inspector同学的一篇文章http://telweb.pkuxkx.net/forum/thread-6501-1-3.html 小蜜蜂机器人(新手专用)

5.2 系统变量
主要介绍最实用的%ctime,%i和%random

%ctime表示你的连线时间,单位是秒

实例一:我需要机器人计算吐纳时每小时增加精力的速度。
#tri {你的精力上限增加了!!} {#add jingli_add 1;#var tuna_speed [@jingli_add*3600/%ctime];#show 每小时获得@tuna_speed点精力!}

%ctime还可用于萧峰,万安塔,胡一刀,护镖,护送和朝廷守门等任务,能接任务时由机器人自动提醒,具体在第七章中专门介绍

%i主要用于各种命令中,配合#number和#loop来使用,也用于专门针对数据库变量、record类型变量和list类型变量的命令中,这些命令主要有#loopdb,#forall等等
比如 #5 kill menggu bing %i等价于kill menggu bing 1;kill menggu bing 2;kill minggu bing 3;kill menggu bing 4;kill menggu bing 5

#number和#loop都是一次性发送数个相同的命令,区别在于变量可以用于#loop中来控制发送命令的次数,而#number中的number一定要是一个实际的阿拉伯数字

错误用法:#var number 5;#@number kill menggu bing %i
正确用法:#var number 5;#loop @number {kill menggu bing %i}
注意事项:#var number 0;#loop @number {kill menggu bing %i}实际效果是kill menggu bing 1,也就是说#loop至少会执行一次,@number中的值为0或者负值或者干脆是字符串乃至汉字,都是执行1次命令,zmud不会抱错,可以自行试验。

#loop命令在我发的领悟机器人中被用到,用来改变领悟或者练习的次数,以适用不同exp层次的id。

%random产生一个0-99的随即数字,%random(1,5)产生一个1-5的随机数,结合#case命令来发送随机命令

#case @variable {cmd1} {cmd2} {cmd3} {cmd4} {cmd5}...可以根据@variable值的不同来发送不同的命令,比如@variable值为3,第3个括号中的命令cmd3会被执行,其他命令都不执行。

另外%time和%dice有兴趣也可以看一下,举几个例子简单说下
#say %time()显示为星期三 七月 22, 2009 2:14:14 am
#say %time(c)显示为2009/7/22 2:16:12
#say %time("aaaa tttt")显示为星期三 2:26:03
%time()的括号中可以使用参数来改变显示内容,这些参数有a(星期)、d(日期)、m(月份)、y(年)、e(年)、t(时分秒)、h(小时)、n(分)和s(秒),下面给个复杂点的例子,有兴趣可以自行试验看下显示结果。
#FORALL {a|d|m|y|e|g|t|h|n|s} {#SH %i: %time(%i) | %time(%i%i) | %time(%i%i%i) | %time(%i%i%i%i)}
这个例子基本包含了所有的格式,%i依次等于a、d、m、y、e、g、t、h、n、s,%time()的括号中分别用1-4个%i来显示不同详细程度的内容。

%dice(2d6+2)显示为掷2个6面色子的和再加上2,是一个随机结果,()中用到的格式为xdy+n,表示"x个y面色子+n",相关变量有%dicemin()、%dicemax()、%diceavg()、%dicedev()。

其他系统变量还有很多,作用都不大,不感兴趣的可以跳过。下面只列举一些,太多了不可能全面的,具体可以#help 自行查找。
%action 最后一次触发所执行的命令。   
%cr 换行。  
%def 当前使用的特殊字符。  
%host 当前连线MUD的IP地址。   
%lastcom 最后被执行的命令。   
%lastcom2 倒数第二个被执行的命令。  
%lastcom3 倒数第三个被执行的命令。  
%line 从MUD中得到的最后一行文本。  
%line2 从MUD中得到的倒数第二行文本。   
%line3 从MUD中得到的倒数第三行文本。   
%port 当前连线的端口号。
%selected 当前选中的文本。可用于speed menu的制作,通过鼠标点击来发出包含选中文本的命令
%selline 当前选中的行。可用于speed menu的制作
%selword 当前选中的单词。可用于speed menu的制作
%title 当前MUD的标题。   
%trigger 最近一次被触发的行。   
%window 当前窗口的标题。

5.3 fuction(函数)

zmud允许自定义函数,命令为#function,#fu为命令的缩写形式.可以简单的把函数理解为带参数的变量.还是用实际的小例子来说明函数的用法.

实例1:#fu sum %eval(%1+%2)
这里%eval是系统自带函数,作用等于[ ],负责运算并且返回运算结果
#say @sum(100,40)显示为140
sum在这里为一个自定义函数,包含2个参数,用法相当于带参数的变量,@sum()为函数的值

实例2:#fu min %if(%1<%2,%1,%2)
这里定义了一个函数min,作用是选择2个数中较小的数
%if为系统自带的条件函数,语法为%if(表达式,true-value,false-value),如果表达式为真,返回true-value,否则返回false-value
#say @min(5,7)显示为5
#say @min(20,11)显示为11

实例3:#fu fact {%if(%1<=1,1,%1*@fact(%eval(%1-1)))}
这个例子要复杂些,用到了函数的自身嵌套,如果参数<=1,返回1,否则返回"参数*@face(参数-1)",然后再对@fact(参数-1)重复上面的条件运算,最终的结果就是"参数*(参数-1)*(参数-2)*...*1",其实就是一个阶乘运算.
#say [@fact(5)]显示为120

函数很多时候用起来很方便,使你的机器人更容易看懂,修改起来当然就更容易
比如在上一章打坐吐纳机器人中,可以定义一个函数对气血,内力,精神,精力进行计算,确定dazuo,tuna的数值
#fu need %max(%min([(%if(%eval(%4*2-%3)<1000,[(%4*2-%3)/2],[%4*2-%3])*100+129)/@sudu],[%1-(%2+9)/10]),10)
这个函数拥有4个参数,并且包含一个变量@sudu
打坐的命令是dazuo @need(@qixue,@qixue_max,@neili,@neili_max)
吐纳的命令是tuna @need(@jingshen,@jingshen_max,@jingli,@jingli_max)
可以看到比第4章更简洁明了,省去了一大堆#var,#math,#if

另外值得一提的是,zmud的命令,系统自带变量,系统自带函数相当的多,从而使得机器人的写法多种多样,相当灵活.基本上任意一个#xxx的命令都对应有一个系统已定义好了的函数或变量%xxx()
比如#if,%if;#case,%case;#eval,%eval;#null,%null;#walk,%walk;#alias,%alias;#color,%color;#additem,%additem
太多了,实在无法一一举例,如此多的命令使zmud语言变得相当丰富灵活,我也无法做到全部了解并且熟练运用每一个命令。但是毫无疑问,知道的越多,做机器人就越容易。你知道多少对这样的命令,他们的用法你都知道吗?平时做机器人的时候有用过吗?
第六章 Variable和Alias的更多应用

本章主要介绍record类型变量和list类型变量以及变量别名的自身嵌套和相互嵌套

6.1 record类型变量

实例一:
#var skill.name "玄铁剑法"
#var skill.id "xuantie-jia"
#var skill.level 310

上面3个命令将一个技能的中文名字、英文名字和等级全部保存到了一个变量@skill中
#say @skill///@skill.name///@skill.id///@skill.level
显示结果为:name玄铁剑法idxuantie-jialevel310///玄铁剑法///xuantie-jia///310

也有专门针对record类型变量的显示命令#showdb,注意没有#saydb这个命令
#showdb @skill,显示结果一共3行如下:
name: 玄铁剑法
id: xuantie-jia
level: 310

我们把@skill称为一个database record,把name、id、level称为key,而"玄铁剑法"、"xuantie-jian"、"310"分别为对应key的值

record类型的变量的优点在于变量的含义更加清楚,并且相关的信息都保存在一个变量中,对这些信息的使用更加方便.

对record类型的变量进行操作的相关命令非常多,下面通过一些例子来简单说明下

(1)#say %db(@skill,level) 完全等同于#say @skill.level

(2)#say %iskey(@skill,id)//%iskey(@skill,shuliandu)
%iskey(record,key)返回record中key的序号,如果不存在返回0
上面例子中id是第二个key,shuliandu不是@skill的key,所以显示为2//0

(3)#say %addkey(@skill,shuliandu,10002)//%iskey(@skill,shuliandu)
显示为:name玄铁剑法idxuantie-jialevel310shuliandu10002//0
%addkey可以在record中添加key和key对应的值,但是并不会改变record的内容,所以%iskey(@skill,shuliandu)的显示结果仍然为0
如果需要改变record的内容,可以用下面的3个命令中的任意一个
#addkey skill shuliandu 10002
#var skill.shuliandu 10002
#var skill %addkey(@skill,shuliandu,10002)


跟添加key类似,删除key的命令有#delkey,%delkey,用法类似,这里就不做介绍了

(4)#say %numkeys(@skill) 显示@skill中key的个数

这里介绍的一些命令已经跟DataBase有点沾边了。在zmud555和721版本中提供DB和Map,并且有非常多的命令配合DB和Map来使用。考虑到DB和Map的制作及使用讲起来需要大量篇幅,同时还要配合大量图片,并且在机器人制作过程中并非不可缺少,所以此文暂时不涉及这2方面内容。以后如有需要,可能会专门开贴来介绍DB和Map。

6.2 list类型变量

将一列信息保存在一个变量中,每个单独的信息用"|"间隔开来,每项信息称为list类型变量的item

实例一:做一个trigger来拾取游戏中掉落的随机装备

#var baobei "剑|刀|杖|鞭|斧|枪|锤|戟|匕|铠甲|盔|腰带|靴|项链|戒指|披风|袍|护腕|手套|盾"
#var baobei2 "sword|blade|staff|whip|axe|spear|hammer|halberd|dagger|armor|head|waist|boots|necklace|ring|surcoat|cloth|wrists|hands|shield"
#tri {从@dbrname身上掉了出来一*之(*)$} {get %item(@baobei2,%ismember(%1,@baobei))}
在这个例子中@baobei中保存了所有随机装备的种类,@baobei2中保存所有随机装备的id,并且注意到@baobei和@baobei2中种类和id是一一对应的
%ismember(%1,@baobei)返回%1在@baobei中的位置。比如如果掉落刀返回值为2,掉落鞭返回值为4
如果%1不在@baobei中,返回为0
%item(@baobei2,%ismember(%1,@baobei)),通过%ismember(%1,@baobei)的返回值得到掉落的随机装备的id
注:现在这个例子已经没有意义了,应该写一些触发来drop垃圾,不过这个例子中的方法还是很好的.

实例二:输入sk命令,抓取所有技能的中文名字英文名字技能等级保存到变量中

#tri {你目前所学过的技能:} {#var skname {};#var sklevel {};#var skills {}}
//将@skname,@sklevel,@skills清空
#tri {│%s(*)%s~((*)~)%s-*%s(%d)~/} {#if (%numitems(@skname)=0) {#var skname %1} {#var skname %additem(%1,@skname)};#if (%numitems(@skils)=0) {#var skills %2} {#var skills %additem(%2,@skills)};#if (%numitems(@sklevel)=0) {#var sklevel %3} {#var sklevel %additem(%3,@sklevel)}}
//%numitems()返回list类型变量中item的个数
//抓取中文名字英文名字和技能等级进行赋值,如果变量为空就直接赋值,否则作为变量的一个item添加进去
//%additem和#additem类似于前面讲过的%addkey和#addkey
//对每个技能都触发,并且抓取到的中文名字英文名字和技能等级在变量中的位置都是对应的

再定义几个函数就可以了
#fu name {%item(@skname,%ismember(%1,@skills))}
#fu level {%item(@sklevel,%ismember(%1,@skills))}
#say @name("xuantie-jian")//@level("xuantie-jian")级
显示为:玄铁剑法//310级

实例三:输入semote,抓取所有的表情命令保存到变量中

输入semote,每一行有5个表情命令,每个命令由字母和数字组成,需要用%a通配

#tri {^(%a)%s(%a)%s(%a)%s(%a)%s(%a)$} {#additem emotes %1;#additem emotes %2;#additem emotes %3;#additem emotes %4;#additem emotes %5}
//list类型变量中item的个数是没有限制的,把所有的表情都保存到@emotes中
然后你可以通过%random %disc等函数来选取随机表情使用

#say %numitems(@emotes) 显示出表情的个数

注意:举个例子来说明%additem和#additem的另一个区别
#var path {n|e|sw|se|e|u|enter boat|out|w}
#say %additem("sw",@path) 显示为n|e|sw|se|e|u|enter boat|out|w|sw
#say %additem(@path,"sw") 显示为sw|n|e|sw|se|e|u|enter boat|out|w
#say @path 显示为n|e|sw|se|e|u|enter boat|out|w
#additem path "sw";#say @path 显示为n|e|sw|se|e|u|enter boat|out|w
//如果要添加的item已经存在于list变量中,则添加无效

#additem path "enter";#say @path 显示为n|e|sw|se|e|u|enter boat|out|w|enter
简而言之,#additem不允许item重复,如果有重复则命令无效.需要添加重复的item,请用%additem

对list类型变量进行操作的命令除了上面提到的%item、%numitems、%ismember、%additem、#additem,还有#delitem、#delnitem、%delitem、%dups、%sort
#delitem list item 删除指定item,如果变量中有多个重复的item,则删除所有重复的item
%delitem(item,@list) 删除指定item,如果变量中有多个重复的item,只删除第一个的item
#delnitem list n 从@list中删除第n个item
%dups 去掉变量中重复的item,只保留第一个item
#SHOW %dups(Athos|Bertrand|Caesar|Bertrand|Caesar|Bertrand)
显示为Athos|Bertrand|Caesar
%sort 对变量中的item按照字母和数字进行排序

6.3 嵌套

zmud允许variable,alias和function互相嵌套,来完成更复杂的事情

上一章中已经介绍了一个function的嵌套
#fu fact {%if(%1<=1,1,%1*@fact(%eval(%1-1)))}
其实就是定义了一个阶乘函数,不用嵌套很难计算阶乘

实例一:定义一个函数,计算exp支持的技能等级上限

#fu maxlevel %if((%1*%1*%1/10)<%2,@maxlevel(%eval(%1+1),%2),%1)
#say @maxlevel(1,100000)//@maxlevel(100,3000000)//@maxlevel(700,50000000)
显示为100/311/794,分别为100k,3m,50m exp对应的技能等级上限

这里第一个参数的作用是保守的预估一个等级,一定要比最大技能等级低,可以减少计算次数,提高计算速度,第二个参数就是要计算的经验值

实例二:用alias来计算exp支持的技能等级上限
#alias maxlevel {#var n 1;#while ((@n*@n*@n/10)<%1) {#add n 1};#say 技能最高为@n级}
maxlevel 3000000
显示为:技能最高为312级
不使用嵌套,但是用到了#while循环语句.#while功能也很强大,但同时也有我不喜欢的缺点,所以我一般避免使用#while,后面章节中会讲到#while的缺点

#alias maxlevel {#if ((%1*%1*%1/10)<%2) {maxlevel %eval(%1+1) %2} {#say 技能最高为%1级}}
使用嵌套同样可以达到目的,你可以随意预估一个值作为%1,要计算的经验值作为%2

这几个例子中,用定义函数的方法来计算速度最快,比另外2种方法要快几倍,推荐使用

总结:个人认为,本章和下一章将要讲到的内容是所有章节中最重要的2章,可以算是从中级水平到高级水平的一个转折。机器人编写中用到record,list以及嵌套,能完成很多复杂的事情.后面的章节中将会大量的出现record,list以及嵌套.
第七章 掌控时间

7.1 #wa命令

#wa是#wait的缩写,用来延迟一个命令的执行.

用法一:延迟的具体时间以毫秒为单位指定.

比如{#wa 2000;hp}作用是2秒后发送hp这个命令

#wa后面可以指定具体数字,也可以指定一个变量用变量来控制等待的时间

前面章节中多次使用这种用法.#wa命令太重要了,如果没有#wa,任何命令都是立即发送,容易造成机器人发狂.另外有些命令执行完毕之后人物处于busy状态,用#wa命令来度过busy时间,#wa的时间设置应该保守一些,一般大于busy时间.

用法二:不指定时间,#wa单独使用

实例一:{study book for 1;hp;#wa;exert regenerate}
在zmud 462和555两个版本中,运行结果为
> study book for 1
hp
你的「基本剑法」进步了!
exert regenerate
你从身上拿出一本华山剑谱准备好好研读。
你研读了一次有关基本剑法的技巧,似乎有点心得。
> ≡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━≡
【 精神 】 110   / 130    [100%]  【 精力 】 0     / 0     (+   0)
【 气血 】 48    / 97     [ 80%]  【 内力 】 0     / 0     (+   0)
【 食物 】 0     / 300    [饥饿]  【 潜能 】 234901               
【 饮水 】 0     / 300    [饥渴]  【 经验 】 75643                 
≡━━━━━━━━━━━━━━━━━━━━━━━◎北大侠客行◎━━≡
> 你略一凝神,精神看起来好多了。
study book for 1;hp;#wa3个命令同时发送执行,然后等待命令执行的结果,但是只等待一行信息,接着发送exert regenerate这个命令。#wa后面的命令得不到hp的信息,如果想让#wa后面的命令对内力气血进行判断,数一下上面的行数,至少需要连续6次#wa,而且会被中途输出的其他信息干扰,实用性为0。使用462和555版本的可以忘记这个用法。需要用其他方法来替代这个用法,最简单的就是tell自己的id,然后用tell的信息做触发。
在zmud721中运行结果为
> study book for 1
hp
你的「基本剑法」进步了!
你从身上拿出一本华山剑谱准备好好研读。
你研读了一次有关基本剑法的技巧,似乎有点心得。
> ≡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━≡
【 精神 】 110   / 130    [100%]  【 精力 】 0     / 0     (+   0)
【 气血 】 48    / 97     [ 80%]  【 内力 】 0     / 0     (+   0)
【 食物 】 0     / 300    [饥饿]  【 潜能 】 234901               
【 饮水 】 0     / 300    [饥渴]  【 经验 】 75643                 
≡━━━━━━━━━━━━━━━━━━━━━━━◎北大侠客行◎━━≡
exert regenerate
> 你略一凝神,精神看起来好多了。
具有比较高的实用性了。不同版本的差异请多多体会。

注意事项:#wa为单线流程,多个#wa同时执行时,后面的会替代前面。
试验A:在命令行里输入#wa 10000;hp,然后输入#wa 1,会发现hp被立刻执行了
试验B:在命令行反复输入{#wa 3000;hp}6次,每次输入的间隔小于3秒,会发现hp迟迟没有执行,当你停止输入时,3秒后会同时执行6次hp
请大家自行体会,做机器人时应该尽量避免多个#wa同时执行,另外可以重新输入#wa xxx来延长或者缩短前面已经输入的#wa命令。这个虽然是#wa命令的不足之处,但是仍然可以被我们利用。

因为#wa单线流程的特点,决定了#wa只用于简单的控制时间。

7.2 tick timer
点击菜单Actions->Tick Timer来设置tick timer,有3个要素组成:Timer IntervalTimeout MarginTimeout Command

举例来说,设置Timer Interval为10,Timeout Margin为2,Timeout Command为hp,点击Start之后,zmud开始循环倒计时10秒,当计时至2秒时,执行hp这个命令。

这是另外一个控制时间的工具,最大的优点是稳定,我喜欢称它为计时器。准确地倒计时,准确地执行命令,不受到机器人的影响。缺点在于一个窗口只能设置一个tick timer,一般只用在最关键的地方。

相关命令#timer on,#timer off,#ts和#tz等。
#timer on :打开tick timer
#timer off:关闭tick timer
#ts 数字:  设置Timer Interval中的值
#tz:        重置计时器的时间,让他重新开始计时
以上4个命令被用于trigger,alias中用来对tick timer进行操作.

注意:zmud 555在加载其他机器人时,不同的时间器之间会发生干扰,tick timer的3要素都会发生改变,所以建议Timeout Command中的值统一填为timer,并且新建一个alias timer来保存计时结束需要执行的命令串。

实例二:胡一刀接任务的时间间隔为10分钟,10分钟到了的时候由机器人提醒
tick timer中Timer Interval设置为600秒,Timeout Margin设置为5秒,Timeout Command设置为{#say 可以接任务了!}
#tri {胡一刀说道:『我收到消息,听说&hydaddress有盗宝人&dbrname~(&dbr~)找到了闯王宝藏的地图} {#tz}

实例三:用#wa来实现计时器的功能,这样你就拥有相当于2个计时器了。
#alias ticktimer {#wa 10000;hp;ticktimer}
如果你在命令行输入ticktimer,恭喜你,你有了另外一个计时器,每隔10秒就执行一次hp命令。如果你不修改ticktimer这个alias,它将永远不会停止,请用#alias ticktimer {}来停止这个时间器。

实例四:达摩院领悟,内力不足时回去睡觉
#alias lw {#if @sleep {d;sd;w;n;sleep} {#wa 500;lingwu sword 100;exert regenerate;lw}}
#tri {你的内力不够。} {#var sleep 1}
这个例子也可以用tick timer来实现;zmud721中也可以直接用#wa来实现;或者用下面要讲到的#alarm来实现。zmud的灵活性使得同样的事情,可以有很多种方法完成。

zmud本身提供#while命令,但是#wa和#while混合使用时会出问题,所以我一般不用#while,而是用这种自身嵌套来实现#while的功能。

上面的2个例子都用到了alias的自身嵌套,更多的用法大家可以自己创造,在高级篇中我会大量的使用自身嵌套,使用时需要注意不要造成死循环,一定要设置一个办法让它停止;否则,唉,你的zmud已经崩溃了,重启吧!

7.3 #alarm命令(重要)

实际上#alarm是创造一个alarm类型的trigger,以时间作为触发,相关命令为#untr

命令格式为#alarm hh:mm:ss {cmds}
其中hh:mm:ss为时:分:秒,时间前面可以加上"+"或者"-",hh、mm、ss前面也可以加上*或者直接用*代替,"+" "-" "*"这3个特殊字符的含义举例说明

#alarm 2:50:00 {#say 保卫时间到了}
时间前面没有+和-,以你计算机上的时间作为触发,凌晨2点50分提示保卫时间到了

#alarm -00:00 {save}
前面加了-,以你的连线时间作为触发.时间上没有指定小时,默认为任意小时,连线时间达到整点时自动save

#alarm +01:05 {cmd}
前面加个+,则建立一个一次性trigger,触发之后自动删除.并且既不以连线时间为触发,也不以计算机时间为触发,而是从建立trigger时算起,65秒之后触发

#alarm +65 {cmd}
跟上面的#alarm +01:05 {cmd}完全相同

#alarm (2|6|10|14|18|22):50:00 {#say %eval(%i+1)点保卫时间到了}
以计算机时间作为触发,并且用"|"指定了多个小时,同时用到了(),小时被保存到了临时变量%i中。
不光能在保卫时提醒,还指出了是哪个时间点的保卫。

#alarm *10:00 {save}
没有"+""-",以计算机时间作为触发。小时未指定默认为任意,分钟指定为*10意思是10的倍数,这个trigger会在计算机时间的分钟为00、10、20、30、40、50时自动save

#alarm -2:30:* {hp}
小时和分都有具体指定,秒为任意。这是一个每秒钟触发一次的trigger,但是仅在你连线时间为2个半小时的触发。事实上小时分和秒都可以设定为任意,看下面的例子

#alarm -* {hp}
每秒钟触发一次,永远不会停止,用#untr -*删除这个trigger

建议:使用这个命令太频繁,机器人里会有很多已经触发过的alarm类型的trigger,但是这些trigger并没有删除掉,建议创建这个trigger的时候,统一取一个alarm的类名,这样就可以随时用#delclass alarm来删除所有的alarm类型的trigger.

实例五:胡一刀接任务的时间间隔为10分钟,10分钟到了的时候由机器人提醒
#tri {胡一刀说道:『我收到消息,听说&hydaddress有盗宝人&dbrname~(&dbr~)找到了闯王宝藏的地图} {#alarm +600 {#say 可以接任务了!}}

实例六:保卫时间自动上线
#alarm {2|6|10|14|18|22}:50:00 {#connect;@account.zhanghao;@account.mima}
#tri {【公告天下】保卫襄阳任务全部结束} {quit;#disconnect}
其中record类型变量@account中保存有帐号密码信息
#connect连接服务器,#disconnect从服务器断开

实例七:守门结束后退出,天亮前上线
#tri {你的经验增加了*!} {#var time [1440-%ctime];#alarm
+@time {#connect;@account.zhanghao;@account.mima};quit;#disconnect}
//经过大米用天色做触发统计游戏中1天的时间为24分钟,因此[1440-%ctime]就是晚上的时间
//在#alarm的时间中用到了变量,这也是zmud允许的一个用法;用变量来控制时间使得#alarm这个命令的作用大大增加,对时间的控制也更加灵活

实例八:多个tick timer并存
#alias tick1 {#alarm +40 {cmd1;tick1}}
#alias tick2 {#alarm +20 {cmd2;tick2}}
命令行输入tick1;tick2就建立了2个tick timer,分别是20秒计时和40秒计时
计时存在时输入tick1或者tick2可以重置计时,相当于#tz命令
#untr +40或者#untr +20可以删除这2个alarm类型的trigger,相当于#timer off

实例九:#tri {林震南说道:「你上次运镖太辛苦了,下去休息休息吧。」} {#alarm +5 {ask lin about job}}
这个例子曾在第二章中出现过,用的是#wa 5000,需要打开关闭触发来防止误触发
这里用#alarm,就不需要那么麻烦,无论如何也不会出现同时输入多个"ask lin about job"的情况
但是新的问题是:如果有人一直在林这里制造干扰,命令就发不出去了。所以还是有必须要打开关闭触发。

注一:#alarm是一个功能更为强大的命令,和#wa,tick timer一起构成了zmud控制时间的3柄利器。
注二:在wiki里诚三的一篇文章中也讲过#alarm这个命令,不过我认为他讲的"-"的作用不准确,而且时间中应该不允许出现小数,所以#alarm -0.5是永远也不会触发的。
注三(新增):在721中去测试#alarm发现wiki里诚三的文章讲的是正确的。由于alarm命令在721中与另2个版本差异较大,下面专门举几个例子说明下。

不管什么版本中,#alarm都是trigger,时间就是它的Pattern。

在721中,Pattern相同的#alarm +@time,后建立的不会替代先建立的。

#alarm -0.5 {};#alarm -10;#alarm -30{}
时间中用到了-号,并且小时和分都未指定,分别是每0.5秒,每10秒,每30秒触发一次,不以连线时间触发,跟#alarm +类似,但是触发之后不会删除。后建立的会替代更新掉先建立的。意味着不停的输入#alarm -5 {commands},commands永远不会执行。

#alarm -1:05 {};#alarm -2:30 {}
这2个命令跟zmud555中一样,以连线时间为触发,只触发一次。

最后的总结就是721中alarm命令很矛盾很复杂,使用时请尤其小心。更多的差异请自行试验。

 

第八章 提高机器人稳定性和适用面

8.1 防止发呆

实例一:跳楼机器人做起来很简单,但是从塔上跳下来之后会busy 1.5秒左右.大部分时候1.5秒以后就可以继续跳楼了,但是由于网络的原因,有时候2秒都不能保证busy结束.
如果#wa 2500;enter;do 6 up;out固然比较保险,效率却很低.
2.5秒跳一次和1.5秒跳一次从效率上来说差别太大.
#tri {你已稳稳地站在地上。} {#t+ busy;#alarm +6 {enter;do 6 up;out};#wa 1500;enter;do 6 up;out}
#tri {你的动作还没有完成,不能移动。} {#t- busy;#wa 2000;enter;do 6 u;out} {busy}

这个trigger不仅能解决busy问题,同时还能解决up的时候被系统跘住的问题,机器人最多发呆6秒就能继续工作
在#alarm +6 {enter;do 6 up;out}未触发之前,再一次建立#alarm +6 {enter;do 6 up;out},这个时间触发器会被重置,触发器中的命令暂时不会执行,所以不用担心会出现多次跳楼的情况。

在侠客岛读石壁也可以用类似办法来防止机器人中断,同时设置好自动连线,除非服务器掉线超过10分钟才可能会从侠客岛上掉下来.
类似办法同样也可以用于打坐吐纳,钓鱼,读书,领悟等各种机器人中

防止发呆的一般做法:

步骤一: 一般一个机器人正常运转时,都会循环出现各种信息,可以将这些信息做成trigger,触发命令中加入#add idle 1
步骤二: #alias Testidle {#alarm +@time {#if (@idle>0) {#say 运行情况良好;#var idle 0} {#say 机器人已经中断;let it run};testidle}}
对@idle进行定时判断,如果@idle大于1,表示运行情况良好,变量归0;否则表示机器人中断,重新运转它

8.2 自动连线

非常简单但是实用的一个办法。

zmud555和721的alias列表里都有atconnect这个alias,atconnect中的命令是空的。你可以修改这个alias里面的命令,在连接到服务器时自动执行,用这个alias来输入帐号密码非常方便。
zmud462中虽然没有atconnect这个alias,但是你可以自己新建一个atconnect,功能和555 721版一样。

注:每当连接到服务器时,atconnect里面的命令都自动执行,不需要另外的trigger或者命令来执行它。所以它不是常规的alias,你在新建常规alias的时候,也不要取名为atconnect。

尽量为你的每个机器人都添加自动连线和防止发呆功能,在双保险的情况,你的机器人连续挂几天不是什么难事。

8.3 提高适用面

一个机器人可能只适用于某个id或者某个门派,需要更改很多变量才能适用于另外的id和门派。可是从大量的variables中寻找要改的变量不仅浪费时间,而且容易漏掉一些变量。如果你不是机器人的制作者,可能都不知道要怎么改。这个时候机器人的适用面就非常窄了,而且别人用起来也不容易,不能算是一个好用的机器人。

有3个命令可以用于解决这个问题,分别是#pick,#pr,#yesno

实例二:在我发布的555版领悟机器人中第一个按钮中的命令
n=1
#pr level "你要领悟或者练习到多少级"
//弹出一个对话框,提示你输入一个数字,这个数字被赋值给level,达到了修改领悟等级的目的,并且别人可以通过“你要领悟或者练习到多少级”这句话很清楚的知道要输入什么内容
#wh @n {#pick {p:添加要领悟或者练习的武功} {o:1} {添加基本功夫:#exe @action1
#exe @action2} {添加特殊功夫:#exe @action1
#exe @action6
#if %ismember(@skill,@tsskills)=0 {#exe @action3
#exe @action4
#exe @action5}} {*添加完毕:#var n 0}}
//这个命令要复杂一些了。#pick弹出一个选项框,可以提供很多选项来选择
//{p:添加要领悟或者练习的武功}为这个选项框设定一个名字或者称为"title"
//{o:1}表示这个选项框为单选框,去掉这个则默认为复选框
//{提示信息:命令}设置这个选项框的选项,当鼠标点击选择这个选项时,命令被执行
//{*添加完毕:#var n 0}}提示信息前面加入了*,表示这个选项为默认选项
//上面#pick总共设置了3个选项,只有当选择最后一个选项时,#while的循环中止,整个命令结束,否则反复弹出选项框,用来反复添加基本功夫和特殊功夫

实例三:如果你的机器人有自动连线功能,那么制作一个按钮来修改帐号密码等信息就很必要了
#button 0 {修改帐号} {#pr account.zhanghao "你的帐号";#pr account.mima "你的密码" "*"}
这里#pr account.mima的时候,多用了一个可选设置"*",当你在对话框中输入密码时显示****,也算是一个有趣的功能吧.

实例四:万安塔简化识别码的输入
#tri {你向鹿杖客打听有关『进塔』的消息。} {#pr answer "识别码";exert powerup;answer %upper(@answer);perform busy wushi}
#alias jinta {ask ke about 进塔}
在万安塔门口输入jinta,就自动弹出一个对话框询问你识别码,对照图片在对话框中输入小写的识别码,自动回答大写的识别码,同时powerup和busy武士

#yesno "question" {yes-command} {no-command}
这个命令用来弹出一个对话框询问你一个问题,如果你点yes,那么yes-command被执行,否则no-command被执行

做机器人的时候尽量考虑到门派的差异以及帐号密码的不同,然后制作一些按钮来修改这些信息,不光自己在用不同id挂机时更加方便,别人也更容易使用你的机器人.

第九章 丰富你的机器人

本章主要介绍各种实用的小命令,让你的机器人变得更有意思

9.1 #beep #play

#beep 让zmud播放音乐,可以在菜单中找到sound进行设置,只支持wav格式
你可以下载一些wav格式的音乐或者干脆自己录制一个
任务完成之后播放一段轻松的音乐,或者遇到危险播放一个警告的音乐都是不错的选择

#play 支持wav、mid、avi格式,把你想要播放的音乐放到zmud目录下或者放到zmud的子文件夹中,然后在命令行或者trigger中用#play filename.wav命令就可以播放了.如果#play命令没有效果,可能是zmud没有找到该音乐文件,请参照下图修改

9.2 status bar

窗口最下面得一段空白的地方就是status bar,你可以在这里显示你最关心的内容,
这里给出1张截图,分别是我的胡一刀机器人和护镖机器人中状态条显示的内容


图中显示了任务计时,这个时间用#alarm每秒更新一次,完全同步显示出任务花费的时间

#st命令用来定义状态条

比如我的护镖机器人中是这样定义的:
#st {【任务:@address  伙计@huoji】【伙计位置编号@huojinum】【第@round轮:用时@time秒】【本次连线成功推车@success/@total次】}

9.3 #cap

实例:#tri {【闲聊】} {#cap chat}
把所有的聊天信息抓到另外一个窗口单独显示出来,不用担心刷屏太快看不到聊天信息
当然你也可以把你感兴趣的任何信息都抓到另一个窗口显示出来。

9.4 #co #cw #hi

#co 改变最后一行显示信息的颜色
#cw 创造一个触发,只改变指定信息的颜色,该行中其他内容的颜色不变
#hi 如果最后一行的颜色为暗色,则改变为亮色;如果为亮色,则改变为暗色


颜色的代码如下:
black   0
blue   1
green   2
cyan   3
red   4
magenta  5
brown   6
grey   7
yellow   14
white   15
bold   128

你可以直接用red,blue,yellow来指定颜色,也可以用代码来指定颜色
比如:#co red或者#co 4是一个意思

#cw 唐果 red,highlight,blink
//创造一个触发,将屏幕上唐果2个字全部用亮红显示,并且闪烁,这里使用了复合颜色

#show %ansi(green)欢迎使用%ansi("red,highlight,blink")糖果机器人!
//用到了%ansi(颜色代码),在一行中可以显示多种颜色

#LOOP 20 {#SHOWP %ansi(%if(%i\2=0,green,"blue,bold")).oOo};#SHOW
//#showp和#show类似,区别在于#showp不另起一行。最后用#show结尾,表示显示完毕,另起一行;命令中的"\"表示模运算,等同于%mod()
//这条命令显示的内容很有趣,有兴趣的不妨把它复制到你的命令行里看看,你可以用#showp和%ansi()来显示更多有趣的内容.

9.5 #mess

当我正在看网页的时候,我的遍历机器人没有找到盗宝人,可以用这个命令提醒我。我可以选择关闭机器人手动去迷宫杀盗宝人,也可以不管它,让机器人放弃这个任务。
你可以将#mess用在更多的地方,无论是提醒还是危险警告都不错。

9.6 #gag

当你在领悟或者读书的时候,刷屏太快,可以用#gag来屏蔽某些信息。需要注意的是,被屏蔽了的信息虽然看不到了,但是它还是存在的,并且机器人会通过这些信息来触发trigger,所以这个命令仅仅的作用就是让你看不到。

#tri {屏息凝神,一神守内,一神游外,意图双手各成方圆,却总是很难成功。} {#gag}
//在学左右互博时,这行字出现的太多,没有任何意思的刷屏,屏蔽掉!

9.7 #sc

当张三跑出来时,由于你的屏幕跳动太快,看不清张三找谁比武了,你可以用#cap,也可以用这个命令

#sc "张 三" 10
//显示最后10行包含张三的信息;如果去掉10,则显示所有包含张三的信息
//省去了你用滚动条到上面手动寻找的麻烦

9.8 #log

把你需要的某些信息保存到一个文本文件中,日后研究或者统计都可以

#log filename.txt
//开启log,接下来所有信息都会保存在filename.txt中
#log
//如果log已经开启,用这个命令关闭log
你可以通过反复的开启关闭来保存你感兴趣的内容。

9.9 #ig

你是否觉得同时关闭所有的trigger太麻烦,需要用#t-来关闭每个类.这时你可以用这个命令,当你需要打开时,再次输入#ig命令就可以了.

注意:用#ig来关闭触发,相当于你用鼠标点击zmud窗口右下角的手枪图标,并不会改变triggers的enable和disable状态.看下面的几个例子

#ig;#alarm +5 {#ig}
//这条命令的初衷是短暂的关闭5秒再打开,可惜#alarm +5是trigger,而所有的triggers都是关闭的,所以5秒后不会打开triggers
#ig;#wa 5000;#ig
//#wa好比泼出去的水,收不回来的.关闭5秒后能自动打开.同样你也可以用tick timer来打开.

9.10 #stw

这个的用途跟定义status bar类似,当你觉得status bar太短不够用的时候你可以用这个.当然也有其他用途.

#stw {hp;@qixue/@maxqixue  内力:@neili/@maxneli %cr 精神: @jingshen/@maxjingshen 食物:@food 饮水:@drink %cr 潜能:@pot  经验: @exp}
//如果你已经用trigger把相关信息保存到了上述变量中,这条命令会打开一个小窗口来显示你的hp有关信息
//%cr的作用是换行.

你也可以用%ansi()来选择你喜欢的颜色,例如
#stw {本次推车获得%ansi(red,high)@expgrain}
//你获得的经验需要用trigger保存到@expgrain中

#stw on#stw off分别是打开status window窗口关闭status window窗口

9.11 #tab

此命令用来定义一些快捷命令,比如
#tab "ask lin about job"
//不论什么时候,在命令行简单的输入一个a,然后按一下键盘上的tab键,ask lin about job就完整的出现在命令行中了

9.12 #menu

这个命令让你可以用鼠标发送命令,请看下面几个有趣的用法

#menu admire {chat* admire %lower(%selword)}
#menu praise {chat* praise %lower(%selword)}
#menu goodman {chat* goodman %lower(%selword)}
//当你把上述3条命令输入到你的zmud中之后,右键点击屏幕,你会看到弹出一个菜单,比平时多了3个选项,任意选择一个,括号里面的命令就发送出去了
//当你右键点击某个玩家的id,然后任意选择一个,你就会在闲聊频道对该玩家发出表情动作了.
//如果你点击的是空白地方,执行的命令等同于chat* admire/praise/goodman
//%selword意思是你选中的单词

这个用法也可以帮助你捡东西或者卖东西,但是请不要用这个命令来kill sb,不小心点错了概不负责.

9.13 #all

当你在一个窗口中输入#all {vote unchblk sb.},你的所有窗口都会发出vote unchblk sb.命令.

9.14 窗口名:{命令}

sghjjj:tell tangguo [@qixue]
//如果你的大米的窗口名是sghjjj,那么这个命令能让你的大米把他的气血值告诉你
//值得注意的,大米tell过来的信息中,@qixue的值是sghjjj窗口中变量qixue的值,这条命令可以让你和你的大米配合的更完美.
//相当于直接在sghjjj这个窗口中输入了冒号后面的命令

你可以用#name xxx 来更改窗口名字

9.15 #gauge(新增)

命令格式#gauge number "Title" @variable @maxvariable (表达式)

用途:将2个变量以图形对比的形式表现在一个浮动的条形框内

number:定义浮动条形框的序号
"Title":定义浮动条形框的标题
@variable和@maxvariable分别为你关心的2个变量,比如气血/气血最大值,任务已花费时间/任务总时间等等
(表达式)可以设定一个危险警告,比如 (@maxvariable/5),那么当@variable<(@maxvariable/5)时,用红色显示@variable所占百分比,否则用蓝色显示。

实例:护送任务总时间10分钟
接到任务时{#var time_left 600;#alarm -* {#add time_left -1};#gauge 1 剩余时间 @time_left 600 (60)}
//time_left初始化600秒,用一个每秒触发一次的trigger同步更新@time_left的值,创建一个浮动条形框显示任务剩余时间,当剩余时间小于1分钟时,浮动条形框的颜色变为红色
任务完成时{#untr -*;#gauge 1 off}
//任务完成后删除alarm类型的trigger并且关闭剩余时间显示


通过序号,你可以定义很多个这样的浮动条形框。你可以用下面的命令随时关闭打开某一个或者所有的浮动条形框。
#gauge 1 off    #gauge 1 on
关闭或者打开第一个浮动条形框
#gauge off      #gauge on
关闭或者打开所有的浮动条形框

这个有趣的命令也可以用来显示你的血量或者你正在领悟的技能,Have Fun!

注:类似以上一些实用小命令可能还有很多暂时想不起来,我会随时添加

到本章为止,zmud的大部分内容已经基本介绍完毕,如果你看到了这里,相信绝大多数机器人已经难不倒你了.

posted on 2011-08-17 17:15 常言笑 阅读(27915) 评论(1)  编辑  收藏

Feedback

# re: ZMUD机器人制作(上篇) 2015-11-16 16:46 老吴

很仔细啊 谢谢楼主 受教了  回复  更多评论   



只有注册用户登录后才能发表评论。


网站导航:
 

My Links

Blog Stats

常用链接

留言簿(5)

随笔分类

随笔档案

搜索

积分与排名

最新评论

阅读排行榜

评论排行榜