mudlet基础脚本开发-6、武当门派任务 - 诵经(念经)
本帖最后由 shanghua 于 2022-9-30 09:44 AM 编辑上一节,我们开发了炼丹的机器,这一节我们继续填架子。
这个教程里的机器,不过多考虑性能问题,以教会新手开发思路和基础语法为目的,我尽量把每一步的执行过程都写清楚。
念经的机器对于新手来说,有一点点的小难度,但是也不用怕,把这教程看明白了,你发现其实也没多难。
念经验机器的难点有两处:
难点一:路径问题,要能去能回。念经的路径需要自己整理,这里只做举3个例子。wiki里提供了一些常用路径。路径的录制可以用 walk add 命令,不会的请 help walk。北侠自带的路径录制命令,反正我感觉相当好用。
难点二:抓取经书内容,正则匹配很简单,关键在于怎么把内容存起来,对lua语法不熟悉的话,确实有点麻烦。
还是老样子,找之前的地方新建一个sj.lua文件,和前几节的文件放到一起,用vscode打开它,以下所有的脚本代码全都复制到这个文件里,最后把文件里的代码一起复制到mudlet的脚本里去。。
先来梳理开发思路,然后再来逐个分析每一步的实现过程:
1、先更新 jobSwitch.lua 脚本
2、接任务并记录相关内容
3、去复真观借经书
4、借到了,回到冲虚处,判断本次任务是否需要下山
4-1、不需要下山的用单向路径直接跑过去念经
4-2、需要下山的准备往返双向路径,确保自己能安全抵达并返回。
5、到地方之后,获取经书籍信息,借用了 Zmud462武当新手任务念经机器人的制作 提供的经书数据,表示感谢。
6、念完还书
7、交任务
1、更新 jobSwitch.lua 脚本
-- 武当路径
paths = {
wd = {
-- 广场 -- 后院
wdgc_hy = 'n;n;n;n',
-- 紫霄宫 -- 太子岩
zxg_tzy='eu;e;e;e;eu;eu;eu;e;e;e;eu',
-- 太子岩 -- 紫霄宫
tzy_zxg = 'wd;w;w;w;wd;wd;wd;w;w;w;wd',
-- 太子岩 -- 雷神洞
tzy_lsd = 'eu;eu;ne;nu;nw;nu;ne;eu;se;u;nu;eu;su;wu;u;eu',
-- 雷神洞 -- 太子岩
lsd_tzy = 'wd;d;ed;nd;wd;sd;d;nw;wd;sw;sd;se;sd;sw;wd;wd'
}
}
function jobSwitch(name)
disableTrigger('找老宋下山')
disableTrigger('武当山门')
disableTrigger('诵经地点')
disableTrigger('诵经')
disableTrigger('炼丹')
disableTrigger('侠客')
disableTrigger('练阵')
disableTrigger('XK-土匪挡道')
disableTrigger('XK-土匪死了')
disableScript('诵经脚本')
disableScript('炼丹脚本')
disableScript('侠客脚本')
disableScript('练阵脚本')
disableTimer('xkTimer')
xk = nil
cj = nil
zf = nil
if name~=nil then
enableTrigger(name)
enableScript(name..'脚本')
end
end
我们把要用到的路径全都做成变量,这样的好处是别名也可以用,程序里也可以用。
开始之前先设置别名,要不然机器跑不动的(我的教程全都不提供xml文件,自己动手实现才有意义):
借经书 ^wd_jjs$ nu;n;n;n;w;w;u
下山 ^xiasha$:tempTimer(2, [])
任务完成 ^cxok (.+)$ send('ask chongxu about success')
紫霄宫-太子岩 ^wd_zxg_tzy$ send(paths.wd.zxg_tzy)
太子岩-紫霄宫 ^wd_tzy_zxg$ send(paths.wd.tzy_zxg)
太子岩-雷神洞 ^wd_tzy_lsd$ send(paths.wd.tzy_lsd)
雷神洞-太子岩 ^wd_lsd_tzy$ send(paths.wd.lsd_tzy)
sj.lua脚本里定义两个变量,存目的地
-- 需要下山的地点
local areasD = {'太子岩','雷神洞'}
-- 山上的地点
local areasI = {'后院'}
2、接任务还是通过 这一节教程 接到任务,去执行 startSJ() 函数,脚本代码:
function startSJ (a,b,c,p)
sj = {
-- 地点
area = a,
-- 书名
book = b,
-- 章节
chapter = c,
-- 章节页数
page = tonumber(p),
-- 记录经书内容
content = '',
-- true是借书,false是还书
go = true,
-- 抓取章节内容时用来记录内容行数
i = 1,
-- 念经的路径,方便返回, 为-1时表示已下山
path = ''
}
print('\n地点:' ..sj.area)
print('书名:' ..sj.book)
print('章节:' ..sj.chapter)
print('页数:' ..sj.page)
expandAlias('wd_jjs', false)
end
3、去复真观借经书
触发:
^\s+复真观二层\s+$
^\s+〓\s+$
^\s+复真观一层\s+
触发代码:
jieSJ()脚本代码:
function jieSJ ()
-- sj.go 是控制借书还书的: 默认值为 true 执行借书,念经完成之后设为 false 执行还书
if (sj.go) then
tempTimer(1, [[
send('jie '..sj.book)
feedTriggers('\n我来借书\n')
)
else
tempTimer(1, [])
end
end
4、借到了,回到冲虚处,准备跑路
触发:
我来借书
知客道长递给你一本.+并对你说
借书的描述所有人都一样,如果只做一行触发的话,别人借书的话,也会执行程序。所以这里自己多加了一行触发。
触发脚本:
readySJ()脚本代码:
function readySJ()
send('d;e;e;do 3 s')
if getAreasD() then
send('n')
enableTrigger('找老宋下山')
expandAlias('xiashan', false)
send('set brief 3')
elseif getAreasI() then
goingSJ();
else
cecho('<green>------------------------------------------\n| <yellow>念经点不在现有的路径内,请补全路径信息 <green>|\n------------------------------------------\n')
end
end
函数 getAreasD :
-- 获取山下地点
function getAreasD()
for i, value in ipairs(areasD) do
if (sj.area == value) then
sj.path = -1
return value;
end
end
return false
end
函数 getAreasI :
-- 获取山上地点
function getAreasI()
for i, value in ipairs(areasI) do
if (sj.area == value) then
return value;
end
end
return false
end
4-1、从上边的逻辑中能判断出是否需要下山,我们先处理要下山的情况。
要下山?那么问题就来了。怎么样精准定位房间?其实常用的也就那么几种方法:
1、简单粗爆的,直接跑过去。缺点:路程太远的话,命令太长,不知道会发生什么,导致脚本停止运行。
2、抓取房间信息,然后跟我们事先保存的 sj.area 变量对比一下,能对的上就是到了。缺点:每走一个房间都会去匹配一下,效率可能受影响
3、通过lua去循环路径,每一次循环就发出一条行走命令,循环到最后两条命令的时候,打开抓取房间的触发。优点:比第2种方式更安全稳定,效率也高,缺点:开发成本高那么一点点,新手不好理解。
这里对于不下山的路径用第2种方式,下山的路径用第3种方式。
触发:
你向宋远桥打听有关『下山』的消息。
宋远桥点了点头。
多行触发,delta设为1触发脚本:
downHillBase()脚本代码:
function downHillBase()
if (sj.go or xk.go) then
tempTimer(1, [[
send('s;sd;')
loopPath()
disableTrigger('找老宋下山')
)
end
end
函数 loopPath:
function loopPath()
local path = '';
if (sj.area == '太子岩') then
-- split:把字符串分割成数组。 这是百度上找的一个通用的轮子
path = split(paths.wd.zxg_tzy, ';')
elseif (sj.area == '雷神洞') then
-- paths.wd 是 jobswitch 脚本中定义的变量
path = split(paths.wd.zxg_tzy..';'..paths.wd.tzy_lsd, ';')
end
-- 获取总数
local count = table.size(path);
-- 记录时间点
local last = 2;
for i, value in ipairs(path) do
-- 倒数第2步,打开触发,开始记数
if (i > count-2) then
-- 打开房间抓起触发器
enableTrigger('抓取房间');
tempTimer(last, function ()
send(value)
end)
last = last + 1.5
else
send(value);
end
end
send('set brief 0')
end
轮子函数 split(别人写好的工具类或函数,行业内称为轮子,直接拿来用就好了,没必要自己再造个轮子。):
-- 返回一个 table,类似于其他弱类型语言的数组
function split(str,delimiter)
local dLen = string.len(delimiter)
local newDeli = ''
for i=1,dLen,1 do
newDeli = newDeli .. "["..string.sub(delimiter,i,i).."]"
end
local locaStart,locaEnd = string.find(str,newDeli)
local arr = {}
local n = 1
while locaStart ~= nil
do
if locaStart>0 then
arr = string.sub(str,1,locaStart-1)
n = n + 1
end
str = string.sub(str,locaEnd+1,string.len(str))
locaStart,locaEnd = string.find(str,newDeli)
end
if str ~= nil then
arr = str
end
return arr
end
split是个工具函数,在mudlet的脚本栏目里,新建一个 "公共函数” 组,然后新建一个“字符串分隔”的脚本,把代码扔进去:
函数 loopPath 里打开了房间抓取的触发,触发器:
^(.+)\s\-\s\[(.*)\] 触发脚本(下边这1段代码直接写在mudlet里,就不上图了,都学到这了,应该能知道怎么添加。):
if sj.path and sj.path~=-1 and matches=='武当广场' then
-- 山上,去还书
expandAlias('wd_jjs')
disableTrigger('抓取房间')
else
-- 快到了,抓取房间
getRoomSJ(matches)
end
脚本函数 :
-- 房间匹配,判断是否到了
function getRoomSJ(r)
if r==sj.area then
-- 找到目的地,关闭触发,获取经书内容
disableTrigger('抓取房间');
getPageSJ()
end
end
4-2、不用下山的情况。这种情况一般路程都很近,我们直接跑过去就行了,所以我们用mudlet自带的 speedwalk 函数来做路径反转,就能实现1条路径走个来回。
在拿到经书后,程序判断我们不需要下山,执行 goingSJ 函数:
function goingSJ()
if sj.area=='后院' then
-- speedwalk(路径,是否反转,每一步的间隔时间)
speedwalk(paths.wd.wdgc_hy, false, 0.1)
-- 记录路径,返回时用到
sj.path = paths.wd.wdgc_hy
tempTimer(2, function ()
getPageSJ()
end)
end
end
两种跑路情况,都已经完成了,现在我们已经到目地的了,开始准备念经了。
5、到地方之后,获取经书籍信息
函数 getPageSJ:
function getPageSJ()
print('\n=== 获取页码 ===\n')
-- 获取页码
if (sj.book=='庄子·外篇「上卷」') then
if (sj.chapter=='骈拇') then sj.currentPage = sj.page+0 end
if (sj.chapter=='马蹄') then sj.currentPage = sj.page+3 end
if (sj.chapter=='胠箧') then sj.currentPage = sj.page+11 end
if (sj.chapter=='在宥') then sj.currentPage = sj.page+30 end
if (sj.chapter=='天地') then sj.currentPage = sj.page+65 end
if (sj.chapter=='天道') then sj.currentPage = sj.page+115 end
if (sj.chapter=='天运') then sj.currentPage = sj.page+150 end
if (sj.chapter=='刻意') then sj.currentPage = sj.page+189 end
if (sj.chapter=='缮性') then sj.currentPage = sj.page+200 end
end
if (sj.book=='庄子·外篇「下卷」') then
if (sj.chapter=='秋水') then sj.currentPage = sj.page+0 end
if (sj.chapter=='至乐') then sj.currentPage = sj.page+50 end
if (sj.chapter=='达生') then sj.currentPage = sj.page+68 end
if (sj.chapter=='山木') then sj.currentPage = sj.page+106 end
if (sj.chapter=='田子方') then sj.currentPage = sj.page+142 end
if (sj.chapter=='知北游') then sj.currentPage = sj.page+176 end
end
if (sj.book=='庄子·内篇「上卷」') then
if (sj.chapter=='逍遥游') then sj.currentPage = sj.page+0 end
if (sj.chapter=='齐物论') then sj.currentPage = sj.page+20 end
if (sj.chapter=='养生主') then sj.currentPage = sj.page+68 end
end
if (sj.book=='庄子·内篇「下卷」') then
if (sj.chapter=='人间世') then sj.currentPage = sj.page+0 end
if (sj.chapter=='德充符') then sj.currentPage = sj.page+45 end
if (sj.chapter=='大宗师') then sj.currentPage = sj.page+76 end
if (sj.chapter=='应帝王') then sj.currentPage = sj.page+123 end
end
if (sj.book=='庄子·杂篇「上卷」') then
if (sj.chapter=='庚桑楚') then sj.currentPage = sj.page+0 end
if (sj.chapter=='徐无鬼') then sj.currentPage = sj.page+41 end
if (sj.chapter=='则阳') then sj.currentPage = sj.page+97 end
if (sj.chapter=='外物') then sj.currentPage = sj.page+140 end
if (sj.chapter=='寓言') then sj.currentPage = sj.page+167 end
end
if (sj.book=='庄子·杂篇「下卷」') then
if (sj.chapter=='让王') then sj.currentPage = sj.page+0 end
if (sj.chapter=='盗跖') then sj.currentPage = sj.page+46 end
if (sj.chapter=='说剑') then sj.currentPage = sj.page+98 end
if (sj.chapter=='渔父') then sj.currentPage = sj.page+113 end
if (sj.chapter=='列御寇') then sj.currentPage = sj.page+138 end
if (sj.chapter=='天下') then sj.currentPage = sj.page+164 end
end
if (sj.book=='道德经「上卷」') then sj.currentPage = sj.page end
if (sj.book=='道德经「下卷」') then sj.currentPage = sj.page-40 end
if (sj.book=='阴符经') then sj.currentPage = sj.page end
-- 翻页
send('page '..sj.currentPage);
-- 3秒倒计时,拼接内容
cecho('\n<SandyBrown>==== 准备念经 ====\n')
tempTimer(3, [])
tempTimer(2, [])
tempTimer(1, [])
-- 念经
tempTimer(4, [])
end
上边代码,直接确定了页数,开始抓内容吧。
触发:
^==\s+(\S+)触发脚本:
-- 抓取每行的内容
getContentSJ(multimatches)获取经书内容,用的是单行触发。
因为每一行的正则模式都是一样的,所以每一行都会被触发,我们要做的事情是 依次 把每一次匹配到的内容记录下来。
注意:是依次,因为有几行内容,就会执行几次触发。而不是执行一次触发匹配所有行数。刚开始有不少人这里都理解不了,也包括我自己 0.0。
脚本代码:
function getContentSJ(c)
-- 抓取每行的内容
sj['contents'..sj.i] = c
sj.i = sj.i+1
end
页数、内容都有了,开始念吧。
函数 songSJ :
function songSJ()
-- 拼装经文
for i = 1, sj.i-1 do
sj.content = sj.content..sj['contents'..i]
end
send('chanting '..sj.currentPage..' '..sj.content)
end
6、念完还书
触发:
^你诵唱完了\S{2,5}触发脚本:
print('\n结束-回城-还书')
bookReturnSJ()脚本代码:
function bookReturnSJ()
sj.go = false
if (sj.path==-1) then
-- 回山,还书
send('set brief 3')
tempTimer(2, function ()
if (sj.area == '太子岩') then
expandAlias('wd_tzy_zxg', false)
elseif (sj.area == '雷神洞') then
expandAlias('wd_lsd_tzy', false)
expandAlias('wd_tzy_zxg', false)
end
send('nu')
send('set brief 0')
expandAlias('wd_jjs', false)
end)
else
-- 未下山,回广场
enableTrigger('抓取房间')
speedwalk(sj.path, true, 0.3)
end
end
这里打开了房间抓取的触发。因为 speedwalk 这个函数,用我能理解的表达方式,我只能说它是一个异步函数。异步是个啥意思自己百度。
由于它这种异步的机制,导致写在它下边的代码会优先执行,自己可以做个实验:speedwalk('n;n;n', false, 1) send('e') 看看它是怎么执行的。
未下山的话,走到武当广场,会执行还书的操作。
7、交任务
触发:
^你给知客道长一本.+触发脚本:
overSJ()脚本代码:
function overSJ()
sj = nil
tempTimer(1, [[
send('d;e;e;do 3 s')
expandAlias('cxok', false)
)
end
目录结构:
感谢分享,很有用,虽然我是使用mushclient来做这个诵经机器人,但是思路可以借鉴,而且那个经书的目录页数很正确,
页:
[1]