杰哥瞎扯淡之伪动态路线规划
更新软件doc的东西,拿出来水一贴一般来说,Mud的路线规划包括静态规划和动态规划。
这里的静态规划和动态规划的定义为
[*]静态规划:在地图上进行规划时,各个路径的移动互相独立,不会相互干扰。一般的路线规划都是这种。
[*]动态规划:在规划时,一个行动可能会影响到其他行动。比如你进入A出口,可能会关闭b和c出口。这种一般是动态迷宫的解法。
为了解决实际上地图的动态化的需求,引入了上下文(Context)的概念,通过动态的生成 静态地图 的方法,来完成实际需求。
具体来说,把地图分为固定和动态的两个部分。
固定的部分,以静态文件的形式,作为代码的一部分,既代码的固有数据。
动态的部分,由代码在运行时,根据实际的ID和服务器状态进行实时生成。
在每次路线规划时,同时使用固定部分和动态部分生成当前的静态地区信息。
在这里,我将固定部分成为地图信息,而动态部分,称为上下文。
上下文包含的内容包含
[*] Tags 标签,字符串>数字对,匹配 出口中的出口条件,动态的开关出口。
[*] RoomConditions 房间条件,与Tags想法,匹配房间的标签,动态的开关房间。
[*] Rooms 房间列表,额外的临时房间,可以存放个人房屋/迷宫等非通用的房间。
[*] Whitelist 白名单,限制只在指定的房间中规划,较少使用,一般可以使用房间条件进行限制。
[*] Blacklist 黑名单,临时拉黑某些房间,可以应对临时变危险的房间。
[*] Shortcuts 捷径,一般是个人专属的可以飞行到某些地点的指令。
[*] Paths 临时路径,一般用来将临时房间与固定房间相连,也可以在特殊的情况下使用。
[*] BlockedLinks 禁用路径,一般用于被npc拦路或者临时关闭的情况下,临时禁止某两个房间的之间的路径参与规划。
[*] CommandCosts 临时指令消耗,临时调整某些指令的消耗,变相实现路径消耗的动态化。理论上解决实现特殊门派的特殊指令和其他门派的消耗不同的情况。
本质上,上下包含的内容,就是临时加入/开关/修改固定地图中现有的信息,对固定地图进行微调。
实际案例
1.门派专有路径
首先,我定义了一个family.txt,里面有门派的信息,节选如下
桃花岛|1990|huang yaoshi|1995|1994|th
关外胡家|2355|hu fei|2364,1929|1243,1927|hu
雪山寺|2139|jiumo zhi|2364|2140|xs
神龙教|41|pang toutuo|2572|40|sld
很明显,第6个就是门派的tag
在初始化时
let fam = App.Core.NPC.Family]
if (fam) {
Note("引入门派设置")
App.Params.LocSleep = fam.LocSleep
App.Params.MasterID = fam.MasterID
App.Params.LocMaster = fam.LocMaster
App.Params.LocDazuo = fam.LocDazuo
App.Params.IDPass = fam.IDPass
}
会根据score中取得的门派,获取这个IDPass
在维护临时信息的钩子里
//根据门派调整移动信息
App.Map.AppendTagsIniter((map) => {
if (App.Params.IDPass) {
App.Params.IDPass.split(",").forEach(val => {
map.SetTag(val.trim(), 1)
})
}
})
这样,在遇到地图有门派信息时
就能限定只有某个门派(比如丐帮)能走这个路径了。
同理也可以通过性别,轻功,经验等临时开关出口。
继续,个人房间。
一些mud(包括北侠)里,可以有个人自建房屋。但是每个人的自建房屋位置可能不同,不同的ID可能有的有房子,有的每房子,怎么办呢。
这时候我就通过临时房间和临时出口来解决这个问题。
我在机器里设置了一个叫house的变量,有房子的可以按一定的格式填入房屋信息。
然后机器里会读取这个变量并分析
var data = line.split(" ")
if (data.length != 3) {
world.Note("解析房屋信息失败,格式应该为 '包子铺 bzp 1558' ")
return
}
var hosuename = data
var houesid = data
var houseloc = data
再获取到房间信息后,会动态的生成对应的房子信息
var hosuename = data
var houesid = data
var houseloc = data
App.Mapper.HomeRooms = [
App.Mapper.NewRoom("1933", `${hosuename}大院`, [
App.Mapper.NewExit("n", "1934"),
App.Mapper.NewExit("out", houseloc),
]),
App.Mapper.NewRoom("1934", `${hosuename}前庭`, [
App.Mapper.NewExit("e", "1936"),
App.Mapper.NewExit("push、n。", "1937"),
App.Mapper.NewExit("s", "1933"),
App.Mapper.NewExit("w", "1935"),
]),
App.Mapper.NewRoom("1935", `右卫舍`, [
App.Mapper.NewExit("e", "1934"),
]),
App.Mapper.NewRoom("1936", `左卫舍`, [
App.Mapper.NewExit("w", "1934"),
]),
App.Mapper.NewRoom("1937", `走道`, [
App.Mapper.NewExit("n", "1938"),
App.Mapper.NewExit("push、s。", "1934"),
]),
App.Mapper.NewRoom("1938", `${hosuename}迎客厅`, [
App.Mapper.NewExit("n", "1939"),
App.Mapper.NewExit("s", "1937"),
App.Mapper.NewExit("open door、e", "2533"),
]),
App.Mapper.NewRoom("1939", `议事厅`, [
App.Mapper.NewExit("e", "1941"),
App.Mapper.NewExit("n", "1942"),
App.Mapper.NewExit("s", "1938"),
App.Mapper.NewExit("w", "1940"),
]),
App.Mapper.NewRoom("1940", `${hosuename}武厅`, [
App.Mapper.NewExit("e", "1939"),
]),
App.Mapper.NewRoom("1941", `${hosuename}武厅`, [
App.Mapper.NewExit("w", "1939"),
]),
App.Mapper.NewRoom("1942", `${hosuename}中庭`, [
App.Mapper.NewExit("open west、w", "1943"),
App.Mapper.NewExit("n", "1944"),
App.Mapper.NewExit("s", "1939"),
]),
App.Mapper.NewRoom("1943", `左厢房`, [
App.Mapper.NewExit("e", "1942"),
]),
App.Mapper.NewRoom("1944", `后院`, [
App.Mapper.NewExit("e", "-1"),
App.Mapper.NewExit("n", "1947"),
App.Mapper.NewExit("s", "1942"),
App.Mapper.NewExit("w", "1945"),
]),
App.Mapper.NewRoom("1945", `厨房`, [
App.Mapper.NewExit("e", "1944"),
]),
App.Mapper.NewRoom("1946", `备用`, [
App.Mapper.NewExit("e。", "1949"),
]),
App.Mapper.NewRoom("1947", `后花园`, [
App.Mapper.NewExit("e", "1948"),
App.Mapper.NewExit("s", "1944"),
App.Mapper.NewExit("open door、w、close door", "2681"),
]),
App.Mapper.NewRoom("1948", `竹林`, [
App.Mapper.NewExit("e", "1949"),
App.Mapper.NewExit("w", "1947"),
]),
App.Mapper.NewRoom("1949", `听涛阁`, [
App.Mapper.NewExit("w", "1948"),
]),
]
world.Note("在位置 " + houseloc + " 添加房屋" + hosuename + "入口[" + houesid + "]")
App.Mapper.HouseID = houesid
App.Mapper.HouseLoc = houseloc
并在每次规划房间时,加入这些临时房间和临时出入口
App.Mapper.InitTag = function (map) {
if (App.Mapper.HomeRooms.length) {
map.AddTemporaryRooms(App.Mapper.HomeRooms)
}
if (App.Mapper.Paths.length) {
App.Mapper.Paths.forEach((p) => {
map.AddTemporaryPath(p)
})
}
}
继续,NPC拦路
这个其实在北侠里特别有用,因为不光npc会拦路,大佬也可能会拦路。
首先,当然是做触发,每个mud出发不一样,我就不贴了。
然后,就是判断当前的移动的出发和结束位置。如果能获取到,就把这两个房间之间的连接屏蔽掉,再重新规划路线重新移动
if (App.Map.Move) {
if (App.Map.Room.ID) {
let step = App.Map.Move.GetLastStep()
if (step.Target) {
App.Core.Blocker.Block(App.Map.Room.ID, step.Target)
}
}
App.Map.InitTags()
App.Map.Retry()
}
屏蔽的代码是带时间加入一个大表里
App.Core.Blocker.Block = (from, to) => {
App.Core.Blocker.Blocked["form>to"] = {
Created: (new Date()).getTime(),
From: from,
To: to,
}
}
然后每次计算路径的时候,判断是否过期
//初始化地图信息时拦截拉黑的出口
App.Map.AppendTagsIniter((map) => {
for (var key in App.Core.Blocker.Blocked) {
let blocked = App.Core.Blocker.Blocked
if ((new Date()).getTime() - blocked.Created > 100000) {
delete (App.Core.Blocker.Blocked)
} else {
map.BlockPath(blocked.From, blocked.To)
}
}
})
嗯,这样本来大佬1个id就能拦住你了,这下至少得4个 {:7_279:}
记下来是固定迷宫。
我这个迷宫是每次生成有固定地图的,直接读地图
这是地图生成类
let matcherLine = /^([◎─ ●│]+)$/
//副本迷宫解析类
class Maze {
Prefix = "mazemap"
Rooms = []
Paths = []
Landmark = {}
AddRoom(x, y) {
let room = `${this.Prefix}-${x}-${y}`
this.Rooms.push(room)
return room
}
AddRoomPath(x, y, x2, y2, to, from) {
this.AddPath(`${this.Prefix}-${x}-${y}`, `${this.Prefix}-${x2}-${y2}`, to)
this.AddPath(`${this.Prefix}-${x2}-${y2}`, `${this.Prefix}-${x}-${y}`, from)
}
AddPath(from, to, command) {
let path = App.Mapper.HMM.Path.New()
path.From = from
path.To = to
path.Command = command
this.Paths.push(path)
}
Install() {
App.Core.Fuben.CurrentRooms=[]
this.Rooms.forEach((room) => {
App.Core.Fuben.CurrentRooms.push(
App.Mapper.NewRoom(room,"")
)
})
}
Destory() {
}
读地图的代码,解析地图并添加房间/路径
task.AddTrigger(matcherLine, (tri, result) => {
task.Data = "ok"
let data = result
data = data.replaceAll("", " ")
if (linenum % 2 == 0) {
let x = 0
for (var i = 0; i < data.length; i = i + 2) {
let room = App.Core.Fuben.Current.AddRoom(x, y)
if (data == "●") {
let newline = new line.Line()
App.History.CurrentOutput.Words.forEach((w) => {
newline.AppendWord(w.CopyStyle(w.Text.replaceAll("", " ")))
})
let l = newline.Slice(i, 1)
switch (l.Words.Color) {
case "Green":
App.Core.Fuben.Current.Landmark["entry"] = room
break
case "Magenta":
App.Core.Fuben.Current.Landmark["exit"] = room
break
}
}
if (i > 0 && data == "─") {
App.Core.Fuben.Current.AddRoomPath(x - 1, y, x, y, "e", "w")
}
x++
}
y++
} else {
for (var i = 0; i < data.length; i = i + 2) {
let x = 0
for (var i = 0; i < data.length; i = i + 2) {
if (data == "│") {
App.Core.Fuben.Current.AddRoomPath(x, y - 1, x, y, "s", "n")
}
x++
}
}
}
linenum++
return true
})
然后添加出入口
Qinling.AddApth = () => {
App.Core.Fuben.Current.AddPath("2820", App.Core.Fuben.Current.Landmark["entry"], "s")
App.Core.Fuben.Current.AddPath(App.Core.Fuben.Current.Landmark["entry"], "2820", "n")
App.Core.Fuben.Current.AddPath("2823", App.Core.Fuben.Current.Landmark["exit"], "n")
App.Core.Fuben.Current.AddPath(App.Core.Fuben.Current.Landmark["exit"], "2823", "s")
}
然后规划地图的时候把这些代码都规划进去
App.Core.Fuben.OnInitTags = (map) => {
if (App.Core.Fuben.Current) {
App.Core.Fuben.Current.Paths.forEach((p) => {
map.AddTemporaryPath(p)
})
map.AddTemporaryRooms(App.Core.Fuben.CurrentRooms)
}
}
就直接能用标准的路径规划来规划迷宫了。
再来个飞行的demo
我这个mud里,有宠物的情况下,可以在室外飞行到固定的几个地点
所以我建立了很多Shortcut
没有出发点,限制房间是室外,限制环境条件是ride
然后给室外的房间打上标记
再加上几个出发维护ride标签
//响应没有马的状况
App.Engine.SetFilter("core.ride.nohorse", function (event) {
var cmd = GetVariable("cmd_ride") || ""
cmd = cmd.trim()
if ((Mode == 0 || canretry()) && cmd) {
Mode = 1
App.Send(cmd + ";whistle;" + cmd)
App.RaiseEvent(event)
return
}
LastTry = (new Date()).getTime()
Mode = 2
App.Map.InitTags()
App.RaiseEvent(event)
})
//响应临时不骑马的状态
App.Engine.SetFilter("core.ride.later", function (event) {
LastTry = (new Date()).getTime()
Mode = 2
App.RaiseEvent(event)
})
规划的时候再加入ride的计算
//判断是否可以raid
ridable = function () {
var cmd = GetVariable("cmd_ride") || ""
cmd = cmd.trim()
if (cmd) {
if (Mode == 0 || Mode == 1) {
return 1
}
if (canretry()) {
Mode = 0
return 1
}
}
return 0
}
let canretry = () => {
return (new Date()).getTime() - LastTry > 5 * 1000
}
App.Map.AppendTagsIniter(function () {
App.Map.SetTag("ride", ridable())
})
就无痛无感的加入飞行的功能了。
就到这里,就到这里
打完收工
页:
[1]