Files
EVEngine/test/dialogue_proc_script.nut
西风逍遥游 703b5e5353 feat(dialogue): procedural dialogue generation v1
Add data-driven procedural dialogue to eve.Dialogue:

- Variables: setVar/getVar*/hasVar/clearVar/clearVars with global and
  scene scopes; the scene scope auto-clears when the selected Scene host
  changes (polled in update()).
- Conditions: structured tables ({var,op,value} with eq/ne/gt/ge/lt/le/
  has/missing, all/any/not, script predicates) + evalCondition().
- Line pools: loadPoolsFromTable() with weight/when/meta/tags, seeded
  weighted selection (setRandomSeed), per-pool no-repeat history, and
  playLine()/playPool() that resolve {var} interpolation or i18n keys and
  auto-apply meta.expression/motion to bound avatars.
- .dnut authoring format compiled by a new C++ parser (DnutParser):
  loadPoolsFromDnut(source, path) / loadPoolsFromDnutFile(path); parse
  errors surface path:line via getLastPoolsError(). The previous Squirrel
  compiler script and its engine wiring are removed.
- Example (examples/dialogue) uses pools.dnut with mood/hour variables,
  scene-scope auto-clear demo, and hot reload via eve_asset_reload.
- Tests: C++ unit tests (dialogue_proc.cpp) covering variables,
  conditions, pools, selection/dedupe, meta, i18n, scene auto-clear and
  the .dnut parser; Squirrel integration test for the script-facing API.
- Docs: design doc updated with the new content model / API / format.
2026-08-18 21:08:19 +08:00

73 lines
2.9 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
function basicPools() {
if (eve.Dialogue == null) return false;
local dlg = eve.Dialogue();
dlg.clearPools();
dlg.clearVars("all");
dlg.setRandomSeed(7);
dlg.setVar("mood", "happy", "scene");
dlg.setVar("hour", 20, "global");
dlg.setVar("ratio", 1.5, "global");
dlg.setVar("flag", true, "global");
if (dlg.getVarType("mood", "scene") != "string") return false;
if (dlg.getVarInt("hour", 0, "global") != 20) return false;
if (dlg.getVarFloat("ratio", 0.0, "global") != 1.5) return false;
if (!dlg.getVarBool("flag", false, "global")) return false;
if (dlg.hasVar("mood", "global")) return false;
local n = dlg.loadPoolsFromTable({
pools = {
"alice.greet" : {
noRepeat = 2,
lines = [
{ id = "a1", speaker = "alice", text = "你好,{name}!", weight = 2,
when = { var = "mood", op = "eq", value = "happy" },
meta = { expression = "happy", voice = "vo/a1.ogg" },
tags = ["greet"] },
{ id = "a2", speaker = "alice", text = "晚安。", weight = 1,
when = { var = "mood", op = "eq", value = "sad" } },
{ id = "a3", speaker = "", text = "(旁白)" },
],
},
},
});
if (n != 1) return false;
if (!dlg.hasPool("alice.greet")) return false;
if (dlg.getPoolCount() != 1) return false;
if (!dlg.evalCondition({ var = "mood", op = "eq", value = "happy" })) return false;
if (dlg.evalCondition({ var = "mood", op = "eq", value = "sad" })) return false;
if (!dlg.evalCondition({
all = [ { var = "hour", op = "ge", value = 18 },
{ var = "mood", op = "eq", value = "happy" } ],
})) return false;
dlg.registerCondition("isNight", function(ctx) {
return ctx.vars.hour >= 18;
});
if (!dlg.evalCondition({ script = "isNight" })) return false;
dlg.unregisterCondition("isNight");
if (dlg.evalCondition({ script = "isNight" })) return false;
local lineId = dlg.pickLine("alice.greet", { name = "Alice" });
if (lineId == "") return false;
if (!dlg.playLine(lineId, { name = "Bob" })) return false;
if (dlg.getCurrentLineId() != lineId) return false;
if (dlg.getFullText() != "你好,Bob!") return false;
if (dlg.getCurrentLineMeta("voice") != "vo/a1.ogg") return false;
local tags = dlg.getCurrentLineTags();
if (tags.len() != 1 || tags[0] != "greet") return false;
if (!dlg.playPool("alice.greet", { name = "Carol" })) return false;
if (dlg.getCurrentLineId() == "") return false;
// scene 区隔离:清 scene 不影响 global。
dlg.clearVars("scene");
if (dlg.hasVar("mood", "scene")) return false;
if (!dlg.hasVar("hour", "global")) return false;
dlg.clearPools();
return true;
}