跳到主要内容

NPC 与首领巡逻

EliteMobs 可让已配置的 NPC 和自定义首领使用 Minecraft 原生导航沿作者设定的路线移动。同一套系统也向 Lua 脚本提供 walk_to、hold 和 teleport。

快速编辑流程​

  1. 生成或放置要编辑的 NPC 或自定义首领。
  2. 在 12 格内直视目标并运行 /em patrol edit。
  3. 站在每个目的地运行 /em patrol add。新路线的节点 0 位于该实体配置中的生成位置。
  4. 使用 /em patrol mode LOOP 将最后一个节点连回第一个节点,或使用 /em patrol mode REVERSE 按列表原路返回。
  5. 运行 /em patrol save 保存路线并刷新该实体。

编辑器显示节点连线和已求解步行路径的预览。/em patrol remove 删除最后一个节点,/em patrol undo 恢复之前的编辑,/em patrol status 显示编辑驱动器的状态。/em patrol cancel 结束会话而不保存路线修改。所有编辑命令只能由玩家使用,并需要 elitemobs.patrol.admin。节点必须位于原始世界;编辑器会拒绝 WorldGuard 中 elitemob-spawning 设为 deny 的区域内的节点。

新路线使用相对坐标。编辑现有路线时,编辑器会保留其 patrol.relative 设置,并以该格式保存新增的路径点。请在 YAML 配置中设置坐标格式。

如果 NPC 或区域首领文件包含多个 spawnLocations,为其中一个目标保存路线时,会为该地点建立子文件,并从原文件移除该地点。这样无论是相对路线还是绝对路线,编辑都只作用于该实体。子文件通过 extends 继承原文件。首领子文件还会复制原文件的 leashRadius、powers 和 eliteScript,因为 extends 不会继承这些键。

配置​

巡逻节点是 x,y,z 坐标。使用默认的 relative: true 时,它们是相对于实体配置生成位置的偏移。使用 relative: false 时,它们是该实体所在世界中的精确坐标,包括其当前所在的地下城实例世界。绝对节点不包含世界名称。

patrol:
enabled: true
relative: true
mode: LOOP
speed: 1.0
virtualSpeed: 0.2
nodes:
- 0,0,0
- 1000,0,0
- 1000,0,250
键值默认值说明
patrol.enabledtrue / false存在节点时为 true启用此路线。
patrol.relativetrue / falsetrue为 true 时使用相对于配置生成位置的偏移,为 false 时使用实体所在世界中的精确坐标。适用于路线中的所有节点。
patrol.modeLOOP / REVERSELOOPLOOP 将最后节点连到节点 0;REVERSE 沿列表返回。
patrol.speed正有限数1.0原生导航速度倍率。
patrol.virtualSpeed正有限数移动速度属性 × 巡逻速度持久目标没有已加载身体时,每 tick 沿缓存的已求解路径前进的方块数。通常应省略。
patrol.startNodeWaitSeconds有限数,0 或更大0实体每次回到节点 0 时在该处休息的秒数:LOOP 模式下每圈一次,REVERSE 模式下每次往返一次。在节点 0 生成的实体在第一圈开始前也会休息。休息期间它会面朝其生成时的朝向。
patrol.nodes至少两个坐标无按顺序排列的目的地,按照 patrol.relative 解释。支持 x,y,z 字符串或包含 x、y、z 的映射。坐标必须有限,相邻节点不能相同。

例如,以下路线依次经过三个精确位置,然后循环回到第一个位置:

patrol:
enabled: true
relative: false
mode: LOOP
nodes:
- 299,91,215
- 286,91,213
- 279,91,230

没有 patrol.relative 的现有路线会继续使用偏移。更改该标志会改变所有节点的解释方式,但不会转换已存储的数值。更改现有路线的格式时,请同时转换节点。

系统没有公开的单段距离上限,1,000 格的路段也是有效的。旧实验键 patrol.maxLegDistance 已废弃且不起作用。

保存时,编辑器会保留路线的导航速度、显式设置的 virtualSpeed 以及起始节点休息设置。解析超过两个节点的路线时,如果最后一个节点重复了首节点,会自动删除该末尾节点。

NPC 接近玩家时暂停​

NPC 还支持 patrol.pauseNearPlayersRadius(默认 0,禁用)和 patrol.faceNearbyPlayers(默认 false)。设置正有限半径后,NPC 会在接近存活且非旁观模式的玩家时暂停;朝向选项让暂停的 NPC 看向最近的有效玩家。距离每 20 tick 检查一次。无效或负半径会禁用这种暂停。

这些选项独立于对话和 Lua 手动巡逻暂停。它们是 NPC 选项,不是首领巡逻的额外字段。

起始节点休息​

设置 patrol.startNodeWaitSeconds 后,实体会在开始下一圈之前停在节点 0。实体未加载时,休息时间也会继续倒计时。接近玩家或战斗导致的暂停会使倒计时停止,直到暂停结束。walk_to、hold、teleport 和 patrol_resume() 会立即结束休息。公会的战斗教官使用 60 秒的休息时间,方便玩家在两圈之间到他的椅子旁找到他。

长距离寻路​

长路段会自动求解。EliteMobs 让 MagmaCore 在当前已加载地形中滚动规划路线,把它拆成原生导航可处理的短目标,并随实体前进继续重新规划。

地形访问与路线搜索分开处理:

  • 已加载区块的快照通过 MagmaCore 共享的 tick 预算工作负载系统在服务器线程中获取。
  • 有界 A* 搜索在这些不可变快照上异步执行。
  • 最终的短路段仍由 Minecraft 原生寻路器验证并行走。
  • 规划不会强制加载区块。缺少地形时会等待并重试。没有身体的持久目标只能沿缓存的已求解路径前进,路径用尽后等待。其身体只能在进行实体 tick 的区域恢复。

这样便能穿越整座城市,而无需在一个 tick 内扫描全部地形,也不会沿直线穿过未加载的建筑。飞行和水生生物使用体积搜索;地面生物要求有支撑和足够空间,并避开常见危险方块。

实体行为​

  • NPC 巡逻会自动启用身体 AI 和自定义模型移动同步。syncMovement 仍控制不巡逻的模型,但它不是第二个巡逻开关。
  • 自定义首领必须使用 ai: true、具有正的移动速度,并且不能是其他实体的乘客。
  • 首领进入战斗时暂停巡逻,战斗结束后恢复。
  • 非实例化的 NPC 和非实例化的持久首领会在区块卸载和重载期间把安全路线位置保存在 patrol-state.yml。当路线的节点或 relative 标志发生变化时,已保存的位置会被丢弃,实体从节点 0 重新开始。
  • 目标只会在进行实体 tick 的区域恢复身体。没有身体时,保存的位置仍可沿先前已求解的路径前进,但不会在未知地形中寻找新路线。
  • 乘客不能拥有巡逻路线;请为其坐骑设置路线。

Lua 控制​

context.npc 和 context.boss 提供以下方法,其巡逻操作需要已配置的控制器。无论 patrol.relative 如何设置,Lua 坐标始终是相对于实体配置生成位置的偏移,各方法返回请求是否被接受。walk_to、hold 和 teleport 还需要存活的身体。步行请求被接受不代表已经到达:战斗、接近玩家时的暂停、关闭 AI 或寻路失败都可能中断移动。

方法行为
patrol_pause()停止当前路线但保留配置。
patrol_resume()取消脚本等待或临时移动并恢复巡逻。
walk_to(x, y, z)走到偏移位置,然后恢复巡逻。
hold(x, y, z)走到偏移位置并停留,直到调用 patrol_resume()。
teleport(x, y, z)当同一世界内的目标区域进行实体 tick 且 Bukkit 接受传送时,将存活的身体传送到偏移位置。不会清除已有的脚本暂停或临时目标;使用 patrol_resume() 返回路线。
return {
api_version = 1,
on_npc_interact = function(context)
context.npc:walk_to(40, 0, -12)
end
}

walk_to 使用相同的长距离求解器,无需手动拆分中间节点。

诊断​

在编辑会话之外,直视目标并运行 /em patrol status。输出包含运行状态、当前与目标节点、路线进度、逻辑位置、虚拟速度、身体是否存在以及暂停原因。start_node_wait 表示正处于起始节点休息。terrain_unavailable 表示无身体移动已没有可用的已求解路径;已加载目标等待规划时可能显示 yielded。逻辑位置代表路线进度,可能不同于用于恢复身体的已保存安全位置。

普通路线失败后,巡逻控制器会重试一次,再前往下一个节点。失败的临时步行或停留移动也会获得一次控制器重试,但这并不证明已到达目标。如果目标跳过节点或一直停留,请检查地形和原生导航。