简体中文
流程与触发器
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_points | Boss 点 | 点位, 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 节用来定义玩家面前的分岔路, 玩家的交互过程全部走聊天栏:
- 触发后发提示"请选择: ... (输入序号或标识)"
- 再做二段确认: "已选 ... 输入
确认定案, 超时自动定案, 输入取消放弃" - 数量必须落在
min_select~max_select之间,single单选 /multi多选 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) 进入独立编辑世界后, 你可以这样做:
- 放标记: 单点标记 (
spawn/option/chest) 对准方块直接记, 区域标记 (room/gate/trigger) 用pos1+pos2圈两点;marks查看暂存列表 - 改配置: 主面板分四块 — 副本元数据, 刷怪点与波次, 门禁, 分支选项; 字段改完先暂存
- 搭地形:
fill <方块>对圈选区域分帧填充 - 测试:
test按已保存的模板走一遍正常进入流程, 不计次数, 不上榜; 有未保存改动时会被拒绝 - 保存: 点"保存并回写模板"把标记与面板改动写进
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 与集成