<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[深入浅出制作全自动Mud机器人-移动确认]]></title><description><![CDATA[<p dir="auto">在开始制作一个Mud机器人的移动模块之前，首先我们先要解决移动确认，也就是判断成功进入一个房间的问题。</p>
<p dir="auto">稍微延展点的话，就是解析当前房间的信息</p>
<ul>
<li>确定房间信息开始</li>
<li>抓取房间名和其他信息</li>
<li>抓取房间描述</li>
<li>抓取房间出口</li>
<li>抓取房间内对象</li>
<li>确定房间信息结束</li>
</ul>
<p dir="auto">本文章以我newhelljs的解析房间代码为例</p>
<p dir="auto"><a href="https://github.com/hellclient-scripts/newhelljs/blob/main/script/src/core/room.js" target="_blank" rel="noopener noreferrer nofollow ugc">代码地址</a></p>
<h2>房间信息开始</h2>
<p dir="auto">一般房间信息开始未必是一个很明确的信号。</p>
<p dir="auto">部分mud可能对房间有特殊的格式。但很多Mud单纯就是一个顶格不带特殊标点的短剧。</p>
<p dir="auto">所以我在处理房间时是分为两个部分</p>
<p dir="auto">第一是普通房间，比如</p>
<pre><code class="language-regexp">^[^，。！：.『』【】…“”？?&gt;.]{2,10}$
</code></pre>
<p dir="auto">然后这里我使用了一个Filter的概念，就是不直接抛事件，而是在进行了预处理后，符合条件时才抛事件。对应的代码是：</p>
<pre><code class="language-javascript">    App.Engine.SetFilter("core.normalroomname", function (event) {
        let words = App.History.CurrentOutput.Words
        if (words.length == 1 &amp;&amp; words[0].Color != "" &amp;&amp; words[0].Bold == true) {
            App.RaiseEvent(event)
        }
    })
</code></pre>
<p dir="auto">进行了简单的视觉判断，单色，非普通色，加粗。</p>
<p dir="auto">除了这个通用标准外，有特殊的出发可以直接抛房间名事件。</p>
<p dir="auto">在这里很明显，我是一个可能判断，并不能保证100%匹配。</p>
<p dir="auto">所以我并没有确定出现疑似房间名的信息就进入房间。</p>
<p dir="auto">出现疑似房间名我只是把已经抓取的房间描述清空，记录最后一个房间名，直到确定进入房间在把这些转化为当前房间信息。</p>
<h2>过滤干扰项</h2>
<p dir="auto">我在代码里把所有季节相关的干扰项都记录在了"data/natures.txt"文件里，并进行了排除。</p>
<p dir="auto">这是为了抓取数据做快照而作的准备。</p>
<p dir="auto">如果你不需要抓取描述，可以不做这个处理。</p>
<h2>出口信息</h2>
<p dir="auto">大部分Mud,出口信息都是比较统一，标准的，所以可以作为真正确认进入房间的信号</p>
<p dir="auto">我的代码里，就是在抓取到出口信息后，确认之前抓起的房间名和描述有效，开始抓取房间对象列表。</p>
<p dir="auto">如果你玩的Mud的代码比较复杂，这里可能有更多的处理。</p>
<h2>对象列表</h2>
<p dir="auto">我这里主要是将对象可能的几个形态都列了出来，统一插入Room对象的 Object列表中。</p>
<p dir="auto">遇到的第一个不符合条件的行，就确认房间结束。</p>
<p dir="auto">这个主要还是看wiz怎么处理，有时候还会需要做特殊的处理。</p>
<pre><code class="language-javascript">    App.BindEvent("core.onexit", App.Core.Room.OnExit)
    let matcherOnHeal = /^  (\S{2,8})正坐在地下(.+)。$/
    let matcherOnYanlian = /^  (\S{2,8})正在演练招式。$/
    let matcherOnObj = /^  ((\S+) )?(\S*[「\(].+[\)」])?(\S+)\(([^\(\)]+)\)( \[.+\])?(( &lt;.+&gt;)*)$/
    //处理得到出口之后的信息(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 &gt; 2 &amp;&amp; output.startsWith("  ") &amp;&amp; output[2] != " ") {
                    return true
                }
                //未匹配过的行代表npc和道具结束
                return event.Context.Get("core.room.onobject")
            })
        }, function (result) {
            if (result.Type != "cancel") {
                if (App.Map.Room.Name &amp;&amp; !App.Map.Room.ID) {
                    let idlist = App.Map.Data.RoomsByName[App.Map.Room.Name]
                    if (idlist &amp;&amp; idlist.length == 1) {
                        App.Map.Room.ID = idlist[0]
                    }
                }
                App.RaiseEvent(new App.Event("core.roomentry"))
            }
        })
</code></pre>
<h2>忽略信号</h2>
<p dir="auto">一般来说，会有两种情况需要忽略移动确认的信号。</p>
<p dir="auto">第一种是MUD的干扰。Mud的Wiz可能能会设置一个类似进入房间的干扰选项。这个需要在代码里进行排除，具体实现要看Mud的设置。</p>
<p dir="auto">第二种是主动的Look。</p>
<p dir="auto">我在我的移动库做了特殊处理</p>
<pre><code class="language-javascript">        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)
            }
        
</code></pre>
<p dir="auto">在Room有Keep属性时，不做彻底的清理处理，并不会触发对应的移动动作。</p>
<h2>封装事件</h2>
<p dir="auto">由于Room处理是一个很复杂的工作。</p>
<p dir="auto">所以正常情况下，有一个专门的Room处理函数进行处理，处理完毕后抛出事件通知需要后续操作的模块。</p>
<p dir="auto">这样才能最大的程度的解耦合，并保证房间处理信息的内聚。</p>
<p dir="auto">不做切割的话，很容易随着房间信息格式的调整，把整个代码搅乱。</p>
]]></description><link>https://forum.hellclient.com/topic/30/深入浅出制作全自动mud机器人-移动确认</link><generator>RSS for Node</generator><lastBuildDate>Thu, 16 Jul 2026 19:56:17 GMT</lastBuildDate><atom:link href="https://forum.hellclient.com/topic/30.rss" rel="self" type="application/rss+xml"/><pubDate>Tue, 03 Feb 2026 07:49:34 GMT</pubDate><ttl>60</ttl></channel></rss>