Skip to content

流程与触发器 ​

flow.yml 是副本的"玩法图纸": 房间怎么推进, 怪分几波, 门什么时候开, 玩家做选择后走哪条路, 全写在这个文件里. 它位于 plugins/EmakiDungeon/world/<副本ID>/flow.yml, 缺失时插件按内置模板自动生成. 模块 ID 在副本内必须唯一, 加载期会校验重复与越界定义; 加载失败时该实例回退为空流程, 你可以用 /edg admin validate 提前体检全部模板

绝大多数人不需要手写它: 用 地图编辑器 在游戏里放标记, 点保存就会写进 flow.yml. 生成出来的结构, 以及怎么用条件和动作把它们串起来, 说明如下

文件骨架 ​

内置模板长这样 (真实文件节选):

yaml
version: "1.0.0"

rooms: {}

入口点约定: 新建副本一律不要在 flow.yml 写 spawn_point 覆盖节 (内置默认模板已移除该节), 入本点统一回退到世界出生点. 你要用 /setworldspawn 把模板世界出生点设置到净空落点, 并保证模板 level.dat 中的出生点有效 (服务端加载时会校验存储的出生点, 无效则会重新选定别的位置). 模板导出后核对 flow 无该节, 再用一次真实进入核对实际落点. 依赖入口位置的区域模块 (如 entry_timer 的 region) 必须覆盖实际落点. spawn_point 仍可作为显式覆盖项使用, 写入后入本/重生/结算落点全部以它为准

每个模块都是"节名 → { 模块 ID → 模块内容 }"的两层结构, 模块内容里带自己的 id 与 enabled 开关, 例如实测副本里的一条房间计时:

yaml
room_timers:
  entry_timer:
    id: entry_timer
    enabled: true
    region:
      min:
        x: -20.0
        y: 55.0
        z: -20.0
      max:
        x: 20.0
        y: 80.0
        z: 20.0
    seconds: 15
    on_zero:
      type: complete
      cause: timed

这条的含义: 玩家进入该区域后开始 15 秒倒计时, 到零执行 complete 动作结束本局. 区域 (region) 一律用 min / max 两个对角坐标表示

功能模块清单 (22 类) ​

节名作用关键内容
spawn_point进入副本的落点location (x / y / z / yaw)
rooms房间拓扑 (列表)id, order 顺序, region 区域, arrival 到达点
checkpoints检查点region, 点位, min_players 最少人数, record_id
respawn_points重生点点位, 关联的 checkpoint
exit_points出口点位, need_confirm 退出前需二次确认
gates门禁door_regions 封门方块, conditions, logic (AND/OR), open_mode 开门方式, redstone 红石点
spawn_points刷怪点spawn_id, 点位, range 活动范围, min_players, once 只触发一次, waves 波次列表
trigger_zones触发区region, direction (enter/leave), conditions
block_interactions方块交互点位, click 点击方式, conditions, actions
rewards奖励点点位, loot_table 掉落表 id, conditions
boss_pointsBoss 点点位, source, boss_id, conditions
options分支选项option_id, select_mode (single/multi), cases, time_limit_seconds, min_select/max_select, safe_point, ending_map
hints提示点点位 + region, text, channel, conditions
teleport_zones传送区region → target, all_members 是否全员
boundary_zones边界区region + pullback 拉回点
effect_zones效果区region, effects, interval_ticks 生效间隔
mechanisms机关targets 方块状态列表 (block_data + 点位), conditions
room_timers房间计时region, seconds 秒数, on_zero 到零动作
kill_targets击杀目标target, count 数量, scope 范围, actions 达成动作
item_interactions物品交互点位, item 物品, amount, consume 是否消耗, conditions, actions
fx_points特效点点位 + region, sound, particle, radius, conditions
temp_items临时物品item, amount, timing 发放时机, hint 提示; 实例结束自动收回

刷怪点的 waves 每一波包含: interval_ticks 波次间隔, drop_loot 本波是否掉落, mobs 刷怪条目 (id / count / weight); 生物 id 支持原版实体 id 与 emakimobs: 前缀 (装了 EmakiMobs 才能用后者)

条件 conditions ​

模块的 conditions 是一个列表, 每项按 type 判定, params 直接平铺在同级; 你还可以用 logic (AND/OR) + children 嵌套组合. 未知的条件类型一律判为不成立:

类型判定内容常用参数
range范围内目标数达到要求min_players, target (player/mob)
region进入或离开了区域direction (enter/leave)
block_interact方块交互的点击方式匹配click
block_activate方块被激活 (如按钮/拉杆)—
redstone红石电平匹配, change 表示"发生变化"level
clear指定刷怪点的波次已清空spawn
boss_kill指定 Boss 已被击杀boss_id
kill_count击杀数达到要求target, count
timer指定计时模块已到点module
item物品交互已发生—
command命令执行结果匹配expected
player_state玩家状态匹配 (可叠加剩余重生数)state, remaining_lives
script副本脚本写入的判定结果—

动作 actions ​

条件满足后就会执行动作, 动作同样是 type + 同级参数的写法; 计时模块用 on_zero 挂到点动作:

类型作用常用参数
hint给玩家发提示text, channel
message按 lang 键发消息key, channel
command以控制台执行命令 (可带玩家占位)command
teleport传送玩家坐标
fx播放特效音效/粒子相关
set_block改方块状态block_data + 点位
open_gate打开指定门禁门禁 id
spawn_wave启动指定刷怪点的波次刷怪点 id
fail判定本局失败cause
complete判定本局通关cause
give_temp_item发临时物品 (实例结束收回)物品与数量

副本级的生命周期动作 (dungeon_enter 等 11 个段) 不写在 flow 里, 而写在主配置的 action 段, 见 配置详解

分支选项与结局 ​

options 节用来定义玩家面前的分岔路, 玩家的交互过程全部走聊天栏:

  1. 触发后发提示"请选择: ... (输入序号或标识)"
  2. 再做二段确认: "已选 ... 输入 确认 定案, 超时自动定案, 输入 取消 放弃"
  3. 数量必须落在 min_select~max_select 之间, single 单选 / multi 多选
  4. time_limit_seconds 是选择时限, 0 不限; 超时未选视为放弃

选中的候选项 (cases) 带自己的 label 显示文本, ending 结局 id, 以及 seal_regions 封路区域: 落选的路会被封住, 玩家靠近封路区会被拉回该候选项的 safe_point 安全点. 多个候选项的结局经 ending_map 组合成最终 ending. 选项弹出选择提示前触发 DungeonChoiceEvent (可取消, 取消则不发提示), 定案后触发 DungeonOptionConfirmEvent, 封路触发 DungeonSealPathEvent, 收尾触发 DungeonEndingEvent (事件清单见 API 与集成)

地图编辑器 ​

用 /edg editor <副本ID> (需 emakidungeon.editor) 进入独立编辑世界后, 你可以这样做:

  1. 放标记: 单点标记 (spawn/option/chest) 对准方块直接记, 区域标记 (room/gate/trigger) 用 pos1 + pos2 圈两点; marks 查看暂存列表
  2. 改配置: 主面板分四块 — 副本元数据, 刷怪点与波次, 门禁, 分支选项; 字段改完先暂存
  3. 搭地形: fill <方块> 对圈选区域分帧填充
  4. 测试: test 按已保存的模板走一遍正常进入流程, 不计次数, 不上榜; 有未保存改动时会被拒绝
  5. 保存: 点"保存并回写模板"把标记与面板改动写进 config.yml / flow.yml 并回写地形, 回显新的世代号; exit 退出, 未保存的改动会全部丢弃

同一副本模板同时只允许一个编辑会话; 你正处于副本实例或挑战流程中时无法打开编辑器, 需要先结束; 编辑世界残留会在插件启动时自动清扫

GraalJS 副本脚本 ​

scripts/<副本ID>.js 会随该副本的每个实例启动装载, 实例销毁即释放 (开关与超时见主配置 script 段). 你可以在脚本里:

  • dungeon.takeover() 接管整局推进, 之后由脚本节奏控制流程
  • onTrigger('onEnter', ...) / waitFor('onRoomEnter') 订阅与等待触发 (如 onEnter, onRoomEnter, onWaveClear, onRoomClear, onBossKill, onGateOpen)
  • dungeon.spawnWave(...) / dungeon.openGate(...) / dungeon.enterRoom(...) / dungeon.complete(...) 驱动流程
  • dungeon.registerEntry(...) 给全员登记临时词条 (实例结束自动摘除), dungeon.hint(...) 发提示
  • dungeon.factFlag(...) / dungeon.setFact(...) 在脚本自己的事实位之间传递状态

脚本异常或超时只会停用该脚本, 实例照常运行 (除非你把 script.fail-on-error 打开); 完整示例见插件资源里的 examples/example_flow.js. 三方插件也可以经 API 注册脚本触发器, 见 API 与集成