深入浅出制作全自动Mud机器人-移动确认
-
在开始制作一个Mud机器人的移动模块之前,首先我们先要解决移动确认,也就是判断成功进入一个房间的问题。
稍微延展点的话,就是解析当前房间的信息
- 确定房间信息开始
- 抓取房间名和其他信息
- 抓取房间描述
- 抓取房间出口
- 抓取房间内对象
- 确定房间信息结束
本文章以我newhelljs的解析房间代码为例
房间信息开始
一般房间信息开始未必是一个很明确的信号。
部分mud可能对房间有特殊的格式。但很多Mud单纯就是一个顶格不带特殊标点的短剧。
所以我在处理房间时是分为两个部分
第一是普通房间,比如
^[^,。!:.『』【】…“”??>.]{2,10}$然后这里我使用了一个Filter的概念,就是不直接抛事件,而是在进行了预处理后,符合条件时才抛事件。对应的代码是:
App.Engine.SetFilter("core.normalroomname", function (event) { let words = App.History.CurrentOutput.Words if (words.length == 1 && words[0].Color != "" && words[0].Bold == true) { App.RaiseEvent(event) } })进行了简单的视觉判断,单色,非普通色,加粗。
除了这个通用标准外,有特殊的出发可以直接抛房间名事件。
在这里很明显,我是一个可能判断,并不能保证100%匹配。
所以我并没有确定出现疑似房间名的信息就进入房间。
出现疑似房间名我只是把已经抓取的房间描述清空,记录最后一个房间名,直到确定进入房间在把这些转化为当前房间信息。
过滤干扰项
我在代码里把所有季节相关的干扰项都记录在了"data/natures.txt"文件里,并进行了排除。
这是为了抓取数据做快照而作的准备。
如果你不需要抓取描述,可以不做这个处理。
出口信息
大部分Mud,出口信息都是比较统一,标准的,所以可以作为真正确认进入房间的信号
我的代码里,就是在抓取到出口信息后,确认之前抓起的房间名和描述有效,开始抓取房间对象列表。
如果你玩的Mud的代码比较复杂,这里可能有更多的处理。
对象列表
我这里主要是将对象可能的几个形态都列了出来,统一插入Room对象的 Object列表中。
遇到的第一个不符合条件的行,就确认房间结束。
这个主要还是看wiz怎么处理,有时候还会需要做特殊的处理。
App.BindEvent("core.onexit", App.Core.Room.OnExit) let matcherOnHeal = /^ (\S{2,8})正坐在地下(.+)。$/ let matcherOnYanlian = /^ (\S{2,8})正在演练招式。$/ let matcherOnObj = /^ ((\S+) )?(\S*[「\(].+[\)」])?(\S+)\(([^\(\)]+)\)( \[.+\])?(( <.+>)*)$/ //处理得到出口之后的信息(npc和道具列表)的计划 var PlanOnExit = new App.Plan(App.Positions.Connect, function (task) { task.AddTrigger(matcherOnObj, function (trigger, result, event) { let item = new objectModule.Object(result[4], result[5], App.History.CurrentOutput). WithParam("身份", result[2]). WithParam("外号", result[3]). WithParam("描述", result[6] || ""). WithParam("状态", result[7] || ""). WithParam("动作", "") App.Map.Room.Data.Objects.Append(item) event.Context.Set("core.room.onobject", true) return true }) task.AddTrigger(matcherOnHeal, function (trigger, result, event) { let item = new objectModule.Object(result[1], "", App.History.CurrentOutput). WithParam("动作", result[2]) App.Map.Room.Data.Objects.Append(item) event.Context.Set("core.room.onobject", true) return true }) task.AddTrigger(matcherOnYanlian, function (trigger, result, event) { let item = new objectModule.Object(result[1], "", App.History.CurrentOutput). WithParam("动作", "演练招式") App.Map.Room.Data.Objects.Append(item) event.Context.Set("core.room.onobject", true) return true }) task.AddCatcher("line", function (catcher, event) { let output = event.Data.Output if (output.length > 2 && output.startsWith(" ") && output[2] != " ") { return true } //未匹配过的行代表npc和道具结束 return event.Context.Get("core.room.onobject") }) }, function (result) { if (result.Type != "cancel") { if (App.Map.Room.Name && !App.Map.Room.ID) { let idlist = App.Map.Data.RoomsByName[App.Map.Room.Name] if (idlist && idlist.length == 1) { App.Map.Room.ID = idlist[0] } } App.RaiseEvent(new App.Event("core.roomentry")) } })忽略信号
一般来说,会有两种情况需要忽略移动确认的信号。
第一种是MUD的干扰。Mud的Wiz可能能会设置一个类似进入房间的干扰选项。这个需要在代码里进行排除,具体实现要看Mud的设置。
第二种是主动的Look。
我在我的移动库做了特殊处理
EnterNewRoom(room) { if (!room) { room = new Room() } let oroom = this.Room this.Room = room if (oroom.Keep) { if (oroom.ID) { this.Room.ID = oroom.ID } this.Room.Keeping = true } this.Room.Keep = false return this.Room } OnWalking() { if (!this.Room.Keeping) { this.Position.StartNewTerm() } if (this.Move != null) { this.Move.OnWalking(this) }在Room有Keep属性时,不做彻底的清理处理,并不会触发对应的移动动作。
封装事件
由于Room处理是一个很复杂的工作。
所以正常情况下,有一个专门的Room处理函数进行处理,处理完毕后抛出事件通知需要后续操作的模块。
这样才能最大的程度的解耦合,并保证房间处理信息的内聚。
不做切割的话,很容易随着房间信息格式的调整,把整个代码搅乱。
-
J jarlyyn 被引用 于这个主题