节点编辑器改用 Drawflow 拖拽连线版
- 中间画布换成 Drawflow:拖动节点摆位、从出口圆点拉线到目标=建跳转 - 出口端口动态映射 IR:线性next/choice选项/random分支/fight胜败 - 连线/拖动实时写回 IR;节点坐标持久化到 ir._layout(编译忽略) - 右栏表单保留并双向联动;改跳转目标触发画布重渲 - 工具栏:自动整理、加后继;防误删(右栏输入时 Del 不删节点) - 移除旧 tree.js
This commit is contained in:
@ -169,12 +169,12 @@
|
||||
if (node.kind === "narration") {
|
||||
host.appendChild(field("说话者(可选)", sel(node.speaker || "P1", [{ value: "P1", label: "P1 玩家" }].concat(slots(ir)), v => { node.speaker = v; mut(1); })));
|
||||
host.appendChild(field("文本", area(node.text, v => { node.text = v; mut(1); })));
|
||||
host.appendChild(field("下一步 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(false); })));
|
||||
host.appendChild(field("下一步 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(true); })));
|
||||
} else if (node.kind === "dialogue") {
|
||||
host.appendChild(field("说话者 speaker", sel(node.speaker, [{ value: "P1", label: "P1 玩家" }].concat(slots(ir)), v => { node.speaker = v; mut(1); })));
|
||||
host.appendChild(field("镜头 camera(点位,可选)", sel(node.camera, pointOpts(ir, ctx, node.camera), v => { node.camera = v || undefined; mut(1); })));
|
||||
host.appendChild(field("文本", area(node.text, v => { node.text = v; mut(1); })));
|
||||
host.appendChild(field("下一步 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(false); })));
|
||||
host.appendChild(field("下一步 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(true); })));
|
||||
} else if (node.kind === "choice" || node.kind === "choice_once") {
|
||||
const box = el("div", { class: "subbox" });
|
||||
box.appendChild(el("div", { class: "hd" }, [
|
||||
@ -188,7 +188,7 @@
|
||||
el("button", { class: "mini", onclick: () => { node.options.splice(i, 1); ctx.onChange(true); } }, ["删"]),
|
||||
]));
|
||||
ob.appendChild(field("文本", txt(o.text, v => { o.text = v; ctx.onChange(false); })));
|
||||
ob.appendChild(field("跳转 goto", sel(o.goto, tgt, v => { o.goto = v; ctx.onChange(false); })));
|
||||
ob.appendChild(field("跳转 goto", sel(o.goto, tgt, v => { o.goto = v; ctx.onChange(true); })));
|
||||
ob.appendChild(condEditor(ir, ctx, o.condition, (c, valOnly) => { if (c) o.condition = c; else delete o.condition; ctx.onChange(!valOnly); }));
|
||||
ob.appendChild(grantsEditor(ir, ctx, (o.reward || {}).grants, (gr, valOnly) => { o.reward = { grants: gr }; ctx.onChange(!valOnly); }));
|
||||
// skip
|
||||
@ -214,7 +214,7 @@
|
||||
(node.branches || []).forEach((b, i) => {
|
||||
box.appendChild(el("div", { class: "row2" }, [
|
||||
field("权重", num(b.weight, v => { b.weight = v; ctx.onChange(false); })),
|
||||
field("跳转", sel(b.goto, tgt, v => { b.goto = v; ctx.onChange(false); })),
|
||||
field("跳转", sel(b.goto, tgt, v => { b.goto = v; ctx.onChange(true); })),
|
||||
el("button", { class: "mini", onclick: () => { node.branches.splice(i, 1); ctx.onChange(true); } }, ["删"]),
|
||||
]));
|
||||
});
|
||||
@ -224,8 +224,8 @@
|
||||
host.appendChild(campPicker("我方 camp1", ir, node, "camp1", ctx, true));
|
||||
host.appendChild(campPicker("敌方 camp2", ir, node, "camp2", ctx, false));
|
||||
host.appendChild(el("div", { class: "row2" }, [
|
||||
field("胜 → win", sel(node.win, tgt, v => { node.win = v; ctx.onChange(false); })),
|
||||
field("败 → lose", sel(node.lose, tgt, v => { node.lose = v; ctx.onChange(false); })),
|
||||
field("胜 → win", sel(node.win, tgt, v => { node.win = v; ctx.onChange(true); })),
|
||||
field("败 → lose", sel(node.lose, tgt, v => { node.lose = v; ctx.onChange(true); })),
|
||||
]));
|
||||
} else if (node.kind === "move") {
|
||||
host.appendChild(field("移动者 actor", sel(node.actor, [{ value: "P1", label: "P1 玩家" }].concat(slots(ir)), v => { node.actor = v; mut(1); })));
|
||||
@ -237,21 +237,21 @@
|
||||
field("动作 ani", txt(node.ani, v => { node.ani = v; ctx.onChange(false); })),
|
||||
]));
|
||||
}
|
||||
host.appendChild(field("下一步 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(false); })));
|
||||
host.appendChild(field("下一步 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(true); })));
|
||||
} else if (node.kind === "anim") {
|
||||
host.appendChild(field("角色 actor", sel(node.actor, [{ value: "P1", label: "P1 玩家" }].concat(slots(ir)), v => { node.actor = v; mut(1); })));
|
||||
host.appendChild(el("div", { class: "row2" }, [
|
||||
field("动画 ani", txt(node.ani, v => { node.ani = v; ctx.onChange(false); })),
|
||||
field("朝向 angle(可选)", num(node.angle, v => { if (v == null) delete node.angle; else node.angle = v; ctx.onChange(false); })),
|
||||
]));
|
||||
host.appendChild(field("下一步 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(false); })));
|
||||
host.appendChild(field("下一步 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(true); })));
|
||||
} else if (node.kind === "reward") {
|
||||
host.appendChild(grantsEditor(ir, ctx, node.grants, (gr, valOnly) => { node.grants = gr; ctx.onChange(!valOnly); }));
|
||||
host.appendChild(field("下一步 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(false); })));
|
||||
host.appendChild(field("下一步 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(true); })));
|
||||
} else if (node.kind === "out_ref") {
|
||||
const seqs = (ir.sequences || []).map(s => ({ value: s.id, label: s.id }));
|
||||
host.appendChild(field("引用子序列 ref", sel(node.ref, [{ value: "", label: "(无)" }].concat(seqs), v => { node.ref = v; ctx.onChange(false); })));
|
||||
host.appendChild(field("出口接回 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(false); })));
|
||||
host.appendChild(field("出口接回 next", sel(node.next, tgt, v => { node.next = v; ctx.onChange(true); })));
|
||||
}
|
||||
|
||||
// 删除节点
|
||||
|
||||
Reference in New Issue
Block a user