---by Nirvana@xyj
以前看到的都是针对老xyj的解谜机器. 没什么事, 瞎写写. 解谜机器人.. 嗯嗯, 看起来很不错. 我们就开始做. 其中会用到的语法我就不说了, 全部可以在zmud help文件里找到, 所有做法基于zmud 5.55, 用db储存解谜数据库
流程, 效果:
首先众所周知, 做quest时云越多, 得的潜能越多, 所以做成缺哪朵云去做哪朵云是最好的. 我的方法是这样, 假设7个人都没死, 三清后领完任务后, 根据要的任务接着7个都做完. 然后不管这7个任务做好没做好都去领奖赏, 根据自己身上云的多少, 先领云少的, 再领云多的. 如果领奖赏失败立即根据所说的话领下一个. 用一个变量来确定任务是不是完成也行, 但是这样工作量会大很多, 因为有很多weapon, cloth, food, armor任务都是用一个alias来完成的. 整个机器的流程是这样的: 三清后一次领完7个任务--mingsi一点使自己能cast chuqiao---按"quests"截取7个任务的所要做什么和有几朵云每朵云有多少格, 然后通过zmud函数优化一会做完任务去领奖励的顺序---做完7个任务---根据刚才优化的结果依次领奖励---判断是否做完任务, 要没做完去三清, 如果7个任务都做完则直接去再问任务.
好有了想法, 下面具体开始一步步实现:
一: 建立数据库
zmud 5.55的数据库有一个bug, 用命令来切换多个数据库会导致zmud crash, 所以任何情况下, 我们都只能用一个数据库. 如果把所有因素都考虑到的话, 一个数据库已经完全可以执行所有任务了
首先建立这几个field:
Quest_cn: 任务名称库. weapon, armor, cloth, food这4个任务中在这个field里储存武器总资料, 防具等的中文名称. ask, give, kill任务在其中储存需要用到的人物总资料. Type: Text.
Quest_en: 任务英文名称. Type: Text.
Type: 任务类型. Type: Single Option(Weapon;Armor;Food;Cloth;Ask;Give;Kill).
Item_cn: 储存Give, Ask任务里需要的物品, 如送银子给路客, Item_cn里就是银子. 问如来佛取经, Item_cn里就是取经. Type: Text.
Item_en: Item_cn的英文名称. Type: Text.
Buy_id: 有的weapon, armor, cloth, food任务中买的时候是一个英文名称, 而买到手上是另外一个id, 这里专门设一个field来储存这个
Available: 用0和1判断能不能完成这个任务, 等级低时有些任务完不成(如xxt laofo, chechi daxian)可先设为0, 等等级够高时再设回1. Type: Boolean.
Action: 将要做的任务. 可以填alias或者其quest的整个路径. 随自己喜欢. Type: Text
Extra: 方便自己查询的一栏. 甚至可以用这一field做成为newbie查询quest物品位置的机器, 可加可不加, 建议加上以便以后查询资料.
Field做好, 现在得收集整个xyj quest系统的资料. 要一个个收集这的确工作量太大了. 以前看过介绍可以从源代码里 (/d/kaifeng/npc/quest_*.c)找到全部任务列表. 这的确可行, 我就是从这里把db import进来的. 不过xyj2000总站的源码不太好找, 我一直没找到哪有, 只能从旧的源代码里export/import到db里后, 把没用的任务删除, 再加上所没有的. 我的DB大概一共有1400条记录.
二: 建立trigger, Variable使任务和数据库连接
现在需要把要做的7个任务都用trigger截下来, 并截取关键字到变量里以便以后向db查询. 例如每次下命令"quests"以后看到:
你还有如下几个正在解的迷:
食物:猪八戒对你说道:老猪我四处寻求天下美味佳肴,
正要找野鹿肉板,这位壮士可否赏老猪个大脸设法子去给弄来?
送物:殷温娇一扬签对你说道:事不宜迟请去送【太乙真经】给汉钟离。
拜贤:陈光蕊燃起一根香祭祀一番,对你说道:祖灵在天,
请您去拜访一下凌空子,并且务必探明诗歌一事。。。
灭妖:胡敬德占卦完毕对你说道:去去,将蟒蛇怪杀掉!
武器:相公想想对你说道:最近有些兵器来货不足,尤其是少了黄铜禅杖,这位壮士能否帮老夫个忙?
盔甲:相婆细想了一下对你说道:魏大人托话,说是京城有人想要什么鹅黄麻纱护裙,这位壮士能否帮相府个忙?
募捐:香兰对你说道:哦,对了,姑娘想帮人找一找什么莲花灯,这位壮士能否替姑娘想个办法?
这个任务下, 我们用DoNow_Food, DoNow_Give, DoNow_Giveitem, DoNow_Ask, DoNow_Askitem, DoNow_Kill, DoNow_Weapon, DoNow_Armor, DoNow_Cloth这几个变量分别储存食物, 送物的人, 送什么东西, 拜贤的人, 问什么事, 灭妖的人, 武器, 盔甲和募捐. 有了这几个变量, 思路也就明确了. 只差把字段赋值给变量.
比如拜贤的这个任务, ask 凌空子 about 诗歌. 我们准备把"凌空子"赋值给DoNow_ask, "诗歌"赋值给"DoNow_Askitem. 由于xyj2000机器太多, wiz把领任务的那7个人说话变得很复杂, 大概有20几种说法, 也就是说陈光蕊有可能给换一种说法给你这个任务, 所以不可能用一个trigger来抓住这两个关键字. 我是这样得到全部说法的, 派个大米站到陈光蕊跟前, 肯定会有人问任务, 把所有的话都#cap下来, 半小时后应该就收集齐了, 当然如果你有源码, 可以方便一点. 收集齐后, 发现把每种说法都做成一个trigger会显得机器极庞大, 而且复杂, 这里给出一个方便点的方法, 用"|"这个语句, 可以把全部说法做成两个trigger:
First trigger:
pattern:
{求见|朝问|访问|查知|致意|问候|拜访|看望|得知有|问候|拜见}(%x){索问|查知|过问|探问|打听|探听|寻查|查询|查明|查问|探明|探访|探知|请问|告知|知道|调查|询问|打探}(%x){一事|的消息|之事|的问题|的情况}
command:
DoNow_Ask=%1
DoNow_Askitem=%2
#var DoNow_Ask %replace(@DoNow_Ask,"打探","")
#var DoNow_Ask %replace(@DoNow_Ask,"探访","")
#var DoNow_Ask %replace(@DoNow_Ask,"探听","")
#var DoNow_Ask %replace(@DoNow_Ask,"探问","")
#var DoNow_Ask %replace(@DoNow_Ask,"打听","")
#var DoNow_Ask %replace(@DoNow_Ask,"调查","")
#var DoNow_Ask %replace(@DoNow_Ask,"拜访","")
#var DoNow_Ask %replace(@DoNow_Ask,"问候","")
#var DoNow_Ask %replace(@DoNow_Ask,"一下","")
#var DoNow_Ask %replace(@DoNow_Ask,"一次","")
#var DoNow_Ask %replace(@DoNow_Ask,"一回","")
#var DoNow_Ask %replace(@DoNow_Ask,",","")
#var DoNow_Ask %replace(@DoNow_Ask,"并且","")
#var DoNow_Ask %replace(@DoNow_Ask,"并","")
#var DoNow_Ask %replace(@DoNow_Ask,"顺路","")
#var DoNow_Ask %replace(@DoNow_Ask,"一定","")
#var DoNow_Ask %replace(@DoNow_Ask,"务必","")
#var DoNow_Ask %replace(@DoNow_Ask,"尽力","")
#var DoNow_Ask %replace(@DoNow_Ask,"看望","")
#var DoNow_Ask %replace(@DoNow_Ask,"寻查","")
#var DoNow_Ask %replace(@DoNow_Ask,"拜见","")
#var DoNow_Askitem %replace(@DoNow_Askitem,"打探","")
#var DoNow_Askitem %replace(@DoNow_Askitem,"调查","")
#var DoNow_Askitem %replace(@DoNow_Askitem,"有关","")
#var DoNow_Askitem %replace(@DoNow_Askitem,"探访","")
#var DoNow_Askitem %replace(@DoNow_Askitem,"关于","")
Second trigger:
pattern:
在天祖灵让您就(%x)一事拜访(%x),请尽早动身也。
command:
DoNow_Ask=%2
DoNow_Askitem=%1
ask任务有时会叫你做问player的任务, 这需要多加个trigger, 并加一个boolean变量@Askplayer区分, 如@Askplayer=1, 则表示这是一个ask player的任务, @askplayer=0时则相反.
其他六个基本上都可以按照这种方法类推, 最关键的是得收集全7个人的每种说话内容. 需要注意的是, 在赋值这几个变量前, 都应该#Unv掉这几个变量然后再赋值, 因为有时候由于特殊原因(7个人中有人死, 小小怪挡路)没有问到任务, 这样的话没触发到trigger的变量还没保存在里面, 做任务的时候还会根据其变量做老任务, 有时还会一直循环的做这个无用的任务.
所有这些截取的trigger我把他归到"CatchQuest"这个class里面.
三: 优化领奖励的顺序(判断自己有几朵云)
下命令"quests"后, 应该会看到类似这样的图例
你解迷暂时成果统计:
食物:□
送物:□□
拜贤:□□□□
灭妖:□□□
武器:□□□□□
盔甲:
募捐:
xyj2000总站云的规则是这样实现的: 7个任务后面都带有格子, 有格子代表其有云, 格子的多少代表其云的生命力, 最多为9格, 最少没有, 表示没有云. 而且每种任务的格子数不可能重复. 如果成功做完一个任务领完赏以后其任务的格子就会恢复成9格, 而其他6个任务所拥有的格子会相应减少一格直到没有. 而且云越多, 得到的奖励越多. 所以当做完任务后去领奖时, 应先考虑领没有格的, 然后再领格少的, 直到领完, 而不是挨个按顺序领, 下面我来介绍我实现的办法:
首先设7个变量, Cloud_Food, Cloud_Give, Cloud_Ask, Cloud_Kill, Cloud_Weapon, Cloud_Armor, Cloud_cloth来准备储存7个任务的格子数. 再做trigger, 这些获取格子多少的trigger归到"CatchCloud"这个class里. 可以用%len这个函数截取有多少格子, 如" 食物:□"里有一个格子:
#tr {食物:(%x)}
{Cloud_Food=%len(%1);
#math Cloud_Food @Cloud_Food/2;
#var Cloud_Food {@Cloud_Food tofood};
}
注意"□"包括了2个string, 所以用#math把string数除以2, 才得到格子数
"#var Cloud_Food {@Cloud_Food tofood}"这句话使Cloud_Food这个变量里不单是数字, 而且有字母使之一会好排列, 这待会会讲到.
#tr {食物:$} {Cloud_Food=0;
#var Cloud_Food {0 tofood};
}
表示没有云的话把@Cloud_Food赋值为"0 tofood".
依此把另外6个做好. 这样我们得到了各自云的数目. 要将这些云按从少到多排列首先我想到了%sort函数.
#var Cloud {@Cloud_food|Cloud_Give|Cloud_Ask|Cloud_Kill|Cloud_Weapon|Cloud_Armor}
#var Cloud %sort(@Cloud)
这样, 上面图例中{1 tofood|2 togive|4 toask|3 tokill|5 toweapon|0 toarmor|0 tocloth}就将会排序成:
{0 toarmor|0 tocloth|1 tofood|2 togive|3 tokill|4 toask|5 toweapon}
再做个filter:
#var Cloud %replace(@Cloud,"0 ")
#var Cloud %replace(@Cloud,"1 ")
#var Cloud %replace(@Cloud,"2 ")
#var Cloud %replace(@Cloud,"3 ")
#var Cloud %replace(@Cloud,"4 ")
#var Cloud %replace(@Cloud,"5 ")
#var Cloud %replace(@Cloud,"6 ")
#var Cloud %replace(@Cloud,"7 ")
#var Cloud %replace(@Cloud,"8 ")
#var Cloud %replace(@Cloud,"9 ")
@Cloud就变成了{toarmor|tocloth|tofood|togive|tokill|toask|toweapon}
为云的格子多少排序就完成了. 目前只想到这个方法, 似乎有点麻烦, 有谁还有更优化的方法请告诉我. 注意每个数字后面有个空格.
当领奖励时, 可以通过做出toarmor, tocloth....7个alias, 用%item(@cloud,x)来读取, x可以是1-7这7个数字.
四: 做7个任务的trigger:
做到这里, 机器的框架已经基本完成了, 现在需要做的是为所有任务做trigger, 或alias. 首先应该去做的任务是Ask或give, 因为有些ask和give任务要去hell得cast chuqiao.
如先做Ask, 可以这样来触发, 命令"set brief doquest_ask", mud就会回应给你
设定环境变数:brief = "doquest_ask"
用这句话就可以做做ask任务的触发了. 我们再讨论这个pattern下的commands:
先加上
Do_Next=doquest_give
设定@Do_Next这个变量表明下个任务是做give, 怎么用这个变量马上会讲到.
然后要考虑到ask任务是不是一个对player的任务, 上面我们已经设了一个变量@askplayer
所以加上:
#if (@askplayer=1) {ToDo} {ToDo2}
其中ToDO里面可以是一组alias, 设定如果是ask player任务时的所要做的.
ToDo2里将要写的是一般ask任务的语句, 可以用#find向数据库里找有没有所要的资料.
#dbreset
#wa 200
#find @DoNow_Ask
#wa 200
#if (!&available or %null(%rec) or @DoNow_Ask!=%trim(&Quest_cn)) {set brief @Do_Next} {Ask_id=%trim(&quest_en)
#exec &action}
其中if语句中如果@available=0或者数据库中没有这条记录或者@DoNow_ask不等于数据库里所找到的第一条记录, 那么就做下一个任务也就是give任务. 相反的话那么新建变量@Ask_id等于其找到数据中Quest_en这个field里的值, 并且运行其field "action"里面的值. 这里解释一下action这个field, 里面可以填alias, 或者几个命令用";"分开, 但是不能用带"#"的语法比如不能用"#wa". 为了使机器简洁化, 应能在数据库写的就在数据库写, 必须得带"#"才用alias, 而且在其最后得接一个做下一个任务的命令, 比如每条action记录最后加上Done这个alias:
#wa 5000;
set brief @Do_Next
这样就连接上所有任务了.
做这些trigger将会是一个很繁琐的过程, 因为有一千多个数据库记录需要一个个输入路径, 很多任务需要额外设置trigger, alias. 不过为了以后的轻松, 现在的努力是会有回报的. :)
五: 领奖赏
做完7个任务, 然后不管有没有完成任务按照云的多少按顺序去领奖赏, 根据那7个人所说的话判断是不是失败了去下一个人那, 或者完成了去唐太宗那, 云怎么排序已经讲过了, 很容易实现.
六: 机器的优化
如果你的机器已经做到了这里, 那么机器也就差不多可以跑起来了. 但是想要完整, 还得加些内容, 比如class的管理, 机器做到这, 将会有6,70个class, 和数百的trigger. 这就需要频繁的#t+, #t-. 不然很容易的误触发, 所以每做一个trigger不是必要的话都应先disable, 甚至每个class都最好先选上Disable class when connecting to a MUD. 当要用的时候, 用相对应的alias或者trigger #t+开来, 然后用马上#t-. 我曾经就因为没管理好class以至于跑的时候老会断, 甚至好长时间找不到原因.
再就是掉枯骨洞里, 被小小怪block, 7个人有时会死掉, 防pk trigger, yalong, jindou食物, dudi momo大米等等.
呼, 写的够长的, 做出一个完整的quest机器不是件容易事, 所以spls或者现在xhg的暂时强大也并不是想当然的, 其背后的努力也不是想象中那么简单的. 所以我也非常理解前几天把我的mieyao机器删掉的原因, 那几天实在心情不佳差点把我的id全s-f了. 呵呵, 我属于婆婆妈妈的那类人, 于是就写了些这么婆婆妈妈的东西. 建议新手们以后就不要问"给我一个robot"之类的问题了, 想要, 自己做去.
mud我是不准备继续玩了, 但是我要感谢xhg所有和我奋斗过的id们, 让我好好享受了这么久mud生活. 我们不能玩一辈子mud, 但是可以做一辈子朋友(zz). :D
最后祝大家Happy mudding