文本变体

同一处文字随访问次数变化——seq / cycle / once / shuffle 四个内置函数,不用自己维护计数器。

读者绕回同一个节点第三次时,还读到一模一样的那句话,故事就显得很假。文本变体让同一处文字随访问次数变化,而你不用维护任何计数器——引擎按源码位置给每个调用点各挂一个,自动记数。

四个函数,写在 { } 里就是普通的 JS 函数调用:

=== 码头 ===
你又站在了码头上。{seq("潮水正在退去。", "潮水退得更远了。", "滩涂上已经能看见船骨。")}
+ [再等等] -> 码头
* [离开] -> END

点「再等等」几次,看那句话怎么变。

seq —— 依次推进,停在最后

随后我听见钟声{seq("响了", "又响了", "这回听起来很远了")}

第 1 次「响了」,第 2 次「又响了」,第 3 次起固定停在最后一项。

适合有进展感的重复:同一个地方去第二次、第三次,描写逐步深入,然后稳定下来。

cycle —— 循环

今天是{cycle("周一","周二","周三","周四","周五","周六","周日")}

走到最后一项就回到第一项,循环往复。适合没有终点的轮转:天气、时辰、NPC 的口头禅。

once —— 用完为空

他笑了。{once("这是我第一次见他笑。", "这次他笑得更开了。")}

依次输出各项,全部用完后返回空字符串

这个和 seq 的差别很重要:seq 会一直停在最后一项,once 用完就彻底安静了。适合只在前几次出现的旁白——新手提示、第一次见到某物时的惊讶。

空插值不会留下空行:once(…) 用尽后那一行整行消失,不会在正文里留一段空白。

shuffle —— 随机

风吹过。{shuffle("你打了个寒颤。", "你拉紧了衣领。", "你忽然觉得有什么不对。")}

每次随机返回一项。随机序列受 seed_random 控制,所以同一局里可复现——存档读档回到同一处,读到的还是同一句。

四个放在一起看:

=== 井边 ===
你趴在井口往下看。
{seq("很深。", "还是很深。", "深得看不见底。")}{once("(第一次看的时候你有点晕。)")}{cycle("从北边", "从南边")}吹来。{shuffle("远处有狗叫。", "什么声音也没有。")}
+ [再看一次] -> 井边
* [走开] -> END

多点几次「再看一次」:seq 推到底就停住,once 出现一次就再也不出现,cycle 在两个方向间来回,shuffle 每次随机。

组合与嵌套

变体函数就是普通表达式,可以和三元、插值、彼此自由组合:

那只{cycle("小狗","大狗")}{has_treat ? "摇了摇尾巴" : "吠了一声"}

嵌套时当心:作为实参的变体会被立即求值。

cycle("小狗", shuffle("黑狗","花狗")) 里的 shuffle(...) 每次都会先求值并推进自己的计数器,哪怕这一轮 cycle 根本没轮到它。想要「轮到才求值」,传一个惰性函数:() => shuffle(...)

嵌套变体本来就少见,一般直接接受立即求值即可。

它和变量的关系

变体是没有变量的记忆——引擎替你记住了「这个位置被经过几次」。所以:

  • 只影响这一处文字怎么写、不参与任何判断,用变体。
  • 据此改变故事走向(显示某个选项、跳到别处),那是变量与条件的活。

两者常常一起用:变量管分支,变体管这一句话的措辞。

接下来

文字之外,故事还有画面与声音——下一篇讲演出命令