北大侠客行MUD论坛

 找回密码
 注册
搜索
热搜: 新手 wiki 升级
查看: 67|回复: 7

杰哥瞎扯淡之伪动态路线规划

[复制链接]
发表于 2025-12-1 16:15:00 | 显示全部楼层 |阅读模式
更新软件doc的东西,拿出来水一贴

一般来说,Mud的路线规划包括静态规划和动态规划。

这里的静态规划和动态规划的定义为

  • 静态规划:在地图上进行规划时,各个路径的移动互相独立,不会相互干扰。一般的路线规划都是这种。
  • 动态规划:在规划时,一个行动可能会影响到其他行动。比如你进入A出口,可能会关闭b和c出口。这种一般是动态迷宫的解法。


为了解决实际上地图的动态化的需求,引入了上下文(Context)的概念,通过动态的生成 静态地图 的方法,来完成实际需求。

具体来说,把地图分为固定和动态的两个部分。

固定的部分,以静态文件的形式,作为代码的一部分,既代码的固有数据。

动态的部分,由代码在运行时,根据实际的ID和服务器状态进行实时生成。

在每次路线规划时,同时使用固定部分和动态部分生成当前的静态地区信息。

在这里,我将固定部分成为地图信息,而动态部分,称为上下文。

北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-12-1 16:16:03 | 显示全部楼层
上下文包含的内容包含

  • Tags 标签,字符串>数字对,匹配 出口中的出口条件,动态的开关出口。
  • RoomConditions 房间条件,与Tags想法,匹配房间的标签,动态的开关房间。
  • Rooms 房间列表,额外的临时房间,可以存放个人房屋/迷宫等非通用的房间。
  • Whitelist 白名单,限制只在指定的房间中规划,较少使用,一般可以使用房间条件进行限制。
  • Blacklist 黑名单,临时拉黑某些房间,可以应对临时变危险的房间。
  • Shortcuts 捷径,一般是个人专属的可以飞行到某些地点的指令。
  • Paths 临时路径,一般用来将临时房间与固定房间相连,也可以在特殊的情况下使用。
  • BlockedLinks 禁用路径,一般用于被npc拦路或者临时关闭的情况下,临时禁止某两个房间的之间的路径参与规划。
  • CommandCosts 临时指令消耗,临时调整某些指令的消耗,变相实现路径消耗的动态化。理论上解决实现特殊门派的特殊指令和其他门派的消耗不同的情况。


本质上,上下包含的内容,就是临时加入/开关/修改固定地图中现有的信息,对固定地图进行微调。

北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-12-1 16:25:31 | 显示全部楼层
实际案例

1.门派专有路径

首先,我定义了一个family.txt,里面有门派的信息,节选如下

  1. 桃花岛|1990|huang yaoshi|1995|1994|th
  2. 关外胡家|2355|hu fei|2364,1929|1243,1927|hu
  3. 雪山寺|2139|jiumo zhi|2364|2140|xs
  4. 神龙教|41|pang toutuo|2572|40|sld
复制代码


很明显,第6个就是门派的tag

在初始化时

  1.         let fam = App.Core.NPC.Family[App.Data.Player.Score["门派"]]
  2.         if (fam) {
  3.             Note("引入门派设置")
  4.             App.Params.LocSleep = fam.LocSleep
  5.             App.Params.MasterID = fam.MasterID
  6.             App.Params.LocMaster = fam.LocMaster
  7.             App.Params.LocDazuo = fam.LocDazuo
  8.             App.Params.IDPass = fam.IDPass
  9.         }
复制代码


会根据score中取得的门派,获取这个IDPass

在维护临时信息的钩子里

  1.     //根据门派调整移动信息
  2.     App.Map.AppendTagsIniter((map) => {
  3.         if (App.Params.IDPass) {
  4.             App.Params.IDPass.split(",").forEach(val => {
  5.                 map.SetTag(val.trim(), 1)
  6.             })

  7.         }
  8.     })
复制代码


这样,在遇到地图有门派信息时


就能限定只有某个门派(比如丐帮)能走这个路径了。

同理也可以通过性别,轻功,经验等临时开关出口。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-12-1 16:30:20 | 显示全部楼层
继续,个人房间。

一些mud(包括北侠)里,可以有个人自建房屋。但是每个人的自建房屋位置可能不同,不同的ID可能有的有房子,有的每房子,怎么办呢。

这时候我就通过临时房间和临时出口来解决这个问题。

我在机器里设置了一个叫house的变量,有房子的可以按一定的格式填入房屋信息。

然后机器里会读取这个变量并分析

  1.             var data = line.split(" ")
  2.             if (data.length != 3) {
  3.                 world.Note("解析房屋信息失败,格式应该为 '包子铺 bzp 1558' ")
  4.                 return
  5.             }
  6.             var hosuename = data[0]
  7.             var houesid = data[1]
  8.             var houseloc = data[2]
复制代码


再获取到房间信息后,会动态的生成对应的房子信息

  1.   var hosuename = data[0]
  2.             var houesid = data[1]
  3.             var houseloc = data[2]
  4.             App.Mapper.HomeRooms = [
  5.                 App.Mapper.NewRoom("1933", `${hosuename}大院`, [
  6.                     App.Mapper.NewExit("n", "1934"),
  7.                     App.Mapper.NewExit("out", houseloc),
  8.                 ]),
  9.                 App.Mapper.NewRoom("1934", `${hosuename}前庭`, [
  10.                     App.Mapper.NewExit("e", "1936"),
  11.                     App.Mapper.NewExit("push、n。", "1937"),
  12.                     App.Mapper.NewExit("s", "1933"),
  13.                     App.Mapper.NewExit("w", "1935"),
  14.                 ]),
  15.                 App.Mapper.NewRoom("1935", `右卫舍`, [
  16.                     App.Mapper.NewExit("e", "1934"),
  17.                 ]),
  18.                 App.Mapper.NewRoom("1936", `左卫舍`, [
  19.                     App.Mapper.NewExit("w", "1934"),
  20.                 ]),
  21.                 App.Mapper.NewRoom("1937", `走道`, [
  22.                     App.Mapper.NewExit("n", "1938"),
  23.                     App.Mapper.NewExit("push、s。", "1934"),
  24.                 ]),
  25.                 App.Mapper.NewRoom("1938", `${hosuename}迎客厅`, [
  26.                     App.Mapper.NewExit("n", "1939"),
  27.                     App.Mapper.NewExit("s", "1937"),
  28.                     App.Mapper.NewExit("open door、e", "2533"),
  29.                 ]),
  30.                 App.Mapper.NewRoom("1939", `议事厅`, [
  31.                     App.Mapper.NewExit("e", "1941"),
  32.                     App.Mapper.NewExit("n", "1942"),
  33.                     App.Mapper.NewExit("s", "1938"),
  34.                     App.Mapper.NewExit("w", "1940"),
  35.                 ]),
  36.                 App.Mapper.NewRoom("1940", `${hosuename}武厅`, [
  37.                     App.Mapper.NewExit("e", "1939"),
  38.                 ]),
  39.                 App.Mapper.NewRoom("1941", `${hosuename}武厅`, [
  40.                     App.Mapper.NewExit("w", "1939"),
  41.                 ]),
  42.                 App.Mapper.NewRoom("1942", `${hosuename}中庭`, [
  43.                     App.Mapper.NewExit("open west、w", "1943"),
  44.                     App.Mapper.NewExit("n", "1944"),
  45.                     App.Mapper.NewExit("s", "1939"),
  46.                 ]),
  47.                 App.Mapper.NewRoom("1943", `左厢房`, [
  48.                     App.Mapper.NewExit("e", "1942"),
  49.                 ]),
  50.                 App.Mapper.NewRoom("1944", `后院`, [
  51.                     App.Mapper.NewExit("e", "-1"),
  52.                     App.Mapper.NewExit("n", "1947"),
  53.                     App.Mapper.NewExit("s", "1942"),
  54.                     App.Mapper.NewExit("w", "1945"),
  55.                 ]),
  56.                 App.Mapper.NewRoom("1945", `厨房`, [
  57.                     App.Mapper.NewExit("e", "1944"),
  58.                 ]),
  59.                 App.Mapper.NewRoom("1946", `备用`, [
  60.                     App.Mapper.NewExit("e。", "1949"),
  61.                 ]),
  62.                 App.Mapper.NewRoom("1947", `后花园`, [
  63.                     App.Mapper.NewExit("e", "1948"),
  64.                     App.Mapper.NewExit("s", "1944"),
  65.                     App.Mapper.NewExit("open door、w、close door", "2681"),
  66.                 ]),
  67.                 App.Mapper.NewRoom("1948", `竹林`, [
  68.                     App.Mapper.NewExit("e", "1949"),
  69.                     App.Mapper.NewExit("w", "1947"),
  70.                 ]),
  71.                 App.Mapper.NewRoom("1949", `听涛阁`, [
  72.                     App.Mapper.NewExit("w", "1948"),
  73.                 ]),
  74.             ]
  75.             world.Note("在位置 " + houseloc + " 添加房屋" + hosuename + "入口[" + houesid + "]")
  76.             App.Mapper.HouseID = houesid
  77.             App.Mapper.HouseLoc = houseloc
复制代码


并在每次规划房间时,加入这些临时房间和临时出入口

  1.     App.Mapper.InitTag = function (map) {
  2.         if (App.Mapper.HomeRooms.length) {
  3.             map.AddTemporaryRooms(App.Mapper.HomeRooms)
  4.         }
  5.         if (App.Mapper.Paths.length) {
  6.             App.Mapper.Paths.forEach((p) => {
  7.                 map.AddTemporaryPath(p)
  8.             })
  9.         }
  10. }
复制代码


北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-12-1 16:35:32 | 显示全部楼层
继续,NPC拦路

这个其实在北侠里特别有用,因为不光npc会拦路,大佬也可能会拦路。

首先,当然是做触发,每个mud出发不一样,我就不贴了。

然后,就是判断当前的移动的出发和结束位置。如果能获取到,就把这两个房间之间的连接屏蔽掉,再重新规划路线重新移动

  1.         if (App.Map.Move) {
  2.             if (App.Map.Room.ID) {
  3.                 let step = App.Map.Move.GetLastStep()
  4.                 if (step.Target) {
  5.                     App.Core.Blocker.Block(App.Map.Room.ID, step.Target)
  6.                 }
  7.             }
  8.             App.Map.InitTags()
  9.             App.Map.Retry()
  10.         }
复制代码


屏蔽的代码是带时间加入一个大表里

  1.     App.Core.Blocker.Block = (from, to) => {
  2.         App.Core.Blocker.Blocked["form>to"] = {
  3.             Created: (new Date()).getTime(),
  4.             From: from,
  5.             To: to,
  6.         }
  7.     }
复制代码


然后每次计算路径的时候,判断是否过期

  1.     //初始化地图信息时拦截拉黑的出口
  2.     App.Map.AppendTagsIniter((map) => {
  3.         for (var key in App.Core.Blocker.Blocked) {
  4.             let blocked = App.Core.Blocker.Blocked[key]
  5.             if ((new Date()).getTime() - blocked.Created > 100000) {
  6.                 delete (App.Core.Blocker.Blocked[key])
  7.             } else {
  8.                 map.BlockPath(blocked.From, blocked.To)
  9.             }
  10.         }
  11.     })
复制代码


嗯,这样本来大佬1个id就能拦住你了,这下至少得4个
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-12-1 16:40:19 | 显示全部楼层
记下来是固定迷宫。

我这个迷宫是每次生成有固定地图的,直接读地图

这是地图生成类

  1.     let matcherLine = /^([◎─ ●│]+)$/
  2.     //副本迷宫解析类
  3.     class Maze {
  4.         Prefix = "mazemap"
  5.         Rooms = []
  6.         Paths = []
  7.         Landmark = {}
  8.         AddRoom(x, y) {
  9.             let room = `${this.Prefix}-${x}-${y}`
  10.             this.Rooms.push(room)
  11.             return room
  12.         }
  13.         AddRoomPath(x, y, x2, y2, to, from) {
  14.             this.AddPath(`${this.Prefix}-${x}-${y}`, `${this.Prefix}-${x2}-${y2}`, to)
  15.             this.AddPath(`${this.Prefix}-${x2}-${y2}`, `${this.Prefix}-${x}-${y}`, from)
  16.         }
  17.         AddPath(from, to, command) {
  18.             let path = App.Mapper.HMM.Path.New()
  19.             path.From = from
  20.             path.To = to
  21.             path.Command = command
  22.             this.Paths.push(path)
  23.         }
  24.         Install() {
  25.             App.Core.Fuben.CurrentRooms=[]
  26.             this.Rooms.forEach((room) => {
  27.                 App.Core.Fuben.CurrentRooms.push(
  28.                     App.Mapper.NewRoom(room,"")
  29.                 )
  30.             })
  31.         }
  32.         Destory() {

  33.         }
复制代码


读地图的代码,解析地图并添加房间/路径

  1.             task.AddTrigger(matcherLine, (tri, result) => {
  2.                 task.Data = "ok"
  3.                 let data = result[0]
  4.                 data = data.replaceAll("  ", " ")
  5.                 if (linenum % 2 == 0) {
  6.                     let x = 0
  7.                     for (var i = 0; i < data.length; i = i + 2) {
  8.                         let room = App.Core.Fuben.Current.AddRoom(x, y)
  9.                         if (data[i] == "●") {
  10.                             let newline = new line.Line()
  11.                             App.History.CurrentOutput.Words.forEach((w) => {
  12.                                 newline.AppendWord(w.CopyStyle(w.Text.replaceAll("  ", " ")))
  13.                             })
  14.                             let l = newline.Slice(i, 1)
  15.                             switch (l.Words[0].Color) {
  16.                                 case "Green":
  17.                                     App.Core.Fuben.Current.Landmark["entry"] = room
  18.                                     break
  19.                                 case "Magenta":
  20.                                     App.Core.Fuben.Current.Landmark["exit"] = room
  21.                                     break
  22.                             }
  23.                         }
  24.                         if (i > 0 && data[i - 1] == "─") {
  25.                             App.Core.Fuben.Current.AddRoomPath(x - 1, y, x, y, "e", "w")
  26.                         }
  27.                         x++
  28.                     }
  29.                     y++
  30.                 } else {
  31.                     for (var i = 0; i < data.length; i = i + 2) {
  32.                         let x = 0
  33.                         for (var i = 0; i < data.length; i = i + 2) {
  34.                             if (data[i] == "│") {
  35.                                 App.Core.Fuben.Current.AddRoomPath(x, y - 1, x, y, "s", "n")
  36.                             }
  37.                             x++
  38.                         }
  39.                     }
  40.                 }
  41.                 linenum++
  42.                 return true
  43.             })
复制代码


然后添加出入口

  1.     Qinling.AddApth = () => {
  2.         App.Core.Fuben.Current.AddPath("2820", App.Core.Fuben.Current.Landmark["entry"], "s")
  3.         App.Core.Fuben.Current.AddPath(App.Core.Fuben.Current.Landmark["entry"], "2820", "n")
  4.         App.Core.Fuben.Current.AddPath("2823", App.Core.Fuben.Current.Landmark["exit"], "n")
  5.         App.Core.Fuben.Current.AddPath(App.Core.Fuben.Current.Landmark["exit"], "2823", "s")
  6.     }
复制代码


然后规划地图的时候把这些代码都规划进去

  1.     App.Core.Fuben.OnInitTags = (map) => {
  2.         if (App.Core.Fuben.Current) {
  3.             App.Core.Fuben.Current.Paths.forEach((p) => {
  4.                 map.AddTemporaryPath(p)
  5.             })
  6.             map.AddTemporaryRooms(App.Core.Fuben.CurrentRooms)
  7.         }
  8.     }
复制代码



就直接能用标准的路径规划来规划迷宫了。
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-12-1 16:46:14 | 显示全部楼层
再来个飞行的demo

我这个mud里,有宠物的情况下,可以在室外飞行到固定的几个地点

所以我建立了很多Shortcut



没有出发点,限制房间是室外,限制环境条件是ride

然后给室外的房间打上标记



再加上几个出发维护ride标签

  1.     //响应没有马的状况
  2.     App.Engine.SetFilter("core.ride.nohorse", function (event) {
  3.         var cmd = GetVariable("cmd_ride") || ""
  4.         cmd = cmd.trim()
  5.         if ((Mode == 0 || canretry()) && cmd) {
  6.             Mode = 1
  7.             App.Send(cmd + ";whistle;" + cmd)
  8.             App.RaiseEvent(event)
  9.             return
  10.         }
  11.         LastTry = (new Date()).getTime()
  12.         Mode = 2
  13.         App.Map.InitTags()
  14.         App.RaiseEvent(event)
  15.     })
  16.     //响应临时不骑马的状态
  17.     App.Engine.SetFilter("core.ride.later", function (event) {
  18.         LastTry = (new Date()).getTime()
  19.         Mode = 2
  20.         App.RaiseEvent(event)
  21.     })
复制代码


规划的时候再加入ride的计算
  1.     //判断是否可以raid
  2.     ridable = function () {
  3.         var cmd = GetVariable("cmd_ride") || ""
  4.         cmd = cmd.trim()
  5.         if (cmd) {
  6.             if (Mode == 0 || Mode == 1) {
  7.                 return 1
  8.             }
  9.             if (canretry()) {
  10.                 Mode = 0
  11.                 return 1
  12.             }
  13.         }
  14.         return 0

  15.     }
  16.     let canretry = () => {
  17.         return (new Date()).getTime() - LastTry > 5 * 1000
  18.     }
  19.     App.Map.AppendTagsIniter(function () {
  20.         App.Map.SetTag("ride", ridable())
  21.     })
复制代码


就无痛无感的加入飞行的功能了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-12-1 16:47:29 | 显示全部楼层
就到这里,就到这里

打完收工
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|北大侠客行MUD ( 京ICP备16065414号-1 )

GMT+8, 2025-12-22 10:40 PM , Processed in 0.014611 second(s), 15 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表