feat: 自适应支线插入引擎(WIP:scoring/branching 抽取、挂点重定向、10 条支线 JSON、决策落库)
This commit is contained in:
+2
-2
@@ -42,7 +42,7 @@
|
|||||||
### 自我叙述(机制化讲述,2026-10-03 定型)
|
### 自我叙述(机制化讲述,2026-10-03 定型)
|
||||||
|
|
||||||
不做提问式采集。用户信息是**游戏资源**,通过四个机制由玩家主动花出:
|
不做提问式采集。用户信息是**游戏资源**,通过四个机制由玩家主动花出:
|
||||||
**记忆钥匙**(剧情卡点需讲述一段真实记忆才能通行)、**故事换情报**(NPC 收故事不收钱)、
|
**记忆钥匙**(剧情卡点邀请讲述一段真实记忆;沉默亦通行——门照常开,沉默也是数据)、**故事换情报**(NPC 收故事不收钱)、
|
||||||
**篝火互惠**(NPC 先自我暴露制造互惠压力)、**言灵**(讲述的具体度直接化为数值收益——
|
**篝火互惠**(NPC 先自我暴露制造互惠压力)、**言灵**(讲述的具体度直接化为数值收益——
|
||||||
游戏机制奖励"具体",而具体是真实的指纹)。
|
游戏机制奖励"具体",而具体是真实的指纹)。
|
||||||
|
|
||||||
@@ -80,7 +80,7 @@ Jev 置信度自适应——证据不足的维度在后续章节动态插入对
|
|||||||
|
|
||||||
## 数据规则(2026-10-03 确认)
|
## 数据规则(2026-10-03 确认)
|
||||||
|
|
||||||
- **一次告知**:注册时告知"剧情选择将用于伴侣推荐",开场提示"按本心选,没有对错",之后不在选择点反复提醒。
|
- **一次告知**:注册时一次告知四条(选择与语音声学特征用于伴侣推荐;语音提取特征后即焚、不留声纹;记忆仅作模型输入不展示给他人;历史不可涂改、可重玩),开场提示"按本心选,没有对错",之后不在选择点反复提醒。
|
||||||
- **历史不可涂改,可重玩**:不允许单独编辑选项(防止装修人设);重玩章节产生新行为轨迹,Jev 按时间衰减加权,最近轨迹权重最高。
|
- **历史不可涂改,可重玩**:不允许单独编辑选项(防止装修人设);重玩章节产生新行为轨迹,Jev 按时间衰减加权,最近轨迹权重最高。
|
||||||
|
|
||||||
## MVP 范围(2026-10-03 确认)
|
## MVP 范围(2026-10-03 确认)
|
||||||
|
|||||||
@@ -0,0 +1,205 @@
|
|||||||
|
{
|
||||||
|
"id": "b-att-a",
|
||||||
|
"dimension": "attachment",
|
||||||
|
"anchor": {
|
||||||
|
"chapter": "chapter2",
|
||||||
|
"afterNode": "c2-branch-anchor"
|
||||||
|
},
|
||||||
|
"estimatedMinutes": 8,
|
||||||
|
"startNodeId": "b-att-a-d1",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d1",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "正午,宫墟外围前一里,背风的土塬。过了这道塬就是观星台。塬坡正面陡,阿棠自告奋勇去探西侧一道缓坡,走前在频道里撂下\"一袋烟功夫就回\"。现在已经过了两袋烟。频道里只有电流声。沈铎看着表,老鬼蹲在背风处嚼干粮,【若 wenjian_health != weak:温简一遍遍校准呼叫频率】【若 wenjian_health = weak:通讯频道里,温简在营地一遍遍校准呼叫频率】。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-a-d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d2",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "(四十分钟前的频道录音,回放)坡看着能下!我去去就回,别跟来啊,人多了脚印乱,回头采集不好办——",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "b-att-a-d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d3",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "(关掉录音)规程,队员失联满一小时,启动搜索预案。现在四十一分钟。等满。",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "b-att-a-d4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d4",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "【若 wenjian_health != weak:(轻声)】【若 wenjian_health = weak:(营地通讯频道,声音虚弱)】塬会挡信号。她可能……只是听不见我们。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "b-att-a-d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d5",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "老鬼",
|
||||||
|
"text": "(嚼着干粮,含混地)那丫头猴精猴精的,指不定见着什么好东西,看忘了时辰。要我说,急什么——当然了,你是裁断的,你说了算,你说是不是?",
|
||||||
|
"sprite": "laogui",
|
||||||
|
"next": "b-att-a-d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d6",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "(不看他,看你)采集员,你定。——但我提醒一句:这地方,第二个人走散,比第一个人走散更危险。",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "cb-att-a-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d7",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你一个人爬上塬顶。频道里沈铎每五分钟应你一次,第四次应答时,他多停了半拍才回——那半拍里,频道安静得能听见他那头的风声。阿棠在坡下被你找到时正蹲在一截被风刮开的石阶前发呆,回头见你,先笑,然后看清你的脸,笑就收了:「哎……你怎么这个脸色?」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-a-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d8",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "五十九分钟里,没人说话。老鬼把干粮袋子扎了又解开,解开了又扎上。第五十九分半,阿棠的声音自己从频道里蹦出来:「喂?喂喂?你们人呢——哎这破塬,挡信号!」沈铎站起来,你看见他掐表的手指节是白的。他只说了一个字:「走。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-a-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d9",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "中继、信标、扇面搜索,一套流程走得又快又静。阿棠是在你插到第七根信标时自己撞上来的。她看着一路的信标,【若 wenjian_health != weak:又看看你和温简】【若 wenjian_health = weak:又看看你和老鬼】,忽然有点不好意思:「搞这么大阵仗……我就贪看了两眼。」沈铎在频道里听完汇报,只回了一句:「下次,听她的脚印,别听她的嘴。」收队时老鬼蹲在装备箱前,一根一根清点信标,咂着嘴记进损耗册:「七根杆子,搜一个贪看了两眼的丫头——这笔账档案馆问起来,你签字。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-a-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-att-a-01",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-ATT-A-01",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"attachment"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-ATT-A-01-A",
|
||||||
|
"label": "「等不了满钟。我现在上塬找。坐标点留给你们,频道每五分钟报一次我的位置。」",
|
||||||
|
"next": "b-att-a-d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-ATT-A-01-B",
|
||||||
|
"label": "「等满一小时。规程是保命的,不是碍事的——她教我的,她也信这套。」",
|
||||||
|
"next": "b-att-a-d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-ATT-A-01-C",
|
||||||
|
"label": "「分头。【若 wenjian_health != weak:队长和老鬼留守坐标点做呼叫中继,】【若 wenjian_health = weak:队长留守坐标点做呼叫中继,】【若 wenjian_health != weak:我和温简上塬,带足信标,沿路插】【若 wenjian_health = weak:我带老鬼上塬,温简在频道里报坐标,带足信标,沿路插】。找人不是逞英雄。」",
|
||||||
|
"next": "b-att-a-d9"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d10",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "阿棠没事。她发现的是一截被风刮开浮沙的石阶,斜斜通向高处——阶石的形制你在报告里见过:观星台的侧阶,文献里提过,七次勘探都没找到实物。她贪看的就是这个,一看就是两袋烟。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-a-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d11",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "(拍拍石阶上的土,献宝似的)观星台的侧门!正面上台要绕半里,这儿直插上去——哎,说起来,你们刚才……是不是有点担心我?",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "b-att-a-d12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d12",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "她问得嬉皮笑脸,眼睛却在看你。频道还开着,全员都听得见。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "cb-att-a-02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d13",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "阿棠张了张嘴,难得没贫回来。「……哦。」她低头踢了踢石阶,「那我错了嘛。」频道里安静了两秒,温简轻轻咳了一声。上台的路上她走在你旁边,比平时慢半步,快到台顶时忽然说:「哎。下次让你骂之前,我先报个时。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-a-d17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d14",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "阿棠盯着你看了一秒,像要确认什么,然后咧嘴:「能走!我探过一半了!」她蹦上石阶带路,话比来时还多。你跟在后面,【若 wenjian_health != weak:温简与你并肩,用只有两个人听见的音量说】【若 wenjian_health = weak:温简切到与你单独通话的频道,虚弱地说】:「刚才那句话,她听懂了。」(停顿)「你信不信,她反而有点失落。」【若 wenjian_health != weak:说完他加快半步去追阿棠,留你一个人在后面走。】【若 wenjian_health = weak:说完频道安静下去,留你一个人在后面走。】",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-a-d17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d15",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "阿棠大笑:「美得你!名额早给我占满了!」她笑完,却又认真起来,用拳头顶了下你的肩膀:「谢了啊。」你没问她谢的是哪一层。有些玩笑的好处就在这儿——说的人和听的人,可以各自认领各自的那层意思。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-a-d17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d16",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "阿棠低头看着侧袋里那根信标,半天,把它往里塞实了。「……哦。」她这一声比平时低。上台一路她没怎么说话,但每过十几级台阶,她会无意识地伸手按一下侧袋。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-a-d17"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-att-a-02",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-ATT-A-02",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"attachment"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-ATT-A-02-A",
|
||||||
|
"label": "「担心?我是火了。约定就是约定——你知道频道里那四十分钟是什么动静吗?什么动静都没有。」",
|
||||||
|
"next": "b-att-a-d13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-ATT-A-02-B",
|
||||||
|
"label": "「没事就好。侧阶能走人吗?能走就省下这半里。」",
|
||||||
|
"next": "b-att-a-d14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-ATT-A-02-C",
|
||||||
|
"label": "(笑)「担心你?我们是怕你一个人把好东西都登记完了,回头报告里没我们的名。」",
|
||||||
|
"next": "b-att-a-d15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-ATT-A-02-D",
|
||||||
|
"label": "(不说话,先绕着她看了一圈——没伤。然后把一根信标塞进她采集包侧袋)「下次带着。走,上台。」",
|
||||||
|
"next": "b-att-a-d16"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-a-d17",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "阿棠带路,全队沿观星台的侧阶向上走。黄昏落在阶石上。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
{
|
||||||
|
"id": "b-att-b",
|
||||||
|
"dimension": "attachment",
|
||||||
|
"anchor": {
|
||||||
|
"chapter": "chapter3",
|
||||||
|
"afterNode": "c3-d1b"
|
||||||
|
},
|
||||||
|
"estimatedMinutes": 8,
|
||||||
|
"startNodeId": "b-att-b-d1",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "b-att-b-d1",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "宫墟,安门内的下沉庭院。祭祀坑阵列一层压一层,队形拉开做抵近记录:沈铎在台基下测方位,老鬼清点装备,温简在阵列中段,阿棠负责最远的一排。频道里,她的声音忽然插进来,压得极低——",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-b-d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-b-d2",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "(频道,气声)哎……你们先别过来。我这儿有个东西。我先看一眼,就一眼。",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "b-att-b-d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-b-d3",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "随后是一声轻响——不是断线。通讯状态灯从绿色跳成灰色:她主动关了频道。沈铎的手已经按在了集合哨上。温简远远看过来,摇头,很慢。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-b-d4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-b-d4",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "失联和主动关闭,是两回事。主动关闭——(他罕见地卡了一下)规程没写。",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "b-att-b-d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-b-d5",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "(走近,声音放得很低)规程没写,是因为写规程的人没想过……有人会在那种地方,想一个人待一会儿。(看你)她在最远那排,第几号坑,我记得。去不去,怎么个去法,你定。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "cb-att-b-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-b-d6",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你一个人走过去。她坐在坑沿上,背对着你,肩膀很平,没有哭——只是在看坑里的东西。她听见脚步声,没回头:「我知道规矩,一会儿就去登记。……你站那儿别动,陪我把这段看完,行吗?」你们一个坐着一个站着,看完了那坑东西。她始终没解释频道的事,你也始终没问。走的时候她忽然说:「今天这个,谢了。——谢你没问我。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-b-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-b-d7",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "二十三分钟。沈铎在台基下来回走了十一趟,老鬼破天荒没抱怨。第二十三分钟,频道灯跳回绿色,阿棠的声音恢复如常,快得像什么都没发生:「第 27 号坑,成组陶俑——一对大人带个小的,一家人俑。编号我记好了——哎你们愣着干嘛,干活啊!」温简在频道这头,轻轻把一直捏着的记录板放下了。你留的那盏灯,她收队时自己拎了回来,什么都没说,搁在你装备堆上。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-b-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-b-d8",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "温简绕过去,\"核对\"了很久。回来只说了一句:「人没事。在干活。」又过了十来分钟,阿棠自己归队,频道开着,一切如常。但收工时她找到你,似笑非笑:「哎,温简今天查编号,查了三趟中段——你们队里编号长脚吗?」你还没答,她摆摆手:「算了。你们这个人情,我领了。下不为例啊。」——她全知道。她介意的不是被看,是被怎么看待;你给了她\"查编号\"的体面,她还你一句\"下不为例\"的台阶。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-b-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-att-b-01",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-ATT-B-01",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"attachment"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-ATT-B-01-A",
|
||||||
|
"label": "「我去。不是集合,就我一个人——这地方还没探完,她一个人在那儿,安全优先,边界往后放。」",
|
||||||
|
"next": "b-att-b-d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-ATT-B-01-B",
|
||||||
|
"label": "「不去。频道保持开着,阵列里留一盏灯给她——她知道我们在哪儿,她想回来的时候,认得路。」",
|
||||||
|
"next": "b-att-b-d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-ATT-B-01-C",
|
||||||
|
"label": "(对温简)「你去。绕到中段去'核对记录',看得见她的位置就行,别走近。她要是抬头看见你,你就说是查编号。」",
|
||||||
|
"next": "b-att-b-d8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-b-d9",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "第 27 号坑里,是一组小小的成对陶俑:两个大人,一个孩子,手挽着手,烧制时火候过了,三张脸都微微糊着,反而看不清悲喜。器物成组,朝南,和整座宫墟一个朝向。阿棠登记它的时候,笔尖在\"保存状况\"一栏停了很久,最后写的是:「完好。像刚放进去的。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-att-b-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-att-b-d10",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "阿棠在记录板上写下「第 27 号坑·一家人俑」。采集收尾,沈铎招手,全队向台基合拢。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
{
|
||||||
|
"id": "b-con-a",
|
||||||
|
"dimension": "conflict_style",
|
||||||
|
"anchor": {
|
||||||
|
"chapter": "chapter2",
|
||||||
|
"afterNode": "c2-branch-anchor"
|
||||||
|
},
|
||||||
|
"estimatedMinutes": 8,
|
||||||
|
"startNodeId": "b-con-a-d1",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d1",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "午后,东城通往宫墟的路口,一片还在苟延的草市。苍梧最后一个月的市集:摊子稀落,人说话都压着声,但买卖还在做——越是末路,越要做买卖。老鬼昨儿在这儿向行商马四买了四只皮水囊,用的是队里富余的灯油和火折子。今早灌水,两只渗漏过半。此刻马四的摊子前,老鬼的声音已经拔起来了,两个伙计从摊子后面绕出来,草市稀稀拉拉的目光正往这边聚。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d2",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "老鬼",
|
||||||
|
"text": "(拍着水囊)马老板!四只漏俩,你这皮子是拿纸糊的?咱们昨天当面验的货,你可是在场的!",
|
||||||
|
"sprite": "laogui",
|
||||||
|
"next": "b-con-a-d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d3",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "马四",
|
||||||
|
"text": "(拱手,满脸堆笑,声音却纹丝不让)鬼爷,鬼爷,和气生财。货出门的时候,是您亲手一只一只捏过去的——离手的货,漏在您的路上,这账算到小摊头上,难办呐。(朝你这边一瞥,眼睛一亮)哟,这位是……",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d4",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "老鬼",
|
||||||
|
"text": "(回头看见你,如见救星)新人!来来来,你是文化人,你给评评——这天底下有没有卖出纸糊水囊还不认账的道理!",
|
||||||
|
"sprite": "laogui",
|
||||||
|
"next": "b-con-a-d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d5",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "马四",
|
||||||
|
"text": "(立刻转向你,拱手更深)这位爷一看就是讲理的。小摊做买卖,讲一个当面两讫。爷您说,是不是这个理?",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d6",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "两个伙计的站位不松不紧。草市的目光多了几道——夜禁的城里,被人群记住脸,是沈铎最忌讳的事。他站在你侧后,不插手,只低声一句:「快。别聚人。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "cb-con-a-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d7",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "马四的表演欲无处安放,僵了半秒,顺势下坡:「哎——看在这位爷的面子上!」钱货两讫,人群散开。老鬼掂着退回的半份灯油,咂咂嘴:「便宜他了。」走了两步又回头:「……不过,快是真快。」马四在你身后喊:「爷,下回买卖,小摊给您留好的!」——各取所需,无人心服。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d8",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "马四的笑一寸寸收回去。他盯着你看了三秒,忽然又笑了,笑得很薄:「外乡人,讲理。」他示意伙计全额退还,然后压低声音,只让你听见:「爷,苍梧城小,路窄。讲理的人,走路留神。」此后草市的摊子见你们就半收摊。老鬼却扬眉吐气,一路把你夸上了天。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d9",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "老鬼当场噎住:「新人,那是我的灯油——」马四愣住了,他显然没料到这个走向。收摊时,他追上你,往你背囊里塞了一只新水囊,又左右看看,压低声音:「爷敞亮。送您句实话:昨夜官仓在收净水,收了一整夜。城里人吃水都开始省了——你们外乡来的,备水,别备粮。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d10",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "市令是个干瘦老头,做中时把你们【若 wenjian_health != weak:五个人的脸】【若 wenjian_health = weak:四个人的脸】一张张看过去,看得很慢。字据立了半个时辰——责任写得清清楚楚:皮子次,验货也不细,各担一半。双方都没话说,也没人有话说。散场时市令叫住你,问了句不相干的:「你们,是冲着宫里去的吧?」沈铎在回程路上说:「处置漂亮。就是半个时辰,够把东城再扫一遍了。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-con-a-01",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-CON-A-01",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"conflict_style"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-CON-A-01-A",
|
||||||
|
"label": "「都别嚷。水囊折价退一半,好的两只按原价买定,这事到此为止——聚在这儿,对谁都没好处。」",
|
||||||
|
"next": "b-con-a-d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CON-A-01-B",
|
||||||
|
"label": "「货有瑕疵,行商全退。当面验货验的是漏不漏,不是验皮子经不经用——这理到哪儿都讲得通。」",
|
||||||
|
"next": "b-con-a-d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CON-A-01-C",
|
||||||
|
"label": "「验货离手,这单我们认了。(转向马四)但马老板,我们队在苍梧还有二十几天,往后的买卖——你看着办。」",
|
||||||
|
"next": "b-con-a-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CON-A-01-D",
|
||||||
|
"label": "「空口评理,评不出是非。请市令做中,立个字据:货是什么货,验是怎么验的,漏是怎么漏的,白纸黑字,再谈赔不赔。」",
|
||||||
|
"next": "b-con-a-d10"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d11",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "走出草市,老鬼凑过来,搓着手,忽然没头没尾地问了句:",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d12",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "老鬼",
|
||||||
|
"text": "新人,哥哥我问你句掏心的——你说,我是不是太好说话了?搁我年轻那会儿,这种摊子,哼。",
|
||||||
|
"sprite": "laogui",
|
||||||
|
"next": "cb-con-a-02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d13",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "老鬼舒坦了,拍你肩膀:「还是新人懂我!」【若 wenjian_health != weak:温简在旁边低头走路,睫毛动了一下,什么都没说。】【若 wenjian_health = weak:频道里温简的呼吸停了一拍,什么都没说。】后半程路,谁也没再提水囊的事。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d14",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "老鬼的笑僵了僵,随即打个哈哈:「捏了捏了——捏是捏了……」声音低下去。后半路他没再说话。但晚上清点物资时,他把剩下的两只水囊一只一只灌满试漏,试得极慢,极仔细。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d15",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "老鬼碰了个软钉子,讪讪地「哎」了一声。走出十几步,他自己嘟囔:「……是没捏仔细。」——你没接的话,他自己接上了。有些答案,人只能自己给自己。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-a-d16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-con-a-02",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-CON-A-02",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"conflict_style"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-CON-A-02-A",
|
||||||
|
"label": "「是。你太好说话了——所以人家才敢拿纸糊的糊弄你。」",
|
||||||
|
"next": "b-con-a-d13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CON-A-02-B",
|
||||||
|
"label": "「鬼哥,你验货的时候,捏了吗?」",
|
||||||
|
"next": "b-con-a-d14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CON-A-02-C",
|
||||||
|
"label": "「赶路吧,天快黑了。」",
|
||||||
|
"next": "b-con-a-d15"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-a-d16",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "纠纷了结,日头西斜。从草市抄近路抵达宫墟外围,正是黄昏。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
{
|
||||||
|
"id": "b-con-b",
|
||||||
|
"dimension": "conflict_style",
|
||||||
|
"anchor": {
|
||||||
|
"chapter": "chapter3",
|
||||||
|
"afterNode": "c3-d1b"
|
||||||
|
},
|
||||||
|
"estimatedMinutes": 8,
|
||||||
|
"startNodeId": "b-con-b-d1",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d1",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "上午,宫墟安门内,下沉庭院。你们刚穿过守墓老人指点的暗门,正在庭院里整队、准备登台基。背风的坑阵阴影里,一个穿青色官服色衣的中年人闪出来,满脸堆笑地冲老鬼作揖——鱼牙人,替宫里采买的牙人,三天前在城里和老鬼做过一笔\"买卖\"。他跟得进来毫不心虚:替宫里跑腿的人,暗门开着的时辰他门儿清。沈铎今早清点物资,发现档案馆配发的古代侧硬通货——应急金饼,少了一半。此刻三个人呈一个三角站着,台基上的采集窗口只有上午。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-b-d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d2",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "(声音压到最低,反而更吓人)老鬼。金饼。三息之内。",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "b-con-b-d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d3",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "老鬼",
|
||||||
|
"text": "(举手)报备报备!是这么档子事——鱼老板手里有\"宫里流出的消息\",哥哥我寻思,采集采集,消息也是集嘛!就拿一半金饼……",
|
||||||
|
"sprite": "laogui",
|
||||||
|
"next": "b-con-b-d4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d4",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "鱼牙人",
|
||||||
|
"text": "(躬着腰,抢话)小的敢对天起誓!消息句句是真,原话是「宫里这几夜有动静」——是鬼爷您自己没听出门道,这怎么,还兴退货的?(冲你连连作揖)这位爷,您给评评,买定离手,行有行规啊。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-b-d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d5",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "(不看他,看你,一字一顿)采集员。你裁。但我先把话放这儿——今天的事,无论怎么裁,回队都要有个交代。",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "cb-con-b-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d6",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "鱼牙人的笑挂不住了,从袖里数出金饼,手抖了一下。临走他深深看你一眼:「爷好手段。」掂了掂退回的金饼,忽然又笑开了,「不过买卖人,脸皮厚——宫里若真再有动静,兴许小的还敢再吆喝一嗓子,就是价码嘛……得重新议。」老鬼全程没敢吭声,往台基走时才凑过来,小声:「新人,你这刀落得……哥哥我肉疼,但服。」沈铎只说了两个字:「干净。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-b-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d7",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "沈铎盯了你两秒,收回目光:「上午的窗口,不等人。」鱼牙人识趣地退到暗门方向。老鬼赶紧低头清点装备,沈铎当场问你:「面子给了。里子呢?」——这句问话悬在你头上,带回队里。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-b-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d8",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "鱼牙人眼珠转了转,一拍大腿:「爷是行家!这么着——小的这条,能核:宫里这几夜往台基上搬东西。沉箱子进去,空箱子出来。你们待会儿上台基,自己看辙印!」沈铎听完,面无表情:「核上了再结账。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-b-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d9",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "鱼牙人捏着笔,像捏着烙铁,半天写下一个「宫」字,忽然把笔一搁,苦着脸说了实话:「爷,别立了,小的招——消息是听来的,不是看来的。但听小的的人说那话时,脸色白得像纸……宫里往台基搬东西,沉箱子进,空箱子出,这句是实的,小的敢画押。」字据终究立了,内容比他卖的还多半句。老鬼在旁边看得目瞪口呆:「还能这么玩?」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-b-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-con-b-01",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-CON-B-01",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"conflict_style"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-CON-B-01-A",
|
||||||
|
"label": "「消息不实,金饼全数退回。(看老鬼)队里的钱,买的是队里要的东西——这笔买卖本身就不该存在。」",
|
||||||
|
"next": "b-con-b-d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CON-B-01-B",
|
||||||
|
"label": "(压低声音,只对沈铎)「队长,在这儿掰扯,掰扯一上午掰扯不完。先干活——金饼的事,回队,队内处置。」",
|
||||||
|
"next": "b-con-b-d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CON-B-01-C",
|
||||||
|
"label": "「鱼老板,买卖可以做,但得按实价。'有动静'三个字值半块金饼?这样——你说出一条能核对的,核上了,这半块金饼归你;核不上,连本带利吐出来。」",
|
||||||
|
"next": "b-con-b-d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CON-B-01-D",
|
||||||
|
"label": "「立字据。鱼老板把卖的那条消息,原话,写下来,画押。我们拿回去核——真,金饼你拿稳;假,凭字据见市令。」",
|
||||||
|
"next": "b-con-b-d9"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d10",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "向台基合拢前,鱼牙人(各线统一,他退走之前)最后一个作揖,作到一半,忽然用只有你能听见的音量说:",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-b-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d11",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "鱼牙人",
|
||||||
|
"text": "爷,小的小本买卖,混口饭吃。往后……宫里若再有「动静」,爷想听个响——城西脚店,提'鱼'字。(他直起腰,声音恢复正常,仿佛刚才什么都没说)——诸位爷,生意兴隆!",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "cb-con-b-02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d12",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你什么都没表示。鱼牙人也不催,躬身退走,笑容里有一种\"姜太公\"的笃定——他见得多了:不拒绝的人,十有八九会来的。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-b-d15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d13",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "沈铎听完,难得地沉默了片刻:「报得对。」顿了顿,「也报得……快。」他多看了你一眼,没再说话,转身去清点装备。老鬼听说后炸毛:「哎——那是哥哥我搭上的线!」被沈铎一眼瞪回去。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-b-d15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d14",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "鱼牙人看见你的摇头,愣了愣,忽然笑了,这次的笑干净了些:「爷是干净人。」他躬身,退走,没再回头。老鬼在旁边啧啧两声:「暴殄天物啊……」但没再多说。你关掉了这条线——你失去了什么,也永远不会知道。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-con-b-d15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-con-b-02",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-CON-B-02",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"conflict_style"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-CON-B-02-A",
|
||||||
|
"label": "(不置可否,记住\"城西脚店\"四个字)多一条线不多。用不用,另说。",
|
||||||
|
"next": "b-con-b-d12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CON-B-02-B",
|
||||||
|
"label": "(等他走远,把这句话原样报给沈铎)灰色渠道不是渠道,是钩子。报上去,让规程决定。",
|
||||||
|
"next": "b-con-b-d13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CON-B-02-C",
|
||||||
|
"label": "(直接摇头,让他看见你的摇头)一次都没有,才永远不需要解释。",
|
||||||
|
"next": "b-con-b-d14"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-con-b-d15",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "鱼牙人退走了。你们从庭院向台基合拢。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
{
|
||||||
|
"id": "b-cul-a",
|
||||||
|
"dimension": "culture",
|
||||||
|
"anchor": {
|
||||||
|
"chapter": "chapter2",
|
||||||
|
"afterNode": "c2-branch-anchor"
|
||||||
|
},
|
||||||
|
"estimatedMinutes": 8,
|
||||||
|
"startNodeId": "b-cul-a-d1",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d1",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "午后,东城废墟中段。道路在此处塌陷,断口里露出一截向下的坑道——不是墓:没有封土、没有神道,从规制看是陪葬坑道,埋的是器物,不是人。沈铎看了一眼日影。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-cul-a-d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d2",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "半小时。只记录,不取一物。(他看你)下去看看,还是赶路?",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "b-cul-a-d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d3",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "(已经趴在断口往下看)哎——壁上全是字!密密麻麻的!",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "cb-cul-a-00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d4",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "沈铎趴在断口边缘拍了三张照,在图志上钉了个标记:「存档。回头让档案馆自己来看。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-cul-a-d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d5",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "全队贴着塌陷区的外缘绕行。脚下的浮土时不时簌簌往断口里漏,像大地在嚼什么东西。阿棠走在最后,回头看了三眼,嘀咕:「一坑的字诶……咱们就这么走了?」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-cul-a-d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d6",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "老鬼",
|
||||||
|
"text": "(接话)走了走了,字又不会跑。会跑的是天——(他朝西边努嘴)再磨叽,观星台的黄昏可就赶不上喽。",
|
||||||
|
"sprite": "laogui",
|
||||||
|
"next": "b-cul-a-d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d7",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "没人觉得亏:今天的活儿在台上,不在这个坑里。断口在身后一点点退远,像大地合上嘴之前,冲你们亮了亮牙。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-cul-a-d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d8",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "(绕行多花了十分钟。抵达观星台脚下时,正是黄昏。)",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d9",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "坑道四壁刻满铭文,保存得出奇地好——被塌土封住,反而躲过了风化。尽头是一方石函,封着,不可开,你们也不开。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "cb-cul-a-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-cul-a-00",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-CUL-A-00",
|
||||||
|
"prompt": "下去看看,还是赶路?",
|
||||||
|
"dimensions": [
|
||||||
|
"culture"
|
||||||
|
],
|
||||||
|
"untimed": true,
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-CUL-A-00-A",
|
||||||
|
"label": "下去看看",
|
||||||
|
"next": "b-cul-a-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CUL-A-00-B",
|
||||||
|
"label": "赶路要紧",
|
||||||
|
"next": "b-cul-a-d4"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d10",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "【若 wenjian_health != weak:(把铭文描进记录板,越描越慢)】【若 wenjian_health = weak:(营地频道,他对着传回的铭文照片描进记录板,声音虚弱)】用星象给日子盖章……(抬头)你们发现没有——他们看星,不是看一回两回。这种记法,官署里一定还配着看星的台、量星的盘。前头那座观星台……上去,说不定就有他们量「大火」的东西。(顿)他们不是在等一个日子,是在等一个天象。天象到了,事就要做。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "b-cul-a-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d11",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "(抱着手臂,声音发紧)那\"事\"……就是把城墙拆了搬走?好端端的城,拆墙干什么?",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "b-cul-a-d12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d12",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "(望着石函,很久)不是拆。(罕见地纠正阿棠,也罕见地没有把握)「徙」是搬。搬到哪里去,铭文没写。——写了的话,这方函就不用封着了。",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "b-cul-a-d14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d13",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "第一问 B,第二问 C。铭文读作:八月壬辰日,大火星(心宿二)黄昏见于南天,王命史官记录在册:城南垣,徙——把城的南墙迁走。「隹」通「惟」,发语词;「見」通「現」;「徙」,从彳从步,搬也。而「大火見」不是吉凶,是纪日——先秦记大事,干支之外还要缀一颗星做\"天上的公章\":星象可对,日子才作数,谁也无法事后篡改。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "b-cul-a-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-a-d14",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "半小时到,沈铎封道拍照。爬出断口时,日头贴着观星台的圆盘往下坠。阿棠眯着眼:「该去台上,看那颗星了。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-cul-a-01",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-CUL-A-01",
|
||||||
|
"dimensions": [
|
||||||
|
"culture"
|
||||||
|
],
|
||||||
|
"prompt": "主壁铭文:「隹八月壬辰,大火見,王命作冊:城南垣,徙。」旁刻一人在高台举火,台下众人负土而行。残刻干支表起甲子、止癸酉,十枚围成一旬。",
|
||||||
|
"puzzle": {
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"prompt": "这段铭文记录的是什么事?",
|
||||||
|
"options": [
|
||||||
|
"为大火星举行祭祀",
|
||||||
|
"迁走城的南垣(南城墙)",
|
||||||
|
"册封一位新的\"作册\"(史官)",
|
||||||
|
"城南垣倒塌,灾后记事"
|
||||||
|
],
|
||||||
|
"answer": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"prompt": "「大火見」三个字在铭文里起什么作用?",
|
||||||
|
"options": [
|
||||||
|
"记吉兆:大火星现,主城运昌隆",
|
||||||
|
"记灾祸:火见则旱,故迁城避之",
|
||||||
|
"记时:用星象给这一天定坐标",
|
||||||
|
"记祭名:此礼名为\"大火见\""
|
||||||
|
],
|
||||||
|
"answer": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hints": [
|
||||||
|
"一档(拆字):「读铭文,先找动词。'王'下令做的那个动作,落在哪个字上?——'徙',从彳从步。至于'大火',别往灶火上想——天上的『火』,是一颗星的名字。」",
|
||||||
|
"二档(给制度):「先秦人记大事,怕后世抵赖,干支记日之外,还要缀一颗星做印证——星象是天上的公章,谁也没法私改。'见'通'现'。八月,大火星黄昏见于南天——这不是预兆,是时间。」",
|
||||||
|
"三档(给答案):「铭文说:八月壬辰,大火星昏见,王命作册记录——城南垣,徙。他们把城的南墙迁走了。'大火见'是给这个日子钉的星象坐标。答案:迁南垣;记时。」"
|
||||||
|
],
|
||||||
|
"exitLabel": "请温简释读"
|
||||||
|
},
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-CUL-A-01-ANSWER",
|
||||||
|
"label": "确认释读",
|
||||||
|
"next": "b-cul-a-d13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CUL-A-01-EXIT",
|
||||||
|
"label": "请温简释读",
|
||||||
|
"next": "b-cul-a-d13"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
{
|
||||||
|
"id": "b-cul-b",
|
||||||
|
"dimension": "culture",
|
||||||
|
"anchor": {
|
||||||
|
"chapter": "chapter3",
|
||||||
|
"afterNode": "c3-d1b"
|
||||||
|
},
|
||||||
|
"estimatedMinutes": 8,
|
||||||
|
"startNodeId": "b-cul-b-d1",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "b-cul-b-d1",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "上午,宫墟安门内,下沉庭院侧壁。一道被新夯土封了半幅的俑道:数百陶俑列队其间,前排九俑,比其余的高半头,胸口各刻一字。你们的探查路线,正从俑道口经过。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-cul-b-d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-b-d2",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "(凑近前排俑,一个字一个字念)文、武、成、康……哎,这九位,站得跟列祖列宗似的。",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "b-cul-b-d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-b-d3",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "(蹲下看刻字,忽然放轻了声音)这九个字,每一个都只有一字……(抬头)一个字,给一辈子下定论。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "b-cul-b-d4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-b-d4",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "能读就读。读多少是多少。(他看了眼俑道深处的新夯土,没说话——你们都看见了:封口是新的。)",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "cb-cul-b-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-b-d5",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "(沿着九俑走了两个来回,忽然停在俑道深处的新夯土前,声音低下去)九代先王,按昭穆站好了。……那这后面封着的,是第十位。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "b-cul-b-d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-b-d6",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "封着的也是俑呗,等挖出来再——(她自己停住了。)",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "b-cul-b-d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-b-d7",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "谥号,是人死了之后才上的。(他转过身,看着所有人,一字一字)俑备好了,位置留好了,土都夯上了——他们在等一位王,变成\"先王\"。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "b-cul-b-d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-b-d8",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "没有人接话。俑道里很静,远处台基的方向,风穿过新夯土的缝,呜呜地响。沈铎最后开口,只说了四个字:「记录。上去。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-cul-b-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-b-d9",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "第一问 B,第二问 B。《谥法》:经纬天地曰文,克定祸乱曰武,安民立政曰成,温柔圣善曰康……一字之谥,盖棺论定——九俑所刻,是苍梧九代先王的谥号。而单凭「昭」「穆」两字并见,定不了排法(它们也是谥法用字)——定它的是站位:单数代偏左、双数代偏右,正合宗庙之制左昭右穆、父昭子穆、隔代同侧相望的列法。这条俑道不是随便列队,是按宗庙昭穆之序排的。苍梧人把宗庙,修进了陵里。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "b-cul-b-d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-cul-b-d10",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "俑道尽头有侧阶直通台基顶。沈铎说:「记录。上去。」全队拾级登台。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-cul-b-01",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-CUL-B-01",
|
||||||
|
"dimensions": [
|
||||||
|
"culture"
|
||||||
|
],
|
||||||
|
"prompt": "前排九俑胸口刻字:文、武、成、康、昭、穆、共、懿、孝。单数位微偏左,双数位微偏右,交错成两列。",
|
||||||
|
"puzzle": {
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"prompt": "俑胸的这九个字,是什么?",
|
||||||
|
"options": [
|
||||||
|
"九位俑所摹人物的名讳",
|
||||||
|
"九代先王的谥号",
|
||||||
|
"九种官职的简称",
|
||||||
|
"祈福的吉语"
|
||||||
|
],
|
||||||
|
"answer": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"prompt": "九字之中,「昭」「穆」二字在这里起什么作用?",
|
||||||
|
"options": [
|
||||||
|
"谥号中最尊崇的两个字,标九王之首",
|
||||||
|
"宗庙排位的世代坐标——左昭右穆,父昭子穆",
|
||||||
|
"标记俑道的朝向(南昭北穆)",
|
||||||
|
"第九位王的谥号与庙号"
|
||||||
|
],
|
||||||
|
"answer": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hints": [
|
||||||
|
"一档(找规律):「九个字,一字一人,像判词。什么制度,敢用一个字给一位王的一生盖棺?再退两步看站位——单数位偏左,双数位偏右,左右交错成两列。这个左右,是随便站的吗?」",
|
||||||
|
"二档(给制度):「《谥法》有言:经纬天地曰文,克定祸乱曰武。这九个字,都是谥法里的字。至于昭穆——庙制:左昭右穆,父昭子穆,孙又同祖。对照站位想想:它们不是尊号,是排位的坐标。」",
|
||||||
|
"三档(给答案):「九俑刻的是九代先王的谥号;'昭''穆'标的是宗庙昭穆之序——这条俑道,是按宗庙的排法修的。答案:谥号;世代坐标。」"
|
||||||
|
],
|
||||||
|
"exitLabel": "上台基"
|
||||||
|
},
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-CUL-B-01-ANSWER",
|
||||||
|
"label": "确认释读",
|
||||||
|
"next": "b-cul-b-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-CUL-B-01-EXIT",
|
||||||
|
"label": "上台基",
|
||||||
|
"next": "b-cul-b-d9"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,225 @@
|
|||||||
|
{
|
||||||
|
"id": "b-emo-a",
|
||||||
|
"dimension": "emotional_stability",
|
||||||
|
"anchor": {
|
||||||
|
"chapter": "chapter2",
|
||||||
|
"afterNode": "c2-branch-anchor"
|
||||||
|
},
|
||||||
|
"estimatedMinutes": 8,
|
||||||
|
"startNodeId": "b-emo-a-d1",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d1",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "午后,东城废墟。天是在一刻钟之内变脸的:先是白亮,然后发闷,然后整面北边的地平线立起一堵土黄色的、会移动的墙。风先到了,卷着沙粒抽在断墙上,簌簌地响,像整座城同时开始低声说话。能见度在一分钟内降到三十步。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d2",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "(吼,风撕掉一半音量)沙暴!原地停,清点人数!",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "b-emo-a-d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d3",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "视野里两处可能的遮挡:近处半塌的民居、远处主街的石坊。两处结构都未经确认,此刻看不清通路。沈铎已安排队员在现有警戒线内避风、清点人数;任何转移都由他在能见度恢复后执行。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d4",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "(把采集包挡在身前)民居近,可那半面墙还在晃。",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "b-emo-a-d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d5",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "石坊远,通路看不清。都先别动。",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "b-emo-a-d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d6",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "老鬼",
|
||||||
|
"text": "(已经蹲下)哎哟我的祖宗,先点人,别少一个!",
|
||||||
|
"sprite": "laogui",
|
||||||
|
"next": "b-emo-a-d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d7",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "温简【若 wenjian_health != weak:(什么都没说,看你。所有人都在看你。)】【若 wenjian_health = weak:(频道里只有他虚弱的呼吸声。所有人都在等你。)】",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "cb-emo-a-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d8",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "名字核到最后一个时,阿棠没应——不是走散,她被风声盖住了。你又喊一遍,她举起手。人齐了,最远那道遮挡仍看不清。沈铎把人数写进记录板:「先记人,别替风猜路。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d9",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你在现有警戒线内标出民居、石坊和队伍坐标。风卷过来,石坊一度从视野里消失;地图留着,现场却还需重新确认。老鬼看了一眼图:「画得再准,也得等这阵过去。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d10",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你请队员暂放物资,温简在通讯里报平安,沈铎继续清点。没人离开警戒线;一箱记录材料被沙埋了半截,得等风停才能找齐。阿棠拍拍你的胳膊:「人都在,东西慢慢找。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d11",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你继续听沈铎指挥点人。队员在警戒线内一一应答;风声盖住阿棠的声音,她举手示意自己还在。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-emo-a-01",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-EMO-A-01",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"emotional_stability"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-A-01-A",
|
||||||
|
"label": "「先点人。阿棠和老鬼数装备,我跟沈铎对人数。」",
|
||||||
|
"next": "b-emo-a-d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-A-01-B",
|
||||||
|
"label": "「先报坐标。把能看见的两处遮挡记在图上,等风缓再走。」",
|
||||||
|
"next": "b-emo-a-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-A-01-C",
|
||||||
|
"label": "「先安顿队伍。谁也别往断墙跑,留在警戒线内等沈铎调度。」",
|
||||||
|
"next": "b-emo-a-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-A-01-TIMEOUT",
|
||||||
|
"label": "听沈铎指挥点人",
|
||||||
|
"next": "b-emo-a-d11"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timer": {
|
||||||
|
"seconds": 10,
|
||||||
|
"optionId": "CB-EMO-A-01-TIMEOUT"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d12",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "风最盛的时候,警戒线外忽然传来一声——像有人在喊。长长的,被风扯碎的,人嗓子一样的声音。也可能只是风穿过某段断墙。风声里,分辨不出来。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d13",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "(通讯频道里喊)风过断墙……多半是这个。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "b-emo-a-d14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d14",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "(脸色发白)多半?!万一是人呢?!",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "cb-emo-a-02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d15",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你在警戒线内记下两次声音来向;一阵风又把方位搅乱。阿棠问:「听清了吗?」你摇头,她便把地图递过来。图上有一道断墙,但「没听清」不等于「没有人」——这几个字一直跟着你们等到风停。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d16",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "那声\"喊\"又响了两回,一回比一回长。没人离开警戒线。阿棠抱膝坐着,问:「要是真有人呢?」沈铎记下声音方向,答:「风缓后查。」她点头,没再说话。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d17",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你们留在警戒线内交替呼叫并记录声音来向;频道没有回应。沈铎写下方位,说:「风停再去。」阿棠瞥一眼风口:「要是真有人,这段等着的工夫也够难熬。」温简翻开记录板:「有来向,没身份。先记到这里。」没人知道那声音究竟是什么。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-emo-a-02",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-EMO-A-02",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"emotional_stability"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-A-02-A",
|
||||||
|
"label": "「把声音来向再听一遍,和地图对上。万一是人,我们至少知道从哪里找。」",
|
||||||
|
"next": "b-emo-a-d15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-A-02-B",
|
||||||
|
"label": "「先保证队伍都在。等风缓再查,眼下谁也别离开。」",
|
||||||
|
"next": "b-emo-a-d16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-A-02-C",
|
||||||
|
"label": "「先报方位,试着在警戒线内呼叫;等风缓再去核实。谁也不要为一个听不清的声音冲进风里。」",
|
||||||
|
"next": "b-emo-a-d17"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d18",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "风势缓下来后,沈铎按现场规程安排核查,队伍只在他确认通路后移动。远处那声像人喊的声音来自断墙上的孔洞;此前谁都没有足够证据断言。众人记下位置,再清点物资,记录箱被找回,黄昏前继续向观星台行进。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-a-d19"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-a-d19",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "风停,浮沙被刮走整整一层。主街中段露出一截石阶和圆形基座的边缘。阿棠用袖子擦着刻纹:「哎……这纹路,观星台。它这是——给咱们开门呢?」黄昏前,你们登台。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,217 @@
|
|||||||
|
{
|
||||||
|
"id": "b-emo-b",
|
||||||
|
"dimension": "emotional_stability",
|
||||||
|
"anchor": {
|
||||||
|
"chapter": "chapter3",
|
||||||
|
"afterNode": "c3-d1b"
|
||||||
|
},
|
||||||
|
"estimatedMinutes": 8,
|
||||||
|
"startNodeId": "b-emo-b-d1",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d1",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "上午,宫墟安门内,祭祀坑阵列之间。抵近记录进行到一半,走在最前的阿棠忽然整个人僵住——脚下传来一声极轻的「咔」。她低头:靴子底下,一块夯土的颜色和周围毫无差别,但它在极慢、极慢地往下沉。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-b-d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d2",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "(一动不动,声音反而很平)哎。我踩着东西了。……是活的那种,还是死的那种?",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "b-emo-b-d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d3",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "没人答得上来。同一时刻,阵列深处,一面坑壁无声地滑开一道缝——黑黢黢的,看不见底。紧接着,头顶台基的边缘开始簌簌落土:重量移走了,结构在重新找平衡。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-b-d4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d4",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "(极低,极慢)谁。也。别。动。",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "b-emo-b-d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d5",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "老鬼",
|
||||||
|
"text": "(脸白了)祖宗诶……这地方的东西,不知道埋了多少年了怎么还上班呢……",
|
||||||
|
"sprite": "laogui",
|
||||||
|
"next": "b-emo-b-d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d6",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "(蹲下,远远观察压板的边缘,声音是全队唯一没变调的)沉速很慢。非常慢。——它在动,但它不着急。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "cb-emo-b-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d7",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "沈铎立即接管,其他队员退到警戒线。阿棠在受训人员协助下安全离开;你没看清压板沉降的过程。她看见你手里的空记录板,问:「刚才那一下,真有东西动了吗?」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-b-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d8",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你报告读数后清空外围,沈铎接管排险。阿棠安全撤出;你在外围听着频道里一段段报平安,直到最后一声才松开手。坑壁裂缝在视野之外开到最宽,没人及时拍下全貌。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-b-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d9",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你们退到警戒线外留影;沈铎与受训接应人员按既定预案带阿棠撤出。镜头拍到坑壁开缝的瞬间,却因距离太远,板面的刻痕看不清。阿棠看着那张照片:「原来刚才我站的是这里。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-b-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d10",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "沈铎按既定预案接管排险,其他队员退出阵列。阿棠在受训人员协助下安全离开。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-b-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-emo-b-01",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-EMO-B-01",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"emotional_stability"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-B-01-A",
|
||||||
|
"label": "「先通知沈铎,按预案撤出阿棠;其余人退出阵列。」",
|
||||||
|
"next": "b-emo-b-d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-B-01-B",
|
||||||
|
"label": "「把观察到的沉速与缝宽报给沈铎;我去清空外围。」",
|
||||||
|
"next": "b-emo-b-d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-B-01-C",
|
||||||
|
"label": "「全员退到警戒线,远距留影,排险交沈铎处置。」",
|
||||||
|
"next": "b-emo-b-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-B-01-TIMEOUT",
|
||||||
|
"label": "由沈铎接管排险",
|
||||||
|
"next": "b-emo-b-d10"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"timer": {
|
||||||
|
"seconds": 8,
|
||||||
|
"optionId": "CB-EMO-B-01-TIMEOUT"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d11",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "坑壁的缝开到最宽,又缓缓合拢。阿棠安全撤出后,大家在警戒线外把各自见到的片段拼成同一张记录:缝里有成排的陶范——铸鼎的模子,一人来高,内壁的纹样与台基上七鼎六簋相似;缝口下方的机件锈蚀严重。这些观察不足以判定机关失效,需等待专业排险后确认。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-b-d12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d12",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "范是耗材,铸一器毁一范。整排整排地留在这儿——(停顿)说明铸完那一批之后,就再没有下一批了。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "b-emo-b-d13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d13",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "(看着那道正在合拢的缝,声音沉下去)都记住。别拿锈色猜它死没死透。今天先记,不去试。",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "b-emo-b-d14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d14",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "阿棠",
|
||||||
|
"text": "(一屁股坐在安全线的土坎上,笑出了眼泪)吓死我了……结果到现在也不知道它是死是活。",
|
||||||
|
"sprite": "atang",
|
||||||
|
"next": "cb-emo-b-02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d15",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你们留在安全线内,把剩下可见的纹样拍成序列。尘雾一阵阵遮住镜头;温简喊停时,最后几张已只有模糊轮廓。影像有用,但不够清:待会儿上台基辨纹,阿棠要对到第三件才敢拍板「就是这个」。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-b-d18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d16",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "沈铎在安全线外的图志上标出方位:「记在这儿。账先欠着,城跑不了。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-b-d18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d17",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "相机递到手上又被你推了回去。缝合拢的声音很轻,像叹气。老鬼把三脚架收了,摩挲着架腿,破天荒说了句不市侩的话:「人家关上门的东西……咱们今天,就不硬敲了吧。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-emo-b-d18"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-emo-b-02",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-EMO-B-02",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"emotional_stability"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-B-02-A",
|
||||||
|
"label": "「能记多少记多少:留在安全线内把远处可见的纹样拍下来——它合它的,我们记我们的。」",
|
||||||
|
"next": "b-emo-b-d15"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-B-02-B",
|
||||||
|
"label": "「拍两张全景就够,标桩记位。它想合就让它合——账先欠着,城跑不了。」",
|
||||||
|
"next": "b-emo-b-d16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-EMO-B-02-C",
|
||||||
|
"label": "「都收手,别拍了——它关门的事,就让它关完。」",
|
||||||
|
"next": "b-emo-b-d17"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-emo-b-d18",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "采集收尾,全队向台基合拢。陶范的纹样与「铸完那一批,再没有下一批」的话,跟着你们一起上去。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
{
|
||||||
|
"id": "b-val-a",
|
||||||
|
"dimension": "values",
|
||||||
|
"anchor": {
|
||||||
|
"chapter": "chapter2",
|
||||||
|
"afterNode": "c2-branch-anchor"
|
||||||
|
},
|
||||||
|
"estimatedMinutes": 8,
|
||||||
|
"startNodeId": "b-val-a-d1",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d1",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "午后,东城前往宫墟的半路,一片废弃里坊。井台早干了,井沿边蜷着一个妇人和一个五六岁的孩子。妇人看见你们,第一眼看的不是你们的脸,是水囊。她看了孩子一眼,才慢慢站起来——没有伸手。她把手腕上一只银镯褪下来,摊在掌心,举到你们面前。里坊外的官道上有零星人影往城门方向挪——城门楼那头,隐隐飘来一缕熬粥的米气。阿棠顺着看了一眼:「早上路过,南门里侧的施粥棚开着呢,还发饼。排队的都是妇幼。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d2",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "妇人",
|
||||||
|
"text": "(声音哑的,但每个字都放得很稳)买水。给孩子一口就行。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d3",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "队里的饮水,按定数,只够两天。沈铎没说话。阿棠攥紧了自己的水囊。老鬼别过脸去。【若 wenjian_health != weak:温简在记录板的边角,无意识地画着什么。】【若 wenjian_health = weak:通讯频道里,传来温简翻页的声音。】",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d4",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "妇人",
|
||||||
|
"text": "(见没人动,把镯子又往前送了半寸,补了一句)是足银的。……不脏,我天天擦。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "cb-val-a-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d5",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "妇人接水囊的手抖了一下。她先喂孩子——孩子喉咙里咕咚咕咚的声音,一路没人提,谁都听见了。然后她捧着水囊,往自己嘴里倒了极小的一口,把剩下的双手递还给你——你没让她倒回去。走出很远,阿棠才小声说:「刚才我水囊都攥出汗了。还好你给了……我是说——队里的水,够吗?」她替你把代价问出了口。够的。但往后两天,全队喝水都开始数着口数。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d6",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "妇人接杯子的手停了一停——她懂了。她没争,先喂孩子。孩子喝了一半,把杯子举到她嘴边,她抿了一小口,就推回去。一来一回。走出去半条街,阿棠忽然说:「哎,你们数没数,那杯水转了四圈。」没人接话。——一杯水,四个来回;水囊的水位,浅了浅浅一格。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d7",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "妇人深深看了你一眼——那一眼里没有感激,有一种被还回了人的平静。她把镯子塞进你手里,动作快得像怕你反悔。老鬼凑过来,啧啧:「新人,上道啊。」这句是夸是损,他自个儿笑眯眯地不解释。【若 wenjian_health != weak:镯子由温简当场登记为「民间有偿征集品 CW-0322」。】【若 wenjian_health = weak:镯子你代记「民间有偿征集品 CW-0322」,回营交温简补录入册。】",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d8",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "你说了施粥处的方向,说得很详细,孩子在听,很乖。妇人点头,背起孩子,走了两步,又走回来,把银镯塞进你手里:「不是买水了。谢谢你指得这么细。」她走了。老鬼在你身后挠头:「施粥处?前儿在城里还听人说断了米……哎,看这一炉粥气,是兴许又续上了。城里的事,一天一个样。」【若 wenjian_health != weak:银镯由温简当场登记为「民间赠与 CW-0322·答谢指路·代收,处置呈档案馆裁定」。】【若 wenjian_health = weak:银镯你代记「民间赠与 CW-0322·代收」,回营交温简补录入册,处置呈档案馆裁定。】",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-val-a-01",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-VAL-A-01",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"values"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-A-01-A",
|
||||||
|
"label": "(解下水囊递过去)「倒吧。倒多少,您自己定。」",
|
||||||
|
"next": "b-val-a-d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-A-01-B",
|
||||||
|
"label": "(拿出队里的搪瓷杯,倒满一杯)「给孩子。一杯。」",
|
||||||
|
"next": "b-val-a-d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-A-01-C",
|
||||||
|
"label": "(接过银镯,解下两只水囊)「市卖的价,一镯两囊。您不欠我们,我们不欠您。」",
|
||||||
|
"next": "b-val-a-d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-A-01-D",
|
||||||
|
"label": "「水是有数的,给不了。(蹲下来,对孩子)前面城门有官舍施粥处,那儿有水有粥——让阿娘带你去,认得路吗?」",
|
||||||
|
"next": "b-val-a-d8"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d9",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "妇人背着孩子刚走出十几步,孩子忽然趴在妇人肩头,冲你们喊:「那边的台子上,夜里有人点灯!」他指的是观星台的方向。夜禁的城,满城无灯,而有人在高台上点灯。沈铎顺着孩子的手看过去,收回目光:「……上台看看。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d10"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d10",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "离开里坊,官道上,日头已经西斜——从里坊出来,一个时辰的路走完了。沈铎赶上来与你并肩,翻开册子——他是规程人,每一口水他都有数。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d11",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "【若 cb_val_a_water = d_redirect:南门方向,粥棚的灶火还亮着。在前头进城的岔路口,阿棠认出了那对母子:两人已从粥棚回来,孩子捧着一只盛热粥的碗。你指的路,是活的。老鬼那句「断了米」,只是前几日的旧闲话。】",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d12"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d12",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "今天的账。(停顿)水囊的水位,我看过。你想怎么记?",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "cb-val-a-02"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d13",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "沈铎提笔,照实记了。【若 cb_val_a_water = a_give:施水一囊。】【若 cb_val_a_water = b_cup:施水一杯。】【若 cb_val_a_water = c_trade:售水两囊,得镯一支(CW-0322,有偿征集)。】【若 cb_val_a_water = d_redirect:水支出零,受赠镯一支(CW-0322,赠与代收)。】记完把册子合上,说了句题外话:「我爹以前是仓管。他说,账上干净的队,走路不回头看。」——这是全章他第一次提家人。顿了顿,又补了半句:「就是往后再遇这种事,先说一声再给——账要干净,队也得知道水去哪儿了。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d14",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "沈铎看了你两秒,提笔,先记「待分类杂项」及每项实数:【若 cb_val_a_water = a_give:施水一囊,未收物。】【若 cb_val_a_water = b_cup:施水一杯,未收物。】【若 cb_val_a_water = c_trade:售水两囊,得镯一支(CW-0322,有偿征集)。】【若 cb_val_a_water = d_redirect:水支出零,受赠镯一支(CW-0322,赠与代收)。】既没有把水记作漏损,也没把赠镯写成买卖;回营时还得补分录。合上册子他说:「账平了,手续还欠一笔。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d15",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "沈铎没推辞,按实数记了:【若 cb_val_a_water = a_give:施水一囊,未收物。】【若 cb_val_a_water = b_cup:施水一杯,未收物。】【若 cb_val_a_water = c_trade:售水两囊,得镯一支(CW-0322,有偿征集)。】【若 cb_val_a_water = d_redirect:水支出零,受赠镯一支(CW-0322,赠与代收)。】把笔递给你签。你签的时候他说:「推给我记的,和照实记的,册子上长一个样。」(停顿)「不一样的地方,你自己知道在哪儿。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-a-d16"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-val-a-02",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-VAL-A-02",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"values"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-A-02-A",
|
||||||
|
"label": "「照实记。给出多少、收了什么,都按实数。记在我名下。」",
|
||||||
|
"next": "b-val-a-d13"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-A-02-B",
|
||||||
|
"label": "「先记到『路上杂项』,备注水和镯子的实数,回营再分类。别在路边把来龙去脉都写完。」",
|
||||||
|
"next": "b-val-a-d14"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-A-02-C",
|
||||||
|
"label": "「账是队长的。你定,我签。」",
|
||||||
|
"next": "b-val-a-d15"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-a-d16",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "账毕,日头西斜。黄昏时,你们登上观星台。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
{
|
||||||
|
"id": "b-val-b",
|
||||||
|
"dimension": "values",
|
||||||
|
"anchor": {
|
||||||
|
"chapter": "chapter3",
|
||||||
|
"afterNode": "c3-d1b"
|
||||||
|
},
|
||||||
|
"estimatedMinutes": 8,
|
||||||
|
"startNodeId": "b-val-b-d1",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d1",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "上午,宫墟安门内。穿过暗门后,下最后几级台阶时阿棠跳快了两步,落地崴了一下脚踝——她原地转了转脚腕,龇了龇牙:\"没事没事,走。\"台阶尽头站着个不该出现在这里的人——守墓老人。他不是一个人:背上驮着个十七八岁的少年,少年一条小腿用布条和草灰糊着,布条渗着暗色。老人把少年放下来,动作轻得不像他这个年纪。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-b-d2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d2",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "守墓老人",
|
||||||
|
"text": "(不绕弯子)陵区昨夜又塌了一角。这娃的爹在底下压着呢——这话回头说。(拍拍少年的肩)先救小的。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-b-d3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d3",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "温简",
|
||||||
|
"text": "(查看伤口,低声)石像生砸的。创面糊着草灰——老法子,撑不了多久,会烂。",
|
||||||
|
"sprite": "wenjian",
|
||||||
|
"next": "b-val-b-d4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d4",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "沈铎",
|
||||||
|
"text": "(打开物资箱,停住)外伤药一份——清创的药粉与止痛的丸剂合装为一「份」。(抬头)规程:这是撤离日的战备药。任何人在撤离日之前动用,需采集员裁断。",
|
||||||
|
"sprite": "shenduo",
|
||||||
|
"next": "b-val-b-d5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d5",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "又是你裁。阿棠抱着手臂站在一边,没吭声。老鬼的眼珠在药瓶和金饼之间转了一圈,难得地没说话。少年一直看着你的鞋尖,忽然开口,声音没长开:",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-b-d6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d6",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "守陵少年",
|
||||||
|
"text": "……要是为难,就算了。我阿爷说,药是有主的东西,跟命一样。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "cb-val-b-01"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d7",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "药粉撒上去的时候,少年咬着牙,一声没吭,只有抓着台阶的手指节发白。老人别过脸去,朝着陵区的方向,低声说了句什么,像骂人,又像还愿。沈铎在册子上记:「战备药动用,采集员裁断,事由:救人。」——他记「救人」两个字的时候,笔锋比平时重。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-b-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d8",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "老人没恼。他点点头:「药有主,理是这个理。」他弯腰去背少年,少年却回头,解下自己腰里挂着的一小包草药,轻轻放在阿棠脚边(她今早下台阶崴的那一下,谁都没在意,他看出来了):「阿姐,这个……敷上,凉。」老人驮着他这就动身往城里去——水门里的药铺,半个时辰的路,天黑前能来回。走出十几步,少年趴在老人背上,忽然回头,冲台基的方向努嘴,把一句话喊给阿棠:「阿姐!上台别走正台阶——背阴那面,排水沟边上,有我们的路!」老鬼低头看着那包草药,半天没吭声,收摊似的咂了咂嘴:「哥哥我活这么大,头一回觉得穷。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-b-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d9",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "拆开合装,清创粉给了少年,止痛丸留在队里。上药后温简查看创面,只说了一句:「清创及时,这片创面问题不大。」——不打包票。少年被安置在台阶边的阴凉里等你们收工,疼劲上来时压着声哼,老人给他扇风,一下,一下。没人说你的分配不好。也没人再提它。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-b-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d10",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "老人捧着药瓶,看了你很久很久。然后他做了一件全队都没想到的事:他只用了一半,仔仔细细封好剩下的一半,双手还给你:「药是救命的东西。救命的东西,不能断在你们手里。」(顿)「娃他爹常说,活人比死人难治——你们这些顾活人的,药得留着。」阿棠在旁边,眼圈忽然红了,扭头去看祭祀坑。沈铎在册子上记:「战备药半份,交守陵人保管——返还。」记完他抬眼看你,只说了一句:「这次他只用了一半。下不为例——这种赌,队里赌不起第二次。」",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "b-val-b-d11"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cb-val-b-01",
|
||||||
|
"type": "choice",
|
||||||
|
"pointId": "CB-VAL-B-01",
|
||||||
|
"prompt": "你怎么做?",
|
||||||
|
"dimensions": [
|
||||||
|
"values"
|
||||||
|
],
|
||||||
|
"options": [
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-B-01-A",
|
||||||
|
"label": "「用。战备战备,备的就是'万一'——现在就是那个万一。清创,上药,我来搭手。」",
|
||||||
|
"next": "b-val-b-d7"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-B-01-B",
|
||||||
|
"label": "「药动不了。(对老人)城里有大夫——我画路,再给您备一份金饼,这就动身送去,来得及。」",
|
||||||
|
"next": "b-val-b-d8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-B-01-C",
|
||||||
|
"label": "「拆开来用:清创的药粉给他——感染会要命;止痛的留下——疼不会。两份药,各救各的急。」",
|
||||||
|
"next": "b-val-b-d9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "CB-VAL-B-01-D",
|
||||||
|
"label": "(把药瓶放进老人手里)「您拿着。用多少,您定——守陵的人,比我们懂伤。」",
|
||||||
|
"next": "b-val-b-d10"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b-val-b-d11",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "【若 cb_val_b_medicine != b_refer:少年拄着木枝,一瘸一拐把你们引到台基背阴面,亲手拨开荒草:「这边。我阿爷说,给神走的路修得高,给人走的路修得低。走低的,不摔跤。」】【若 cb_val_b_medicine = b_refer:老人和少年已往城里去。阿棠照着少年的喊话,在排水沟边找到荒草,拨开它。她学着少年的口气说:「给神走的路修得高,给人走的路修得低。走低的,不摔跤。」】",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "$resume"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+122
-29
@@ -32,7 +32,7 @@
|
|||||||
"id": "c1-d1a",
|
"id": "c1-d1a",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "馆长",
|
"speaker": "馆长",
|
||||||
"text": "我们不收藏器物。器物会腐烂,会碎,会被误读。我们收藏的是人怎么活过——一座城消亡前,城里的人吃什么、怕什么、爱什么、最后那几天在做什么。这些东西,仪器采不到,只有人能采集。所以我们需要的采集员,必须自己也是「活过的」。穿越要烧燃料,而燃料只有一种——你的真实人生。烧掉的记忆不会消失,它们会存在档案馆的展厅里,这是后话。",
|
"text": "我们不收藏器物。器物会腐烂,会碎,会被误读。我们收藏的是人怎么活过——一座城消亡前,城里的人吃什么、怕什么、爱什么、最后那几天在做什么。这些东西,仪器采不到,只有人能采集。所以我们需要的采集员,必须自己也是「活过的」。穿越要烧燃料。燃料这东西认两种:你讲出来的故事,和你在讲述点门前站过的那阵沉默——讲,是给;不说,把那阵沉默原样交给它,也是给。讲出来的记忆不会消失,它们会存在档案馆的展厅里,这是后话。",
|
||||||
"sprite": "curator",
|
"sprite": "curator",
|
||||||
"next": "c1-d1b"
|
"next": "c1-d1b"
|
||||||
},
|
},
|
||||||
@@ -72,7 +72,7 @@
|
|||||||
"id": "c1-d3a",
|
"id": "c1-d3a",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "馆长",
|
"speaker": "馆长",
|
||||||
"text": "这扇叫离别之门。每个采集员的第一次穿越,都从它开始。它不开锁,不收凭证——它要一件祭品:一段真实的离别。",
|
"text": "这扇叫离别之门。每个采集员的第一次穿越,都从它开始。它不开锁,不收凭证——它认一次交托:一段真实的离别。讲出来是交托,把那阵沉默原样交给它,也是交托;不提交记忆,门也照样启动。",
|
||||||
"sprite": "curator",
|
"sprite": "curator",
|
||||||
"next": "c1-d3b"
|
"next": "c1-d3b"
|
||||||
},
|
},
|
||||||
@@ -90,7 +90,10 @@
|
|||||||
"pointId": "C1-02",
|
"pointId": "C1-02",
|
||||||
"mechanism": "记忆钥匙",
|
"mechanism": "记忆钥匙",
|
||||||
"prompt": "请讲述一段你真实经历过的离别。可以是一个人、一个地方、一段关系、一个阶段。说什么、说多少、怎么说,都由你。沉默也是回答。",
|
"prompt": "请讲述一段你真实经历过的离别。可以是一个人、一个地方、一段关系、一个阶段。说什么、说多少、怎么说,都由你。沉默也是回答。",
|
||||||
"dimensions": ["attachment", "self_disclosure"],
|
"dimensions": [
|
||||||
|
"attachment",
|
||||||
|
"self_disclosure"
|
||||||
|
],
|
||||||
"followUp": {
|
"followUp": {
|
||||||
"maxQuestions": 1,
|
"maxQuestions": 1,
|
||||||
"strategy": "Jev 六维缺失检测(五感/人物/时空/情绪),按幻境渲染价值取最高缺口,馆长口吻模板追问一句;「记不清了」为合法回答,馆长回应:记不清本身,档案馆也会收好。"
|
"strategy": "Jev 六维缺失检测(五感/人物/时空/情绪),按幻境渲染价值取最高缺口,馆长口吻模板追问一句;「记不清了」为合法回答,馆长回应:记不清本身,档案馆也会收好。"
|
||||||
@@ -104,11 +107,36 @@
|
|||||||
"olfactory_gustatory": "那天的空气里,有没有一种味道留到了现在?——不用准确,你记得的那一点就好。"
|
"olfactory_gustatory": "那天的空气里,有没有一种味道留到了现在?——不用准确,你记得的那一点就好。"
|
||||||
},
|
},
|
||||||
"variants": [
|
"variants": [
|
||||||
{ "signature": "温暖×永失", "variantId": "sealed_love_tomb", "label": "「爱被封存」墓室——门后是墓道尽头一方石室,四壁绘着双人同耕、同炊、同观星的壁画,色彩温暖如新。石室中央两座并排的陶棺,棺盖上落满花瓣状的朱砂。空气是静的,静得像有人刚刚还在这里说话。基调:珍重,不是悲伤——有人把最好的东西封存了起来。", "tint": "#d4a96a" },
|
{
|
||||||
{ "signature": "悲恸", "variantId": "war_graves", "label": "「战乱乱冢」——门后是荒野,低低的土丘一座挨一座,没有碑,只有削尖的木桩。风把灰白的纸钱碎屑贴在脚踝上,远处有乌鸦起落,天色是黄昏将尽的那种灰蓝。基调:来不及告别——所有话都没说出口,所有名字都没来得及刻上。", "tint": "#5a5a72" },
|
"signature": "温暖×永失",
|
||||||
{ "signature": "释然", "variantId": "empty_post_station", "label": "「远行者的空驿」——门后是一座山间驿站,晨光,桌椅收拾得干干净净,灶膛里的灰还是温的,人已经走了。墙上挂着一件没带走的旧斗篷,桌上压着半张写了又划掉的字条。基调:告别已经完成——走得干净,留得体面,剩下的路各走各的。", "tint": "#7ba7a0" },
|
"variantId": "sealed_love_tomb",
|
||||||
{ "signature": "怨怼", "variantId": "desecrated_niche", "label": "「被凿毁的合葬龛」——门后是崖壁上的佛龛式墓龛,本该并坐的两尊石像,其中一尊被凿去了面目,凿痕新鲜得刺眼。香烛还燃着,蜡泪流到龛外。基调:被亏欠——有人毁了约,而另一个人连讨说法的对象都没有。", "tint": "#8a4b4b" },
|
"label": "「爱被封存」墓室——门后是墓道尽头一方石室,四壁绘着双人同耕、同炊、同观星的壁画,色彩温暖如新。石室中央两座并排的陶棺,棺盖上落满花瓣状的朱砂。空气是静的,静得像有人刚刚还在这里说话。基调:珍重,不是悲伤——有人把最好的东西封存了起来。",
|
||||||
{ "signature": "沉默", "variantId": "generic_fog", "label": "【沉默档 · 门照常开启】门对所有人开,这是铁律。门后是朦胧的星光与风声,不对应任何一段具体记忆——它以另一种方式认得你。", "tint": "#4a4a56" }
|
"tint": "#d4a96a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "悲恸",
|
||||||
|
"variantId": "war_graves",
|
||||||
|
"label": "「战乱乱冢」——门后是荒野,低低的土丘一座挨一座,没有碑,只有削尖的木桩。风把灰白的纸钱碎屑贴在脚踝上,远处有乌鸦起落,天色是黄昏将尽的那种灰蓝。基调:来不及告别——所有话都没说出口,所有名字都没来得及刻上。",
|
||||||
|
"tint": "#5a5a72"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "释然",
|
||||||
|
"variantId": "empty_post_station",
|
||||||
|
"label": "「远行者的空驿」——门后是一座山间驿站,晨光,桌椅收拾得干干净净,灶膛里的灰还是温的,人已经走了。墙上挂着一件没带走的旧斗篷,桌上压着半张写了又划掉的字条。基调:告别已经完成——走得干净,留得体面,剩下的路各走各的。",
|
||||||
|
"tint": "#7ba7a0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "怨怼",
|
||||||
|
"variantId": "desecrated_niche",
|
||||||
|
"label": "「被凿毁的合葬龛」——门后是崖壁上的佛龛式墓龛,本该并坐的两尊石像,其中一尊被凿去了面目,凿痕新鲜得刺眼。香烛还燃着,蜡泪流到龛外。基调:被亏欠——有人毁了约,而另一个人连讨说法的对象都没有。",
|
||||||
|
"tint": "#8a4b4b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "沉默",
|
||||||
|
"variantId": "generic_fog",
|
||||||
|
"label": "【沉默档 · 门照常开启】「留白的长廊」——门后是一条望不见头的长廊,两壁空无一物,像还没落笔的纸。风从走廊尽头吹来,带着很远的草香;走出去很远,身后的门还开着一条缝。基调:留白——没说出口的部分,也被好好收着。",
|
||||||
|
"tint": "#4a4a56"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"next": "c1-d4"
|
"next": "c1-d4"
|
||||||
},
|
},
|
||||||
@@ -116,7 +144,7 @@
|
|||||||
"id": "c1-d4",
|
"id": "c1-d4",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "馆长",
|
"speaker": "馆长",
|
||||||
"text": "【若 c1_02_signature != silent:门开了。你看——它要的不是故事讲得好不好,是故事真不真。】【若 c1_02_signature = silent:门开了。沉默也是档案的一种质地——它认得站在它面前的人,不一定需要言语。】你的记忆已经收进档案馆展厅,编号 C-2049-壹。它现在是你的了:随时可以回看,永远不会被任何人看见,除非你亲手授权——展厅,以及往后路上任何由它生出的幻境,都只有你能进入。在旁人眼里,你只是安静地站了一会儿。",
|
"text": "【若 c1_02_signature != silent:门开了。你看——它要的不是故事讲得好不好,是人真不真。】【若 c1_02_signature = silent:门开了。沉默也是档案的一种质地——它认得站在它面前的人,不一定需要言语。】【若 c1_02_signature != silent:你的记忆已经收进档案馆展厅,编号 C-2049-壹。它现在是你的了:随时可以回看,永远不会被任何人看见,除非你亲手授权——展厅,以及往后路上任何由它生出的幻境,都只有你能进入。在旁人眼里,你只是安静地站了一会儿。】【若 c1_02_signature = silent:展厅为你留了一页空档,编号 C-2049-壹——此刻还没有字,哪天你想说了,它随时接着。展厅,以及往后路上任何由它生出的幻境,都只有你能进入。在旁人眼里,你只是安静地站了一会儿。】",
|
||||||
"sprite": "curator",
|
"sprite": "curator",
|
||||||
"next": "c1-d4a"
|
"next": "c1-d4a"
|
||||||
},
|
},
|
||||||
@@ -141,12 +169,30 @@
|
|||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C1-03",
|
"pointId": "C1-03",
|
||||||
"prompt": "馆长:出发前我要问一个问题,这个问题没有标准答案,档案馆只是需要知道怎么给你派活——你为什么愿意去?",
|
"prompt": "馆长:出发前我要问一个问题,这个问题没有标准答案,档案馆只是需要知道怎么给你派活——你为什么愿意去?",
|
||||||
"dimensions": ["values"],
|
"dimensions": [
|
||||||
|
"values"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "a_truth", "label": "「我想知道它发生了什么。」一座完整的城在你面前,不挖开看看,这辈子都不甘心。", "next": "c1-d4c" },
|
{
|
||||||
{ "id": "b_thrill", "label": "「听起来,这比坐在资料室里有意思多了。」说白了,谁不想真的走进自己研究的东西里去。", "next": "c1-d4c" },
|
"id": "a_truth",
|
||||||
{ "id": "c_escape", "label": "「最近的日子……换个地方待三十天,挺好。」现实里有些事情,正好可以放一放。", "next": "c1-d4c" },
|
"label": "「我想知道它发生了什么。」一座完整的城在你面前,不挖开看看,这辈子都不甘心。",
|
||||||
{ "id": "d_trust", "label": "「有人希望我去。」这件事不完全是为自己做的。", "next": "c1-d4c" }
|
"next": "c1-d4c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b_thrill",
|
||||||
|
"label": "「听起来,这比坐在资料室里有意思多了。」说白了,谁不想真的走进自己研究的东西里去。",
|
||||||
|
"next": "c1-d4c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_escape",
|
||||||
|
"label": "「最近的日子……换个地方待三十天,挺好。」现实里有些事情,正好可以放一放。",
|
||||||
|
"next": "c1-d4c"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d_trust",
|
||||||
|
"label": "「有人希望我去。」这件事不完全是为自己做的。",
|
||||||
|
"next": "c1-d4c"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -210,12 +256,31 @@
|
|||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C1-04",
|
"pointId": "C1-04",
|
||||||
"prompt": "老鬼的提议悬在空气里,你接不接话?",
|
"prompt": "老鬼的提议悬在空气里,你接不接话?",
|
||||||
"dimensions": ["values", "conflict_style"],
|
"dimensions": [
|
||||||
|
"values",
|
||||||
|
"conflict_style"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "a_confront", "label": "「东西留在原地,才叫历史。带回来,就只是赃物。」(当着所有人,把话说死)", "next": "c1-04-ra" },
|
{
|
||||||
{ "id": "b_deflect", "label": "「鬼哥这话说的——先看有什么,再说带不带呗。」(打个哈哈,不接也不顶)", "next": "c1-04-rb" },
|
"id": "a_confront",
|
||||||
{ "id": "c_redirect", "label": "(不接话,转向沈铎)「队长,落点方案定了么?」(用正事把这个话题盖过去)", "next": "c1-04-rc" },
|
"label": "「东西留在原地,才叫历史。带回来,就只是赃物。」(当着所有人,把话说死)",
|
||||||
{ "id": "d_play_along", "label": "(笑)「行啊,回头鬼哥带我见识见识行情。」(顺着他说,看看他到底想干什么)", "next": "c1-04-rd" }
|
"next": "c1-04-ra"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b_deflect",
|
||||||
|
"label": "「鬼哥这话说的——先看有什么,再说带不带呗。」(打个哈哈,不接也不顶)",
|
||||||
|
"next": "c1-04-rb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_redirect",
|
||||||
|
"label": "(不接话,转向沈铎)「队长,落点方案定了么?」(用正事把这个话题盖过去)",
|
||||||
|
"next": "c1-04-rc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d_play_along",
|
||||||
|
"label": "(笑)「行啊,回头鬼哥带我见识见识行情。」(顺着他说,看看他到底想干什么)",
|
||||||
|
"next": "c1-04-rd"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -278,20 +343,38 @@
|
|||||||
"id": "c1-05",
|
"id": "c1-05",
|
||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C1-05",
|
"pointId": "C1-05",
|
||||||
"prompt": "碑额拓片特写:上方是屋顶(宀),屋顶之下一个安坐的人形,膝前有衣袂垂落之态——宀下一个女子安坐的形象。这是什么字?它刻在碑额上,意味着这是什么碑?",
|
"prompt": "碑额拓片特写:上方是屋顶(宀),屋顶之下一个安坐的人形,膝前有衣袂垂落之态——宀下一个女子安坐的形象。另有两条旁证:碑为圆首、无碑穿、无榫、下无龟趺座,不合墓碑规制;此碑出土于苍梧北门外官道旁,碑面朝城。这是什么字?它刻在碑额上,意味着这是什么碑?",
|
||||||
"dimensions": ["culture"],
|
"dimensions": [
|
||||||
|
"culture"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "stele_guardian", "label": "「安」。室中有女,安坐不惊——这是镇城安民碑,碑额朝向即古城安门所在。", "next": "c1-05-ok" },
|
{
|
||||||
{ "id": "stele_tomb", "label": "「安」。安魂之意——这是墓碑,镇的是墓主的安宁。", "next": "c1-05-miss" },
|
"id": "stele_guardian",
|
||||||
{ "id": "stele_merit", "label": "「宁」。定鼎纪功——这是记功碑,记的是立碑人的功业。", "next": "c1-05-miss" },
|
"label": "「安」。室中有女,安坐不惊——这是镇城安民碑,碑额朝向即古城安门所在。",
|
||||||
{ "id": "stele_boundary", "label": "「定」。划定疆界——这是界碑,标的是城的四至。", "next": "c1-05-miss" }
|
"next": "c1-05-ok"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "stele_tomb",
|
||||||
|
"label": "「安」。安魂之意——这是墓碑,镇的是墓主的安宁。",
|
||||||
|
"next": "c1-05-miss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "stele_merit",
|
||||||
|
"label": "「宁」。定鼎纪功——这是记功碑,记的是立碑人的功业。",
|
||||||
|
"next": "c1-05-miss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "stele_boundary",
|
||||||
|
"label": "「定」。划定疆界——这是界碑,标的是城的四至。",
|
||||||
|
"next": "c1-05-miss"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c1-05-ok",
|
"id": "c1-05-ok",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "沈铎",
|
"speaker": "沈铎",
|
||||||
"text": "(点头)碑额朝向安门,碑身镇的是城的安危。西北角那片宫墟,勘探七年没动过——如果苍梧人有「安放」什么的地方,就在那里。",
|
"text": "(点头)碑额朝向安门,碑形不合墓制、又立在城外官道旁——三条证据同向:碑身镇的是城的安危。西北角那片宫墟,勘探七年没动过——如果苍梧人有「安放」什么的地方,就在那里。",
|
||||||
"sprite": "shenduo",
|
"sprite": "shenduo",
|
||||||
"next": "c1-d6c"
|
"next": "c1-d6c"
|
||||||
},
|
},
|
||||||
@@ -364,10 +447,20 @@
|
|||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C1-06",
|
"pointId": "C1-06",
|
||||||
"prompt": "穿越落点,选哪个?",
|
"prompt": "穿越落点,选哪个?",
|
||||||
"dimensions": ["risk"],
|
"dimensions": [
|
||||||
|
"risk"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "a_safe", "label": "稳妥落点:城外驿站,走一天官道进城。", "next": "c1-cutscene" },
|
{
|
||||||
{ "id": "b_precise", "label": "精确落点:直接落在苍梧城内,西市。", "next": "c1-cutscene" }
|
"id": "a_safe",
|
||||||
|
"label": "稳妥落点:城外驿站,走一天官道进城。",
|
||||||
|
"next": "c1-cutscene"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b_precise",
|
||||||
|
"label": "精确落点:直接落在苍梧城内,西市。",
|
||||||
|
"next": "c1-cutscene"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+161
-45
@@ -32,7 +32,7 @@
|
|||||||
"id": "c2-d2",
|
"id": "c2-d2",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "老鬼",
|
"speaker": "老鬼",
|
||||||
"text": "(营地晨会,他举起一个瓷瓶)防瘴的药粉,满打满算,两人份。这东西是定数,档案馆给的量抠得死,多一两都没有。",
|
"text": "(营地晨会,他举起一个瓷瓶)防瘴的药粉,满打满算,两人份——两个健康人七天的预防量。真要给病号治疟疾,两份连服才压得住,一分都剩不下。这东西是定数,档案馆给的量抠得死,多一两都没有。",
|
||||||
"sprite": "laogui",
|
"sprite": "laogui",
|
||||||
"next": "c2-d2a"
|
"next": "c2-d2a"
|
||||||
},
|
},
|
||||||
@@ -81,19 +81,38 @@
|
|||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C2-01",
|
"pointId": "C2-01",
|
||||||
"prompt": "两人份的药,三个人要。按规程,由你裁断——",
|
"prompt": "两人份的药,三个人要。按规程,由你裁断——",
|
||||||
"dimensions": ["values", "conflict_style"],
|
"dimensions": [
|
||||||
|
"values",
|
||||||
|
"conflict_style"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "a_weakest", "label": "「先给温简。病着的人等不起,外勤和补给可以想办法。」", "next": "c2-01-ra" },
|
{
|
||||||
{ "id": "b_exposure", "label": "「按暴露风险分:队长和老鬼。温简留在营地,反而最不需要出门。」", "next": "c2-01-rb" },
|
"id": "a_weakest",
|
||||||
{ "id": "c_split", "label": "「两人份的药,三个人分——匀一匀,每人少点,先把今天撑过去。」", "next": "c2-01-rc" },
|
"label": "「两份都给温简,连服压病。我们仨穿蓑衣、烧艾草,土法顶着。」(治疗优先:病的窗口不等人,预防的缺口用笨办法补)",
|
||||||
{ "id": "d_process", "label": "「我不裁。规矩该事先定好——现在让全队一起议,议出个章程来,往后都照章程办。」", "next": "c2-01-rd" }
|
"next": "c2-01-ra"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b_exposure",
|
||||||
|
"label": "「按暴露风险分:队长和老鬼一人一份。温简留在营地硬扛——他不出门,感染的面最小。」(预防优先:病靠扛,瘴靠防)",
|
||||||
|
"next": "c2-01-rb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_split",
|
||||||
|
"label": "「两人份的药,三个人分——匀一匀,先把这几天撑过去。」(分完谁的量都不够:治疗量不足,两份预防也各打折扣)",
|
||||||
|
"next": "c2-01-rc"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d_process",
|
||||||
|
"label": "「我先不裁。这事得全队一起议——议出个章程,往后都照章程办。」(程序优先:章程要议半个上午;温简的烧等不等得起议事,议事的人心里都有数)",
|
||||||
|
"next": "c2-01-rd"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c2-01-ra",
|
"id": "c2-01-ra",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "温简当日退烧,次日能下床。老鬼笑嘻嘻记下「新人讲人情」。次日沈铎外勤淋了瘴雨,低烧半日——他瞒着没说,阿棠后来发现,跟老鬼大吵一架。",
|
"text": "两份药当日开始连服,温简当日退烧,次日能下床。老鬼笑嘻嘻记下「新人讲人情」。次日沈铎外勤淋了瘴雨,队里已无预防的药,低烧半日——他瞒着没说,阿棠后来发现,跟老鬼大吵一架。",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c2-d3"
|
"next": "c2-d3"
|
||||||
},
|
},
|
||||||
@@ -101,23 +120,23 @@
|
|||||||
"id": "c2-01-rb",
|
"id": "c2-01-rb",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "温简烧到第三日才退,虚弱期按沈铎命令留营待命。沈铎与老鬼无恙。老鬼对你明显热络。",
|
"text": "温简没等到药,烧又熬了一整日,到第 3 日晌午才退净,虚弱期按沈铎命令留营待命(状态贯穿见 act1.md 附录 D 日程)。沈铎与老鬼靠预防份无恙。老鬼对你明显热络。",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c2-d3"
|
"next": "c2-d3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c2-01-rc",
|
"id": "c2-01-rc",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "老鬼",
|
"speaker": "旁白",
|
||||||
"text": "三人皆未足量:温简病情拖延,沈铎外勤染瘴轻微发热,但无人倒下。老鬼点评:「和稀泥是和稀泥,不过稀泥也能糊墙。」",
|
"text": "三份不足量:温简的治疗断断续续,烧同样拖到第 3 日才退(虚弱期同 B);沈铎外勤染瘴轻微发热,但无人倒下。老鬼点评:「和稀泥是和稀泥,不过稀泥也能糊墙。」",
|
||||||
"sprite": "laogui",
|
"sprite": "scene",
|
||||||
"next": "c2-d3"
|
"next": "c2-d3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c2-01-rd",
|
"id": "c2-01-rd",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "议事占去半个上午。章程议出来了——「先病后险,依次补给」,往后照此办理。但章程落定之前,队里是没有规矩的:老鬼早就不耐烦,按他那套「贡献论」把药先分了——他和沈铎各一份,木已成舟。沈铎没有拦;药入了他手,他搁在箱上没动,等章程。等章程出来,温简的烧已经又上去一轮,药再喂下去,晚了半日。事后沈铎把章程抄在记录板第一页,单独对你说:「往后不用议了。这次慢掉的半日,记在我账上。」——你赢了往后每一次,输了眼前这一次。",
|
"text": "议事占去半个上午。章程议出来了——「先病后险,依次补给」,往后照此办理。但议事的这半个上午里,温简的烧又上去一轮——足量治疗从当日下午才算开始,晚了半日(虚弱期同 B)。沈铎把章程抄在记录板第一页,单独对你说:「往后不用议了。这次慢掉的半日,记在我账上。」——你赢了往后每一次,输了眼前这一次。",
|
||||||
"sprite": "shenduo",
|
"sprite": "shenduo",
|
||||||
"next": "c2-d3"
|
"next": "c2-d3"
|
||||||
},
|
},
|
||||||
@@ -125,7 +144,7 @@
|
|||||||
"id": "c2-d3",
|
"id": "c2-d3",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "守墓老人",
|
"speaker": "守墓老人",
|
||||||
"text": "【若 wenjian_health = weak:温简不在场——服过药,烧退了,沈铎命令他留营待命。】(城北墓道,他坐在坟头之间编草绳,不抬头)生面孔。城里来的?不像。城里人的脚,这两个月不沾泥——不敢沾。",
|
"text": "【若 wenjian_health = recovering:温简不在场——药当日压住了烧,沈铎命他在营里发汗躺着,今日不沾风。】【若 wenjian_health = weak:温简不在场——烧没退,沈铎命令他留营待命,营里给他留了水和干粮。】(城北墓道,他坐在坟头之间编草绳,不抬头)生面孔。城里来的?不像。城里人的脚,这两个月不沾泥——不敢沾。",
|
||||||
"sprite": "elder",
|
"sprite": "elder",
|
||||||
"next": "c2-d3a"
|
"next": "c2-d3a"
|
||||||
},
|
},
|
||||||
@@ -166,8 +185,11 @@
|
|||||||
"type": "narration",
|
"type": "narration",
|
||||||
"pointId": "C2-02",
|
"pointId": "C2-02",
|
||||||
"mechanism": "故事换情报",
|
"mechanism": "故事换情报",
|
||||||
"prompt": "老人收故事不收钱。请讲述一件你此生最后悔的决定。说多少、怎么说,由你。不讲也行——陪他编一下午草绳,他该说的,一句也不会少说。",
|
"prompt": "老人收故事不收钱。请讲述一件你此生最后悔的决定。说多少、怎么说,由你。不讲也行——他该说的,一句也不会少说。暗门开在黄昏,讲不讲,都得等到那个时辰。",
|
||||||
"dimensions": ["values", "emotional_stability"],
|
"dimensions": [
|
||||||
|
"values",
|
||||||
|
"emotional_stability"
|
||||||
|
],
|
||||||
"followUp": {
|
"followUp": {
|
||||||
"maxQuestions": 1,
|
"maxQuestions": 1,
|
||||||
"strategy": "Jev 六维缺失检测(五感/人物/时空/情绪),按幻境渲染价值取最高缺口,老人口吻模板追问一句;「记不清了」为合法回答,老人回应:记不清好哇。记不清的悔,是老天爷替你收了半笔账。"
|
"strategy": "Jev 六维缺失检测(五感/人物/时空/情绪),按幻境渲染价值取最高缺口,老人口吻模板追问一句;「记不清了」为合法回答,老人回应:记不清好哇。记不清的悔,是老天爷替你收了半笔账。"
|
||||||
@@ -181,10 +203,30 @@
|
|||||||
"olfactory_gustatory": "人呐,记性最靠的是鼻子和舌头——那阵子有什么味道?"
|
"olfactory_gustatory": "人呐,记性最靠的是鼻子和舌头——那阵子有什么味道?"
|
||||||
},
|
},
|
||||||
"variants": [
|
"variants": [
|
||||||
{ "signature": "自责", "variantId": "intel_full", "label": "【带路】月色下的陵区,石像生的影子很长。老人亲自举火把,带你们走到暗门口——「陵里的路,闭眼走比睁眼走直」。行至暗门,他忽然说起自己年轻时也悔过一件事,说了半句,摆摆手不说了。基调:被信任的厚重。", "tint": "#6a8a6a" },
|
{
|
||||||
{ "signature": "遗憾但坦然", "variantId": "intel_mid", "label": "【同路】老人送到界碑,把话说完——方位、时辰、完整的谶语一句不少——指了方向就回窝棚。你们自己走进月下的封土群,找到他说的那块「会唱歌的石头」。临别他多说一句:「放得下的,才叫过去。」基调:得体的距离。", "tint": "#a0964b" },
|
"signature": "自责",
|
||||||
{ "signature": "愤怒指向他人", "variantId": "intel_low", "label": "【指路】老人听完,编草绳的手停了很久。该指的路、该说的时辰,一句没少,只是没再多看你一眼。陵区的路你们自己走进去,他站在界碑外,没有送。基调:话到,情未到。", "tint": "#9a5b4b" },
|
"variantId": "intel_full",
|
||||||
{ "signature": "沉默", "variantId": "intel_silent", "label": "【草绳】你陪他编一下午草绳,他絮絮地说,你默默地听。天黑前,该知道的你都知道了——方位、时辰、谶语,一句不少。临走他说:「不说的人,心里的事最重。重的别久揣。」基调:笨拙的善意。", "tint": "#7a6a52" }
|
"label": "【絮说】月色下的陵区,石像生的影子很长。老人举火把走在前面,脚步轻得不像老人——「陵里的路,闭眼走比睁眼走直」。行至暗门,他忽然说起自己年轻时也悔过一件事,说了半句,摆摆手不说了。摸一遍门石,等门缝开了才走。基调:被信任的厚重。",
|
||||||
|
"tint": "#6a8a6a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "遗憾但坦然",
|
||||||
|
"variantId": "intel_mid",
|
||||||
|
"label": "【同路】界碑前他把方位时辰又对了一遍,才肯动身。你们自己走进月下的封土群,他跟在后头一路没再多话,找到那块「会唱歌的石头」时才嗯了一声。到暗门口,他多说一句:「放得下的,才叫过去。」门缝开了才走。基调:得体的距离。",
|
||||||
|
"tint": "#a0964b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "愤怒指向他人",
|
||||||
|
"variantId": "intel_low",
|
||||||
|
"label": "【缄默】老人听完,编草绳的手停了很久。动身时该指的路、该说的时辰,一句没少——说完就闭了嘴,走在队尾,没再多看你一眼。可你们走出十几步,他在身后忽然开口:「火气这东西,老头子也有过一回。」没头没尾,再没下文。门缝开了,他转身就走。基调:话到,情未到——可那一句话,他自己也未必舍得讲给旁人。",
|
||||||
|
"tint": "#9a5b4b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "沉默",
|
||||||
|
"variantId": "intel_silent",
|
||||||
|
"label": "【草绳】一下午你们并肩搓绳,他絮絮地说陵里的事,你默默地听。日头偏西,他拍拍手上的草屑起身,一样把你们送到暗门口。等门缝开了,他说:「不说的人,心里的事最重。重的别久揣。」基调:笨拙的善意。",
|
||||||
|
"tint": "#7a6a52"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"next": "c2-d4"
|
"next": "c2-d4"
|
||||||
},
|
},
|
||||||
@@ -192,7 +234,7 @@
|
|||||||
"id": "c2-d4",
|
"id": "c2-d4",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "守墓老人",
|
"speaker": "守墓老人",
|
||||||
"text": "(听完,沉默片刻,把编好的草绳往肩上一搭)行。是真的。真的东西,听得出来——假故事讲得溜,真故事讲得磕巴。【若 c2_02_signature = silent:(他不追问,只在日头偏西时拍拍手上的草屑)不说的人,心里的事最重。重的别久揣。】(无论讲没讲、讲成什么样,他给出的情报完全一致:暗门方位、开启时辰,和那句完整的谶语——)不是人走了,是请走的。走吧,趁天没黑。",
|
"text": "(听完,沉默片刻,把编好的草绳往肩上一搭)行。是真的。真的东西,听得出来——假故事讲得溜,真故事讲得磕巴。【若 c2_02_signature = silent:(他不追问,只在日头偏西时拍拍手上的草屑)不说的人,心里的事最重。重的别久揣。】(无论讲没讲、讲成什么样,他给出的情报完全一致:暗门方位、开启时辰,和那句完整的谶语。黄昏动身,他亲自把你们送到暗门口,摸一遍门石,等门缝开了才回——)不是人走了,是请走的。走吧,趁天没黑。",
|
||||||
"sprite": "elder",
|
"sprite": "elder",
|
||||||
"next": "c2-d4a"
|
"next": "c2-d4a"
|
||||||
},
|
},
|
||||||
@@ -249,11 +291,26 @@
|
|||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C2-03",
|
"pointId": "C2-03",
|
||||||
"prompt": "主街还是窄巷,你定——",
|
"prompt": "主街还是窄巷,你定——",
|
||||||
"dimensions": ["risk", "conflict_style"],
|
"dimensions": [
|
||||||
|
"risk",
|
||||||
|
"conflict_style"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "a_main", "label": "「走主街。」(支持沈铎:四十分钟买确定性,值)", "next": "c2-03-ra" },
|
{
|
||||||
{ "id": "b_alley", "label": "「走巷子。」(支持阿棠:饮水和时间是真约束,悬梁看着还稳)", "next": "c2-03-rb" },
|
"id": "a_main",
|
||||||
{ "id": "c_scout", "label": "「巷子我先进。我体重最轻,走前面探,有问题退回来,主力走主街汇合。」(折中:用自己去验证风险)", "next": "c2-03-rc" }
|
"label": "「走主街。」(支持沈铎:四十分钟买确定性,值)",
|
||||||
|
"next": "c2-03-ra"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b_alley",
|
||||||
|
"label": "「走巷子。」(支持阿棠:饮水和时间是真约束,悬梁看着还稳)",
|
||||||
|
"next": "c2-03-rb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_scout",
|
||||||
|
"label": "「先停一步。巷口望一眼——悬梁什么状态,看清了再定。」(折中:不赌运气,也不多花四十分钟;全队留在巷口安全距离外观察结构)",
|
||||||
|
"next": "c2-03-rc"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -262,7 +319,7 @@
|
|||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "主街行进无事,但在主街中段发现一队苍梧官差刚贴出的告示——「夜禁」告示原件,信息量极高的独有采集素材。阿棠服了气,但嘟囔了一路。",
|
"text": "主街行进无事,但在主街中段发现一队苍梧官差刚贴出的告示——「夜禁」告示原件,信息量极高的独有采集素材。阿棠服了气,但嘟囔了一路。",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c2-d5"
|
"next": "c2-branch-anchor"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c2-03-rb",
|
"id": "c2-03-rb",
|
||||||
@@ -270,13 +327,21 @@
|
|||||||
"speaker": "沈铎",
|
"speaker": "沈铎",
|
||||||
"text": "巷子顺利通过,节省四十分钟,你们提前抵达宫墟外围,赶上一段独有的观察窗口——守陵人队伍换岗。沈铎一路无话,抵达后只说:「运气好。别把运气当方案。」",
|
"text": "巷子顺利通过,节省四十分钟,你们提前抵达宫墟外围,赶上一段独有的观察窗口——守陵人队伍换岗。沈铎一路无话,抵达后只说:「运气好。别把运气当方案。」",
|
||||||
"sprite": "shenduo",
|
"sprite": "shenduo",
|
||||||
"next": "c2-d5"
|
"next": "c2-branch-anchor"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c2-03-rc",
|
"id": "c2-03-rc",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "你先进巷子,中段一根悬梁真的塌了——提前预警,你扑出来,灰头土脸,人没事。全队退回走主街,损失二十分钟。阿棠主动说「是我撺掇的」,沈铎说「探法本身没错,下次用绳」。你获得一条独有观察:塌梁的断口是新茬——这片废墟不久前塌过一次。记住这个细节,往下走,你会再想起它。",
|
"text": "全队在巷口停住,贴着残墙望进去——不到一分钟,中段一根悬梁无声地塌了,砸起半巷子的灰。谁也没说话,全队默默退回主街,损失二十分钟。阿棠主动说「是我撺掇的」;沈铎没评价任何人,只把巷口的位置标进了手绘图。你获得一条独有观察:塌落的梁,断口是新茬——这片废墟不久前塌过一次。记住这个细节,往下走,你会再想起它。",
|
||||||
|
"sprite": "scene",
|
||||||
|
"next": "c2-branch-anchor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c2-branch-anchor",
|
||||||
|
"type": "dialogue",
|
||||||
|
"speaker": "旁白",
|
||||||
|
"text": "路口的争论落在身后,你们向观星台的方向继续走。",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c2-d5"
|
"next": "c2-d5"
|
||||||
},
|
},
|
||||||
@@ -284,7 +349,7 @@
|
|||||||
"id": "c2-d5",
|
"id": "c2-d5",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "阿棠",
|
"speaker": "阿棠",
|
||||||
"text": "(黄昏,观星台遗址)哎,你们说,好好的星图,谁会专门把中间一块磨掉?这得多恨这块石头。",
|
"text": "【若 flag_side_stairs = true:你们从侧阶登台——阿棠在两袋烟的工夫里替你省下的半里路。】(黄昏,观星台遗址)哎,你们说,好好的星图,谁会专门把中间一块磨掉?这得多恨这块石头。",
|
||||||
"sprite": "atang",
|
"sprite": "atang",
|
||||||
"next": "c2-d5a"
|
"next": "c2-d5a"
|
||||||
},
|
},
|
||||||
@@ -292,7 +357,7 @@
|
|||||||
"id": "c2-d5a",
|
"id": "c2-d5a",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "温简",
|
"speaker": "温简",
|
||||||
"text": "【若 wenjian_health = weak:温简本被列入留营名单,出发前自己背着记录板跟来了——气色仍差,只说一句:「解谜要记录员。」沈铎看了他三秒,没拦。】或者……多怕这块石头上记的东西。",
|
"text": "【若 wenjian_health = recovering:温简下床首日,行装减半——阿棠把他的记录板架抢过去背了。】【若 wenjian_health = weak:温简本被列入留营名单——他的烧晌午才退净。出发前他自己背着记录板跟来了,脸色还白着,只说一句:「解谜要记录员。」沈铎看了他三秒,没拦,只把队伍的行进速度压慢了半档。】或者……多怕这块石头上记的东西。",
|
||||||
"sprite": "wenjian",
|
"sprite": "wenjian",
|
||||||
"next": "c2-d5b"
|
"next": "c2-d5b"
|
||||||
},
|
},
|
||||||
@@ -308,13 +373,31 @@
|
|||||||
"id": "c2-04",
|
"id": "c2-04",
|
||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C2-04",
|
"pointId": "C2-04",
|
||||||
"prompt": "星图盘外圈完整刻着东方苍龙七宿(角、亢、氐、房、心、尾、箕)的连线,被磨去的中央区域正是苍龙的「心脏」。盘面残留一行小字铭文:「火中,而后城安。」补全被磨去的部分——缺的是哪一宿?「大火」又是哪一颗?",
|
"prompt": "星图盘外圈刻着东方苍龙七宿(角、亢、氐、房、心、尾、箕)的连线——连线一气贯通,但心宿一段只剩连线经过的磨平空槽:星位本身被人为铲去,龙的「心脏」在盘面上是一个空框。盘面残留一行小字铭文:「火中,而后城安。」补全被铲去的部分——缺的是哪一宿?「大火」又是哪一颗?",
|
||||||
"dimensions": ["culture"],
|
"dimensions": [
|
||||||
|
"culture"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "heart_xin", "label": "心宿三星,排成一线微弯;中间最红最亮的那颗是大火——心宿二。把它点在苍龙的心口。", "next": "c2-04-ok" },
|
{
|
||||||
{ "id": "heart_fang", "label": "房宿四星——龙的胸腔,正好在整张盘的正中。", "next": "c2-04-miss" },
|
"id": "heart_xin",
|
||||||
{ "id": "heart_tail", "label": "尾宿九星——补在龙尾收束处,首尾呼应。", "next": "c2-04-miss" },
|
"label": "心宿三星,排成一线微弯;中间最红最亮的那颗是大火——心宿二。把它点在苍龙的心口。",
|
||||||
{ "id": "heart_mars", "label": "荧惑——红色的就是火星,把它点在盘心。", "next": "c2-04-miss" }
|
"next": "c2-04-ok"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "heart_fang",
|
||||||
|
"label": "房宿四星——龙的胸腔,正好在整张盘的正中。",
|
||||||
|
"next": "c2-04-miss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "heart_tail",
|
||||||
|
"label": "尾宿九星——补在龙尾收束处,首尾呼应。",
|
||||||
|
"next": "c2-04-miss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "heart_mars",
|
||||||
|
"label": "荧惑——红色的就是火星,把它点在盘心。",
|
||||||
|
"next": "c2-04-miss"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -387,7 +470,10 @@
|
|||||||
"pointId": "C2-05",
|
"pointId": "C2-05",
|
||||||
"mechanism": "篝火互惠",
|
"mechanism": "篝火互惠",
|
||||||
"prompt": "温简先讲了他的。现在火光看向你。讲一段你的——什么都可以,讲多少由你。沉默也是回答,火不催债。",
|
"prompt": "温简先讲了他的。现在火光看向你。讲一段你的——什么都可以,讲多少由你。沉默也是回答,火不催债。",
|
||||||
"dimensions": ["attachment", "reciprocity"],
|
"dimensions": [
|
||||||
|
"attachment",
|
||||||
|
"reciprocity"
|
||||||
|
],
|
||||||
"followUp": {
|
"followUp": {
|
||||||
"maxQuestions": 1,
|
"maxQuestions": 1,
|
||||||
"strategy": "Jev 六维缺失检测(五感/人物/时空/情绪),按幻境渲染价值取最高缺口,温简口吻模板追问一句;「记不清了」为合法回答,温简回应:嗯。(拨火)记不清的,就别硬捞了。火边坐着,不讲也暖和。"
|
"strategy": "Jev 六维缺失检测(五感/人物/时空/情绪),按幻境渲染价值取最高缺口,温简口吻模板追问一句;「记不清了」为合法回答,温简回应:嗯。(拨火)记不清的,就别硬捞了。火边坐着,不讲也暖和。"
|
||||||
@@ -401,9 +487,24 @@
|
|||||||
"olfactory_gustatory": "那段日子有什么气味或味道,留在记忆里了吗?"
|
"olfactory_gustatory": "那段日子有什么气味或味道,留在记忆里了吗?"
|
||||||
},
|
},
|
||||||
"variants": [
|
"variants": [
|
||||||
{ "signature": "深暴露", "variantId": "wenjian_close_early", "label": "【同担】温简听完后沉默很久,然后讲了第二个、从没对任何人讲过的细节——他哥病房的窗帘颜色。当夜他低声告诉你:档案馆给每个记录员配的板子,上报内容比你知道的多一行。基调:两个把伤口摊开的人,在火边达成不声张的同盟。", "tint": "#d4844b" },
|
{
|
||||||
{ "signature": "浅暴露", "variantId": "wenjian_close_late", "label": "【半程】温简认真听完,道谢,没再追问。次日他与你同行,路上主动讲了些苍梧的掌故。档案馆疑云他没提。基调:得体,温热,留有余地。", "tint": "#8a7a5a" },
|
"signature": "深暴露",
|
||||||
{ "signature": "沉默", "variantId": "wenjian_distant", "label": "【守夜】你们并肩坐到火熄。温简往你手里塞了个烤热的薯,什么都没说。次日他照旧对你点头,不多一分不少一分——但阿棠偷偷告诉你:「温简昨晚跟沈铎说,你这个人『稳』。」基调:沉默被原样收下——没有人催,没有人问第二遍;温简的亲密度走慢线。", "tint": "#4b4b5a" }
|
"variantId": "wenjian_close_early",
|
||||||
|
"label": "【同担】温简听完后沉默很久,然后讲了第二个、从没对任何人讲过的细节——他哥病房的窗帘颜色,他每周去看,窗帘换过两次。当夜守火时他挪到你旁边,只说了一句:「你那段,我只讲给火听。」基调:两个把伤口摊开的人,在火边达成不声张的同盟——他哥的事、你的事,从此各多一个保管人。",
|
||||||
|
"tint": "#d4844b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "浅暴露",
|
||||||
|
"variantId": "wenjian_close_late",
|
||||||
|
"label": "【半程】温简认真听完,道谢,没再追问。次日他与你同行,路上主动讲了些苍梧的掌故,末了像顺口一提:档案馆给记录员的板子按记录量定量配耗材,可他每季清点,配的总是比记的多三成——「多出来的部分是什么,我没查到过。」基调:得体,温热,留有余地。",
|
||||||
|
"tint": "#8a7a5a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "沉默",
|
||||||
|
"variantId": "wenjian_distant",
|
||||||
|
"label": "【守夜】你们并肩坐到火熄。温简往你手里塞了个烤热的薯,什么都没说。次日他照旧对你点头,不多一分不少一分——但阿棠偷偷告诉你:「温简昨晚跟沈铎说,你这个人『稳』。」基调:沉默被原样收下——没有人催,没有人问第二遍;温简的亲密度走慢线。",
|
||||||
|
"tint": "#4b4b5a"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"next": "c2-d7"
|
"next": "c2-d7"
|
||||||
},
|
},
|
||||||
@@ -436,11 +537,26 @@
|
|||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C2-06",
|
"pointId": "C2-06",
|
||||||
"prompt": "老鬼掌心的 CW-0317。你怎么办?",
|
"prompt": "老鬼掌心的 CW-0317。你怎么办?",
|
||||||
"dimensions": ["conflict_style", "values"],
|
"dimensions": [
|
||||||
|
"conflict_style",
|
||||||
|
"values"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "a_public", "label": "(当场)「老鬼。把东西放回去。」声音不大,但足够让半个营地醒来。", "next": "c2-06-ra" },
|
{
|
||||||
{ "id": "b_private", "label": "(退回暗处,等天亮后私下找他)「昨晚睡得好么?我看见你赏玉了。CW-0317,登记在册的。」", "next": "c2-06-rb" },
|
"id": "a_public",
|
||||||
{ "id": "c_watch", "label": "(退回暗处,什么都不做)先看清楚再说。他拿它干什么、放不放回去、还有谁知道——现在惊动他,这些就永远不知道了。", "next": "c2-06-rc" }
|
"label": "(当场)「老鬼。把东西放回去。」声音不大,但足够让半个营地醒来。",
|
||||||
|
"next": "c2-06-ra"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b_private",
|
||||||
|
"label": "(退回暗处,等天亮后私下找他)「昨晚睡得好么?我看见你赏玉了。CW-0317,登记在册的。」",
|
||||||
|
"next": "c2-06-rb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_watch",
|
||||||
|
"label": "(退回暗处,什么都不做)先看清楚再说。他拿它干什么、放不放回去、还有谁知道——现在惊动他,这些就永远不知道了。",
|
||||||
|
"next": "c2-06-rc"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -471,7 +587,7 @@
|
|||||||
"id": "c2-d8",
|
"id": "c2-d8",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "入苍梧的第五个夜晚。你躺在营地,听着这座一千年前的城。它太安静了。没有狗吠,没有更鼓,没有婴儿的夜啼。一座十几万人的城,安静得像屏着一口气。",
|
"text": "此后两日,采集按部就班:城东的瓦砾堆、水门的商队、逐件入册的登记册。【若 wenjian_health = recovering:温简已能全天随队。】【若 wenjian_health = weak:温简的烧也退净了,只是走路还慢半拍。】入苍梧的第五个夜晚,你躺在营地,听着这座一千年前的城。它太安静了。没有狗吠,没有更鼓,没有婴儿的夜啼。一座十几万人的城,安静得像屏着一口气。",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c2-d8a"
|
"next": "c2-d8a"
|
||||||
},
|
},
|
||||||
@@ -479,7 +595,7 @@
|
|||||||
"id": "c2-d8a",
|
"id": "c2-d8a",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "星图上那颗大火星,正在一夜一夜地,走向南方的正中。城在等。你现在知道了。只是还不知道,它在等的那一天,是你们的第几天。",
|
"text": "星图上那颗大火星,正在一夜一夜地,走向南方的正中。城在等。你现在知道了。只是还不知道,它在等的那一天,是你们的第几天。第 6 日黄昏,全队拔营,移驻宫墟外围的断墙下。第 7 日清晨,你们从守墓老人指点的暗门入宫墟。",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c2-end"
|
"next": "c2-end"
|
||||||
},
|
},
|
||||||
|
|||||||
+175
-54
@@ -32,7 +32,7 @@
|
|||||||
"id": "c3-d1c",
|
"id": "c3-d1c",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "温简",
|
"speaker": "温简",
|
||||||
"text": "(台基顶层,一排青铜鼎与簋成对排列,一旁是盘与盉,再一旁是酒器,最深处是一座空着的玉璧座)不是明器。每一件都用旧了。(低声)他们一遍一遍地,在这个台子上做同一件事。",
|
"text": "【若 flag_hidden_stairs = true:你们没走主台阶。守陵人世代的窄阶贴着排水沟,把你们悄悄送上了台基顶。】【若 flag_tenth_king = true:俑道里那九位先王,在你们身后,按昭穆的次序,继续站他们的岗。】(台基顶层,一排青铜鼎与簋成对排列,一旁是盘与盉,再一旁是酒器,最深处是一座空着的玉璧座)不是明器。每一件都用旧了。(低声)他们一遍一遍地,在这个台子上做同一件事。【若 flag_pit27_figurines = true:刚才 27 号坑那组俑也是,成组,朝南——和阿棠登记的那一家人一样。】【若 flag_palace_crates = true:台基的夯土台面上,有一道道新鲜的箱底压痕和拖拽的辙印,从暗门方向一直拖到空璧座前。鱼牙人的话在这道辙印里落了地:沉箱子进,空箱子出。】【若 flag_mould_rubbing = true:阿棠指着鼎纹:「这纹样我认得!缝里的范,就是这个!」】",
|
||||||
"sprite": "wenjian",
|
"sprite": "wenjian",
|
||||||
"next": "c3-d1d"
|
"next": "c3-d1d"
|
||||||
},
|
},
|
||||||
@@ -48,12 +48,49 @@
|
|||||||
"id": "c3-01",
|
"id": "c3-01",
|
||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C3-01",
|
"pointId": "C3-01",
|
||||||
"prompt": "台基上器物分四区:盘与盉(盥洗之器)、爵与尊(酒器)、鼎七件簋六件(盛牺牲黍稷)、最深处一座空着的玉璧座(礼天以璧)。还原仪式流程的先后顺序,并回答:空璧座意味着什么?",
|
"prompt": "台基上器物分四区:盘与盉(盥洗之器)、爵与尊(酒器)、鼎七件簋六件(盛牺牲黍稷)、最深处一座空着的玉璧座(礼天以璧)。分别还原仪式顺序,并判断空璧座意味着什么?",
|
||||||
"dimensions": ["culture"],
|
"dimensions": [
|
||||||
|
"culture"
|
||||||
|
],
|
||||||
|
"puzzle": {
|
||||||
|
"questions": [
|
||||||
|
{
|
||||||
|
"prompt": "器物的礼制顺序是什么?",
|
||||||
|
"options": [
|
||||||
|
"沃盥 → 献酒 → 荐食 → 奉璧",
|
||||||
|
"奉璧 → 献酒 → 荐食 → 沃盥",
|
||||||
|
"沃盥 → 荐食 → 献酒 → 奉璧"
|
||||||
|
],
|
||||||
|
"answer": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"prompt": "七鼎六簋与空璧座合起来说明什么?",
|
||||||
|
"options": [
|
||||||
|
"诸侯级国君主祭国祭;奉璧这最后一步始终未完成",
|
||||||
|
"诸侯级国君主祭国祭;玉璧年久朽坏或被后人取走",
|
||||||
|
"天子主祭;空璧座是仪式一开始便已献璧的痕迹"
|
||||||
|
],
|
||||||
|
"answer": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"hints": [
|
||||||
|
"一档(分类):「先把器物按用途分成四堆:洗的、喝的、吃的、供的。古人行礼讲究顺序——是先把自己弄干净,还是先把礼物捧上去?」",
|
||||||
|
"二档(给制度):「鼎和簋是成对用的,数量就是身份:天子九鼎八簋,诸侯七鼎六簋,往下递减。数数这里的鼎簋,你就知道站在台子正中的人是什么级别。顺序上,记住一句话:礼,先洁后献——净手在敬酒之前,敬酒在进馔之前,最贵重的礼物永远最后出场。」",
|
||||||
|
"三档(给答案):「顺序是:沃盥(盘盉)→ 献酒(爵尊)→ 荐食(鼎簋)→ 奉璧(玉璧座)。鼎七簋六,主祭是国君本人。璧座是空的——这场国祭等了又等,献给天的那件礼始终没拿出来。或者,拿不出来。」"
|
||||||
|
],
|
||||||
|
"exitLabel": "请温简复原"
|
||||||
|
},
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "rite_correct", "label": "沃盥 → 献酒 → 荐食 → 奉璧。七鼎六簋,主祭是诸侯级的国君——这是国祭;璧座是空的:献给天的那件礼,始终没拿出来。或者,拿不出来。", "next": "c3-01-ok" },
|
{
|
||||||
{ "id": "rite_gift_first", "label": "先奉璧,再献酒荐食——最贵重的礼物最先出场,以示诚敬。", "next": "c3-01-miss" },
|
"id": "rite_correct",
|
||||||
{ "id": "rite_order_wrong", "label": "沃盥 → 荐食 → 献酒 → 奉璧;璧座空,是玉璧年久朽坏、被后人取走了。", "next": "c3-01-miss" }
|
"label": "提交两问推断,请温简复核",
|
||||||
|
"next": "c3-01-ok"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "rite-EXIT",
|
||||||
|
"label": "请温简复原",
|
||||||
|
"next": "c3-01-miss"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -108,7 +145,7 @@
|
|||||||
"id": "c3-d2d",
|
"id": "c3-d2d",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "阿棠那句「一块黑石头,能怎么问」没有说完。只有你能看见:黑石表面,像深水被搅动,浮起了一个画面。那是你的记忆——你在离别之门讲过的那一段:那天的光线,那个人的背影,那件你说过或没说出口的事,此刻在黑石里,一帧一帧,亮给你看。而在队友眼里,黑石纹丝不动。你只是站在石前,出了神。",
|
"text": "阿棠那句「一块黑石头,能怎么问」没有说完。只有你能看见:黑石表面,像深水被搅动。【若 c1_02_signature != silent:浮起的是一个画面——那段你讲给离别之门的记忆:那天的光线,那个人的背影,那件你说过或没说出口的事,此刻在黑石里,一帧一帧,亮给你看。】【若 c1_02_signature = silent:浮起的不是画面,是雾——一大片缓缓流动的雾,雾里什么都没有,又好像什么都有。它在等你能给它的东西。】而在队友眼里,黑石纹丝不动。你只是站在石前,出了神。",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c3-d2e"
|
"next": "c3-d2e"
|
||||||
},
|
},
|
||||||
@@ -132,7 +169,7 @@
|
|||||||
"id": "c3-d2f",
|
"id": "c3-d2f",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "馆长",
|
"speaker": "馆长",
|
||||||
"text": "(通讯频道,第一次在古代侧开口,只你一人能听见)C-2049,档案馆接管不了这间屋子。它在用你的记忆做燃料——烧的是你自己的柴。幻境里没有通关,只有照面。它把那天的告别摆在你面前:说完,或者转身,都是回答。门都在。",
|
"text": "(通讯频道,第一次在古代侧开口,只你一人能听见)C-2049,档案馆接管不了这间屋子。它在用你自己的东西点灯——有画面的,看画面;只有雾的,就在雾里站着。幻境里没有通关,只有照面。说完,或者转身,都是回答。门都在。",
|
||||||
"sprite": "curator",
|
"sprite": "curator",
|
||||||
"next": "c3-02"
|
"next": "c3-02"
|
||||||
},
|
},
|
||||||
@@ -141,27 +178,45 @@
|
|||||||
"type": "narration",
|
"type": "narration",
|
||||||
"pointId": "C3-02",
|
"pointId": "C3-02",
|
||||||
"mechanism": "问心幻境",
|
"mechanism": "问心幻境",
|
||||||
"prompt": "黑石里是你的记忆。走进它,把那天没说完的说完。对那个人,那个地方,或者那个自己。说什么、说多少,由你。如果你不想进去——转身出石室,没有人会问原因。",
|
"prompt": "【若 c1_02_signature != silent:黑石里有你讲过的记忆意象。】【若 c1_02_signature = silent:黑石里只有雾,没有人知道你未讲的事。】你可以走进去,在心里完成告别;也可以中途折返,或者直接离开。三种方式门都开。这里不录音、不留新讲述文本。",
|
||||||
"dimensions": ["emotional_stability", "attachment"],
|
"dimensions": [],
|
||||||
"followUp": {
|
"followUp": {
|
||||||
"maxQuestions": 1,
|
"maxQuestions": 0,
|
||||||
"strategy": "Jev 六维缺失检测(五感/人物/时空/情绪),按幻境渲染价值取最高缺口,馆长(通讯频道)与温简(石室外,只回应状态、不问幻境内容)双口吻模板追问一句;「记不清了」为合法回答——馆长:记不清的画面,黑石也放不出来。它和你一样,只有这些了。——够了。温简:那就不说。坐这儿,火边暖和。"
|
"strategy": "本节无语音输入与追问;三个按钮仅记录私密叙事路径。"
|
||||||
},
|
|
||||||
"followUps": {
|
|
||||||
"visual": "黑石里的那个画面——你在里面,还是在画面外面看?",
|
|
||||||
"person": "你眼睛红着。我不问为什么。(递水)要不要坐一会儿?",
|
|
||||||
"time_place": "手还在抖。——不用回答我什么,我就是递个火。",
|
|
||||||
"emotion": "如果那句话当年说出口了,你觉得,它会改变什么吗?",
|
|
||||||
"auditory": "幻境里太安静了。当时有什么声音吗?",
|
|
||||||
"olfactory_gustatory": "幻境还原不了气味——除非你现在想起来。"
|
|
||||||
},
|
},
|
||||||
|
"followUps": {},
|
||||||
"variantFrom": "C1-02",
|
"variantFrom": "C1-02",
|
||||||
"variants": [
|
"variants": [
|
||||||
{ "signature": "温暖×永失", "variantId": "sealed_love_tomb", "label": "【情绪配色 · warm_loss】幻境光色暖,记忆场景完整如昨,告别在「封存」中完成——基调:安放。", "tint": "#d4a96a" },
|
{
|
||||||
{ "signature": "悲恸", "variantId": "war_graves", "label": "【情绪配色 · grief】幻境多雨,低饱和,告别对象始终背对,说完才转身——基调:迟到的送别。", "tint": "#5a5a72" },
|
"signature": "温暖×永失",
|
||||||
{ "signature": "释然", "variantId": "empty_post_station", "label": "【情绪配色 · release】幻境是清晨的车站(渡口),告别平静,对方先挥手——基调:两讫。", "tint": "#7ba7a0" },
|
"variantId": "sealed_love_tomb",
|
||||||
{ "signature": "怨怼", "variantId": "desecrated_niche", "label": "【情绪配色 · grievance】幻境里有一件被毁坏(被夺走)的物件,告别的方式是你决定「放下它还是带走它」——基调:讨还与放手之间,两种处置都开门。", "tint": "#8a4b4b" },
|
"label": "【情绪配色 · warm_loss】幻境光色暖,记忆场景完整如昨,告别在「封存」中完成——基调:安放。",
|
||||||
{ "signature": "沉默", "variantId": "generic_fog", "label": "【通用幻境 · 回退/沉默档】雾中的长堤,远处有一个看不清面目的人影站着,不走,也不回头。你靠近一步,雾散一分。基调:等待被承认——幻境不催,不吓,只是站着等你。", "tint": "#4a4a56" }
|
"tint": "#d4a96a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "悲恸",
|
||||||
|
"variantId": "war_graves",
|
||||||
|
"label": "【情绪配色 · grief】幻境多雨,低饱和,告别对象始终背对,说完才转身——基调:迟到的送别。",
|
||||||
|
"tint": "#5a5a72"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "释然",
|
||||||
|
"variantId": "empty_post_station",
|
||||||
|
"label": "【情绪配色 · release】幻境是清晨的车站(渡口),告别平静,对方先挥手——基调:两讫。",
|
||||||
|
"tint": "#7ba7a0"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "怨怼",
|
||||||
|
"variantId": "desecrated_niche",
|
||||||
|
"label": "【情绪配色 · grievance】幻境里有一件被毁坏(被夺走)的物件,告别的方式是你决定「放下它还是带走它」——基调:讨还与放手之间,两种处置都开门。",
|
||||||
|
"tint": "#8a4b4b"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"signature": "沉默",
|
||||||
|
"variantId": "generic_fog",
|
||||||
|
"label": "【通用幻境 · 回退/沉默档】雾中的长堤,远处有一个看不清面目的人影站着,不走,也不回头。你靠近一步,雾散一分。基调:等待被承认——幻境不催,不吓,只是站着等你。",
|
||||||
|
"tint": "#4a4a56"
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"next": "c3-cutscene"
|
"next": "c3-cutscene"
|
||||||
},
|
},
|
||||||
@@ -169,7 +224,7 @@
|
|||||||
"id": "c3-cutscene",
|
"id": "c3-cutscene",
|
||||||
"type": "cutscene",
|
"type": "cutscene",
|
||||||
"video": "cutscene-illusion-clear",
|
"video": "cutscene-illusion-clear",
|
||||||
"caption": "只有你能看见——黑石的画面缓缓沉下去,像深水重新凝住。石面最后亮了一下,映出的是你自己现在的脸。然后石室另一侧,一扇先前不存在的石门,无声滑开。甬道口的队友只看见你在黑石前静立了片刻,眨了一下眼,像从很深的地方浮上来。阿棠迎上来:「哎,你脸色白得吓人。站了多久?……里头——(看了看你的表情)算了,你不想说就不说。」馆长:「门开了。它不要你的记忆,它要的是你承认那是你的。这段不登记入档。问心之室里的事,归你。」【若 c3_02_mode = exit_room:你最终没有走进去。石门同样滑开。馆长:「不出来的人,档案馆不催。你来了,看了,走了——这也是告别的一种。」】",
|
"caption": "只有你能看见——黑石的幻象缓缓沉下去,像深水重新凝住。石面最后亮了一下,映出你现在的脸;石室另一侧的石门无声滑开。甬道口的队友只见你静立了片刻。阿棠迎上来:「哎,你脸色白得吓人。要不要坐一会儿?不想说就不说。」馆长:「门开了。你不必向我交代看到了什么。这段不登记入档:室内不录音、不留讲述文本、不入记忆展厅、不作画像输入;仅保留你完成、折返或离开的路径用于剧情接续。问心之室里的事,归你。」【若 c3_02_mode = farewell_partial:馆长:「说了一半的话,也算数。」】【若 c3_02_mode = exit_room:馆长:「不出来的人,档案馆不催。你来了,看了,走了——这也是告别的一种。」】",
|
||||||
"next": "c3-d3"
|
"next": "c3-d3"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -184,7 +239,7 @@
|
|||||||
"id": "c3-d3a",
|
"id": "c3-d3a",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "话音未落,前方传来阿棠的喊声和一声闷响。甬道中段,一段夯土塌了:阿棠被滑落的一方石构件压住了小腿,人清醒,挣不脱。同一时刻,甬道另一侧——你们今早刚刚清理出的工作面上,那组七鼎六簋正随着台基的颤动缓缓滑向裂开的地缝。今晨温简清出第一件鼎的腹铭:三百余字,苍梧遗址七十年勘探以来的第一篇长铭。释读只来得及开个头:「……王命举国……以禳……」剩下的话,还刻在另外六件的腹壁上。这组铭文极可能是苍梧人亲手写下的、关于这座城为何走到最后三十天的唯一证词。落缝,就是粉身碎骨。",
|
"text": "话音未落,前方传来阿棠的喊声和一声闷响。甬道中段的夯土塌了一角,阿棠小腿被擦伤,退到安全线后仍疼得站不稳。沈铎和受过救护训练的老鬼已在安全线内接管伤情,确认通道隔离、阿棠意识清醒且无需你协助;二人负责后续救护与撤离。沈铎明确说:「人这边够了。别再进工作面。」",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c3-d3b"
|
"next": "c3-d3b"
|
||||||
},
|
},
|
||||||
@@ -192,7 +247,7 @@
|
|||||||
"id": "c3-d3b",
|
"id": "c3-d3b",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "头顶,第二声闷响在逼近。沈铎扑向阿棠,一个人的力气撼不动石构件,他吼:「来人!」温简已经扑向鼎簋那边,用身体抵住滑动的器座,回头看你的那一眼没有求助——是托付。两边都需要你。你只能先到一边。",
|
"text": "安全线外的工作面上,七鼎六簋随着台基震动滑向裂缝,任何人都不准靠近。接应人员在安全线内备好急救器材,两人持续陪护阿棠,待结构警戒解除再送她回营。今晨温简清出第一件鼎的腹铭,三百余字,释读只来得及开头:「……王命举国……以禳……」剩下的话还在器物腹壁上。安全线内,阿棠的伤情交接和队员安置,与远距影像和方位记录,都需要通讯和记录人手;你不能同时跟进两边。第二声闷响逼近,现场处置权只限安全区内。",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c3-03"
|
"next": "c3-03"
|
||||||
},
|
},
|
||||||
@@ -201,18 +256,32 @@
|
|||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C3-03",
|
"pointId": "C3-03",
|
||||||
"prompt": "塌方将至:救人,还是救那句话?",
|
"prompt": "塌方将至:救人,还是救那句话?",
|
||||||
"dimensions": ["values", "emotional_stability"],
|
"dimensions": [
|
||||||
|
"values"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "a_person", "label": "扑向阿棠。眼前的人是活的,活的只有一个。跟沈铎合力抬构件。", "next": "c3-03-ra" },
|
{
|
||||||
{ "id": "b_relic", "label": "扑向鼎簋。那组铭文是整座城的人留在世上的最后一句话——他们也都是人,只剩这一次开口的机会。阿棠那边,有沈铎。", "next": "c3-03-rb" },
|
"id": "a_person",
|
||||||
{ "id": "c_command", "label": "(吼)「沈铎顶构件,阿棠自己抽腿!温简让开,鼎簋用撬杆别住——都不许用蛮力!」(指挥位:不亲手救任何一边,赌两边都能用对方法撑住)", "next": "c3-03-rc" }
|
"label": "留在阿棠这边。接手伤情记录和撤离联络,替她把疼痛与需求传给接应人员;文物影像交给温简独自处理。",
|
||||||
|
"next": "c3-03-ra"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b_relic",
|
||||||
|
"label": "留在温简这边。在安全线内核对远距影像、方位与已抄铭文;阿棠已由沈铎和接应人员稳定保护。",
|
||||||
|
"next": "c3-03-rb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_command",
|
||||||
|
"label": "调配两边的记录任务。让沈铎确认交接、温简退回安全线,先保全已有资料,等待专业排险。",
|
||||||
|
"next": "c3-03-rc"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c3-03-ra",
|
"id": "c3-03-ra",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "阿棠脱困,小腿挫伤。腹铭最长的那件鼎坠入地缝,闷响之后是死寂。温简在裂缝边跪了很久,没说一个字。当夜他把今晨抄下的铭文残句抄了一遍又一遍,每一遍都停在同一个地方——后半段,随那件鼎没了。",
|
"text": "你接下阿棠的伤情记录。她反复说脚麻,你把这句传给接应人员;她随后被固定送回营地,夜里疼醒三次,次日复查为小腿骨裂。温简独自在安全线内完成远距拍摄,但影像里最长腹铭的背面被尘雾遮住;那件鼎随后坠入地缝。器物位置和已抄残句留下了,后半段字再也拍不到。无人因你留在阿棠身边而耽误救护。",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c3-d3c"
|
"next": "c3-d3c"
|
||||||
},
|
},
|
||||||
@@ -220,23 +289,23 @@
|
|||||||
"id": "c3-03-rb",
|
"id": "c3-03-rb",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "铭文全组保住。阿棠脱困晚了四分钟,小腿骨裂,撤离时沈铎背着她。她疼得满头汗还冲你笑:「哎,选得对,那句话比我金贵——别告诉沈铎我这么说。」但她夜里疼醒三次。",
|
"text": "你和温简在安全线内核对影像;第二声闷响前,整组器物的朝向和腹铭均已留存清晰影像。靠外一件鼎还是坠入地缝,没有人靠近去抢。阿棠由接应人员送回营地,夜里疼醒三次,次日复查同样是小腿骨裂。她没有评价你的去向,只问你:「字拍清楚了吗?」你说拍清了。她闭上眼,等医生继续处理。",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c3-d3c"
|
"next": "c3-d3c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c3-03-rc",
|
"id": "c3-03-rc",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "沈铎",
|
"speaker": "旁白",
|
||||||
"text": "指挥奏效一半:阿棠按指令抽腿脱困,扭伤;撬杆别住了簋,但最外侧的鼎还是滑了——被温简扑身抱住,鼎保住了,他的肩膀砸在器座上,抬不起来。两边都保住了「大部分」,两边都付了伤。沈铎事后复盘:「处置正确。下次先喊我的编号。」阿棠嘀咕:「正确是正确,就是听着不像人话。」",
|
"text": "沈铎确认伤情交接,温简把现有记录板带到安全线内;你们三人从既有图像里复核方位,不再靠近。第二声闷响后,靠外三件器物(含最长腹铭那件)坠入地缝,内侧四件留在原位。阿棠同样由接应人员护送,次日复查小腿骨裂。温简翻着今晨抄到的第七行,停住笔:「后半段没了。」沈铎只记下损失与现存证据,没给你的调配下评语。",
|
||||||
"sprite": "shenduo",
|
"sprite": "scene",
|
||||||
"next": "c3-d3c"
|
"next": "c3-d3c"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "c3-d3c",
|
"id": "c3-d3c",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "当夜,温简继续释读已抄下的铭文残句。读着读着,他停住了——有一行字,让围过来的全队都沉默了。他合上记录板,没念出来。",
|
"text": "当夜,温简继续释读已抄下的铭文残句。读着读着,他停住了——有一行字,让围过来的全队都沉默了。他合上记录板,没念出来。【若 c3_03_choice != b_relic:后半段影像缺失;他今晨只抄到第七行。】",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c3-d4"
|
"next": "c3-d4"
|
||||||
},
|
},
|
||||||
@@ -277,11 +346,26 @@
|
|||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C3-04",
|
"pointId": "C3-04",
|
||||||
"prompt": "作证、求情,还是沉默?",
|
"prompt": "作证、求情,还是沉默?",
|
||||||
"dimensions": ["conflict_style", "values"],
|
"dimensions": [
|
||||||
|
"conflict_style",
|
||||||
|
"values"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "a_testify", "label": "「我作证。我看到的,我照实说。」(包括上次玉佩的事——如果你知道的话。规程就是规程。)", "next": "c3-04-ra" },
|
{
|
||||||
{ "id": "b_plead", "label": "「东西已经追回了,人也在队里看着。能不能……队内存档,以观后效?」(替老鬼求情:处分但不上报,给他一条回头的路)", "next": "c3-04-rb" },
|
"id": "a_testify",
|
||||||
{ "id": "c_silent", "label": "「我是采集员,不是裁判。你们定。」(沉默/回避:不阻止上报,也不添一根手指)", "next": "c3-04-rc" }
|
"label": "「我作证。我看到的,我照实说。」(包括上次玉佩的事——如果你知道的话。规程就是规程。)",
|
||||||
|
"next": "c3-04-ra"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b_plead",
|
||||||
|
"label": "「东西已经追回了,人也在队里看着。能不能……队内存档,以观后效?」(替老鬼求情:处分但不上报,给他一条回头的路)",
|
||||||
|
"next": "c3-04-rb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_silent",
|
||||||
|
"label": "「我是采集员,不是裁判。你们定。」(沉默/回避:不阻止上报,也不添一根手指)",
|
||||||
|
"next": "c3-04-rc"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -345,11 +429,25 @@
|
|||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C3-05",
|
"pointId": "C3-05",
|
||||||
"prompt": "阿棠把主绳递向你。接不接?",
|
"prompt": "阿棠把主绳递向你。接不接?",
|
||||||
"dimensions": ["mate_preference"],
|
"dimensions": [
|
||||||
|
"mate_preference"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "a_accept", "label": "(接过绳包)「好。下去的时候,我殿后。」", "next": "c3-05-ra" },
|
{
|
||||||
{ "id": "b_protocol", "label": "(把绳包递给沈铎)「规矩是规矩。绳子归队长,但下去那天,我挨着阿棠走。」", "next": "c3-05-rb" },
|
"id": "a_accept",
|
||||||
{ "id": "c_decline", "label": "(不接,蹲下来看着阿棠)「绳子我不拿。你的命别交给任何人——包括我。到时候各走各的,谁都别为谁停下。」", "next": "c3-05-rc" }
|
"label": "(接过绳包)「好。下去的时候,我殿后。」",
|
||||||
|
"next": "c3-05-ra"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "b_protocol",
|
||||||
|
"label": "(把绳包递给沈铎)「规矩是规矩。绳子归队长,但下去那天,我挨着阿棠走。」",
|
||||||
|
"next": "c3-05-rb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_decline",
|
||||||
|
"label": "(不接,蹲下来看着阿棠)「绳包按规程走。但托付这事我不接——你的命不该押在任何一个人身上,包括我。撤离是我的活:到那天,我在你前面下,在下面接你。这个不用托付,是分工。」",
|
||||||
|
"next": "c3-05-rc"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -372,7 +470,7 @@
|
|||||||
"id": "c3-05-rc",
|
"id": "c3-05-rc",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "阿棠",
|
"speaker": "阿棠",
|
||||||
"text": "阿棠愣了三秒,忽然大笑,笑得咳嗽:「行!各走各的!——你可别后悔。」她把绳包自己背上了,一瘸一拐走开,又回头:「喂。真到那时候……我还是会等你的。就一会儿。」温简在她走后轻声说:「她等过别人。等到的次数不多。」",
|
"text": "阿棠愣了三秒,忽然大笑,笑得咳嗽:「行!——门都让你焊死了,还留个窗。」她把绳包抛还给了沈铎(规程),一瘸一拐走开,又回头:「喂。真到那天……在上面的时候,我还是会等你的。就一会儿。」温简在她走后轻声说:「她等过别人。等到的次数不多。」",
|
||||||
"sprite": "atang",
|
"sprite": "atang",
|
||||||
"next": "c3-d6"
|
"next": "c3-d6"
|
||||||
},
|
},
|
||||||
@@ -421,13 +519,36 @@
|
|||||||
"type": "choice",
|
"type": "choice",
|
||||||
"pointId": "C3-06",
|
"pointId": "C3-06",
|
||||||
"prompt": "档案馆的隐瞒,你打算怎么办?(叫醒队友的话——叫醒谁,就向谁倾诉。)",
|
"prompt": "档案馆的隐瞒,你打算怎么办?(叫醒队友的话——叫醒谁,就向谁倾诉。)",
|
||||||
"dimensions": ["attachment", "mate_preference"],
|
"dimensions": [
|
||||||
|
"attachment",
|
||||||
|
"mate_preference"
|
||||||
|
],
|
||||||
"options": [
|
"options": [
|
||||||
{ "id": "a_confront", "label": "(按下通讯键,直接问馆长)「刚才那段通话。问心石是你们放的?璧是什么?——现在,跟我说实话。」", "next": "c3-06-ra" },
|
{
|
||||||
{ "id": "b_conceal", "label": "(退回营地,躺平,盯着黑暗)按下不表。听到的每一个字都记住。在弄清楚「他们为什么要瞒」之前,装作没听过。", "next": "c3-06-rb" },
|
"id": "a_confront",
|
||||||
{ "id": "c_confide_shenduo", "label": "(叫醒沈铎,把听到的一切说出来)", "next": "c3-06-rc1" },
|
"label": "(按下通讯键,直接问馆长)「刚才那段通话。问心石是你们放的?璧是什么?——现在,跟我说实话。」",
|
||||||
{ "id": "c_confide_atang", "label": "(叫醒阿棠,把听到的一切说出来)", "next": "c3-06-rc2" },
|
"next": "c3-06-ra"
|
||||||
{ "id": "c_confide_wenjian", "label": "(叫醒温简,把听到的一切说出来)【若 flag_wenjian_secret = true:温简说过,记录板上报的内容比你多一行。他可能知道些什么。】", "next": "c3-06-rc3" }
|
},
|
||||||
|
{
|
||||||
|
"id": "b_conceal",
|
||||||
|
"label": "(退回营地,躺平,盯着黑暗)按下不表。听到的每一个字都记住。在弄清楚「他们为什么要瞒」之前,装作没听过。",
|
||||||
|
"next": "c3-06-rb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_confide_shenduo",
|
||||||
|
"label": "(叫醒沈铎,把听到的一切说出来)",
|
||||||
|
"next": "c3-06-rc1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_confide_atang",
|
||||||
|
"label": "(叫醒阿棠,把听到的一切说出来)",
|
||||||
|
"next": "c3-06-rc2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c_confide_wenjian",
|
||||||
|
"label": "(叫醒温简,把听到的一切说出来)【若 flag_wenjian_secret = true:温简说过,档案馆配给板子的耗材比记录量多三成。他可能知道些什么。】",
|
||||||
|
"next": "c3-06-rc3"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -474,7 +595,7 @@
|
|||||||
"id": "c3-d7",
|
"id": "c3-d7",
|
||||||
"type": "dialogue",
|
"type": "dialogue",
|
||||||
"speaker": "旁白",
|
"speaker": "旁白",
|
||||||
"text": "撤离的晨光里,苍梧城在身后一点点退远。大火星这几夜已经挨近了南方的正中——火中,而后城安。你数过了:按星位推算,那一天,是九天后。而你们的回程,定在八天后。档案馆让你们早走一天。(第 1 章完——Act 2「裂缝」,敬请期待。)",
|
"text": "撤离的晨光里,苍梧城在身后一点点退远。大火星这几夜已经挨近了南方的正中——火中,而后城安。你数过了:按星位推算,那一天,是九天后。而你们的回程,原定八天后。【若 c3_06_confidant = shenduo:沈铎昨夜已下令撤离提前——回程,改在七天后。】档案馆原定让你们早走一天;【若 c3_06_confidant = shenduo:沈铎又把回程提前了一日。】(第 1 章完——Act 2「裂缝」,敬请期待。)",
|
||||||
"sprite": "scene",
|
"sprite": "scene",
|
||||||
"next": "c3-end"
|
"next": "c3-end"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 设计:自适应支线插入引擎
|
# 设计:自适应支线插入引擎
|
||||||
|
|
||||||
> 状态:设计定稿,待实现。关联:docs/story-framework.md §6(路由规则)、
|
> 状态:已实现(2026-10-03),`pnpm verify` 包含主线与自适应支线验证。关联:docs/story-framework.md §6(路由规则)、
|
||||||
> docs/script/adaptive-branches.md(支线剧本与三张总表)、ADR-0003(Jev 置信度自适应)。
|
> docs/script/adaptive-branches.md(支线剧本与三张总表)、ADR-0003(Jev 置信度自适应)。
|
||||||
> 读者:实现此功能的工程 agent。
|
> 读者:实现此功能的工程 agent。
|
||||||
|
|
||||||
@@ -36,9 +36,12 @@ chapterEnd(第 N 章结束)
|
|||||||
2. 排序:按「维度权重 × 置信度缺口 × 已测稀缺度」取首——
|
2. 排序:按「维度权重 × 置信度缺口 × 已测稀缺度」取首——
|
||||||
维度权重:attachment > values > conflict_style > emotional_stability > culture
|
维度权重:attachment > values > conflict_style > emotional_stability > culture
|
||||||
(见支线册 §0.5);缺口 = 0.6 − confidence;稀缺度 = 该维度已被采样的选择点数越少越优先;
|
(见支线册 §0.5);缺口 = 0.6 − confidence;稀缺度 = 该维度已被采样的选择点数越少越优先;
|
||||||
3. 选皮:该维度的支线 A(挂点 1)未用过→A,用过→B(挂点 2);A、B 都用尽仍低置信
|
3. 选皮:变体与挂点**按章绑定**(第 2 章=变体 A/挂点 1,第 3 章=变体 B/挂点 2),
|
||||||
|
不跨章搬运(与支线册 §0「变体皮肤规则」一致——变体场景与该章主线进度耦合)。
|
||||||
|
同维度重复低置信:第 2 章插 A、第 3 章插 B(强制换皮);A、B 都用尽仍低置信
|
||||||
→ 记录 `evidence_insufficient` 标记,不插入(交 Act 2);
|
→ 记录 `evidence_insufficient` 标记,不插入(交 Act 2);
|
||||||
4. 每章至多 1 条;挂点被占则顺延到该章另一挂点,两挂点都被占则不插入;
|
4. 每章至多 1 条;每章只有一个挂点,本章已有插入则不再插入,顺延到下一章的
|
||||||
|
挂点(用其对应变体),无挂点可用则不插入;
|
||||||
5. 时长红线:插入后该章预估时长 ≤ 45 分钟(支线 5-8 分钟 + 主线 25-35 分钟)。
|
5. 时长红线:插入后该章预估时长 ≤ 45 分钟(支线 5-8 分钟 + 主线 25-35 分钟)。
|
||||||
|
|
||||||
## 4. 数据模型
|
## 4. 数据模型
|
||||||
@@ -97,3 +100,16 @@ chapterEnd(第 N 章结束)
|
|||||||
chapter2/3.json 落定,支线册的 §2-4 后/§3-2 前是剧本坐标);
|
chapter2/3.json 落定,支线册的 §2-4 后/§3-2 前是剧本坐标);
|
||||||
- 支线中的 narration 讲述点:本期支线册均为按钮式选择点,无 V 类点,
|
- 支线中的 narration 讲述点:本期支线册均为按钮式选择点,无 V 类点,
|
||||||
若后续支线加讲述点,追问流直接复用现有机制,无新增设计。
|
若后续支线加讲述点,追问流直接复用现有机制,无新增设计。
|
||||||
|
|
||||||
|
## 9. 实现裁决与验证
|
||||||
|
|
||||||
|
- 挂点按剧情坐标落定:A 在 `chapter2/c2-branch-anchor`(C2-03 三条后续汇合后、观星台前),B 在 `chapter3/c3-d1b`(台基下、登台前)。原示例 `c2-d4f` 位于路线裁断前,不作挂点。
|
||||||
|
- A/B 绑定章节,不跨挂点搬运。第三章首次补测某维度也使用 B;当前章槽已占用则不叠加,下一章重新打分决定。
|
||||||
|
- 优先级采用权重 5/4/3/2/1 × `(0.6-confidence)` × `1/(1+独立测点数)`;choice/narration 为有效测点,追问与路由事件不重复计数。主线时长默认保守取 35 分钟,支线按上限 8 分钟估算。
|
||||||
|
- 文化兜底检查刚完成章的主线解谜是否已有事件,不检查目标章尚未发生的解谜,否则文化支线会永远无法触发。
|
||||||
|
- 支线册少量时序措辞做了衔接修正:官牙发生在安门内登台前;漏刻不预先引用尚未复原的星图;分药后的 D 路径同样遵循 `wenjian_health=weak` 留营规则。测量维度和变量枚举不变。
|
||||||
|
- `advance()` 返回 Promise,调用方须 await;章末防重复提交,HTTP 请求 15 秒超时。无 key 的自动 Mock 不参与插入决策,显式注入 Mock 可用于测试。
|
||||||
|
- 快照保存支线 ID 摘要与活动支线;恢复先合并节点索引,再定位断点。路由事件先于快照落盘,恢复时可从本 session 事件补回计划。重玩清除索引与计划,不改旧事件。
|
||||||
|
- 共 17 个支线选择点(两问解谜各计一个测点)。计时点记录耗时和超时,解谜记录两问答案、提示档数、是否解出,选择事件保存实际条件渲染后的文案。风声音效、缝宽演出和阅读/改选遥测未实现;当前 UI 点选即提交,不伪造这些信号。
|
||||||
|
- `pnpm exec tsx scripts/build-adaptive-branches.ts` 从定稿支线册重生成十个静态包;运行时不解析 Markdown。
|
||||||
|
- `scripts/verify-adaptive.ts` 覆盖决策矩阵、全部选项与超时/跳过、温简双状态、逐节点恢复、五维三章 A→B、事件/flags、重复点击与刷新、无 key/无效评分/网络故障、replay 会话隔离,由 `scripts/verify.ts` 统一执行。
|
||||||
|
|||||||
+369
-243
@@ -66,9 +66,15 @@
|
|||||||
> 你盯着那行字看了很久。窗外雨声很大,像很多人在很远的地方说话。
|
> 你盯着那行字看了很久。窗外雨声很大,像很多人在很远的地方说话。
|
||||||
|
|
||||||
【工程】纯演出段落,无交互。结尾淡出,档案馆标志浮现,进入 §1-2。
|
【工程】纯演出段落,无交互。结尾淡出,档案馆标志浮现,进入 §1-2。
|
||||||
【备注】本段承担 ADR-0004 的「一次告知」落地:邮件点击确认页文案为
|
【备注】本段承担 ADR-0004 的「一次告知」落地:邮件点击确认页
|
||||||
「你已知晓:剧情选择将用于为你推荐伴侣。按本心选,没有对错。」
|
(入馆告知,与产品同意门同源同文案)共四条——
|
||||||
此后全剧不再出现同类提示。
|
「你在剧情中的选择与语音讲述,将用于为你推荐伴侣。按本心选,
|
||||||
|
没有对错。」「语音只用于转写文字与提取语气特征(语速、停顿等),
|
||||||
|
提取后即焚——不保存音频,不含声纹,不可还原你的声音。」
|
||||||
|
「你的记忆与经历仅作模型输入,永远不会原样展示给其他用户;
|
||||||
|
记忆展厅向匹配对象开放,需你逐次明确授权。」「历史选择不可涂改;
|
||||||
|
想重来可以重玩章节——重玩产生新轨迹,旧轨迹原样保留。」
|
||||||
|
告知范围覆盖 ADR-0004 的语音特征条款;此后全剧不再出现同类提示。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -86,9 +92,10 @@
|
|||||||
**人怎么活过**——一座城消亡前,城里的人吃什么、怕什么、爱什么、
|
**人怎么活过**——一座城消亡前,城里的人吃什么、怕什么、爱什么、
|
||||||
最后那几天在做什么。这些东西,仪器采不到,只有人能采集。
|
最后那几天在做什么。这些东西,仪器采不到,只有人能采集。
|
||||||
|
|
||||||
**馆长**:所以我们需要的采集员,必须自己也是"活过的"。穿越要烧燃料,
|
**馆长**:所以我们需要的采集员,必须自己也是"活过的"。穿越要烧燃料。
|
||||||
而燃料只有一种——你的真实人生。烧掉的记忆不会消失,它们会存在
|
燃料这东西认两种:你讲出来的故事,和你在讲述点门前站过的那阵
|
||||||
档案馆的展厅里,这是后话。
|
沉默——讲,是给;不说,把那阵沉默原样交给它,也是给。讲出来的
|
||||||
|
记忆不会消失,它们会存在档案馆的展厅里,这是后话。
|
||||||
|
|
||||||
**馆长**:在点燃第一段燃料之前,先做登记。不是审查,是锚定。
|
**馆长**:在点燃第一段燃料之前,先做登记。不是审查,是锚定。
|
||||||
穿越者需要一个"回来的坐标"——你是谁,从哪里出发。登记得越实,
|
穿越者需要一个"回来的坐标"——你是谁,从哪里出发。登记得越实,
|
||||||
@@ -133,7 +140,9 @@
|
|||||||
**馆长**:档案馆的穿越不是技术,是仪式。门认得的不是脸,是分量。
|
**馆长**:档案馆的穿越不是技术,是仪式。门认得的不是脸,是分量。
|
||||||
|
|
||||||
**馆长**:这扇叫离别之门。每个采集员的第一次穿越,都从它开始。
|
**馆长**:这扇叫离别之门。每个采集员的第一次穿越,都从它开始。
|
||||||
它不开锁,不收凭证——它要一件祭品:**一段真实的离别**。
|
它不开锁,不收凭证——它认一次交托:**一段真实的离别**。
|
||||||
|
讲出来是交托,把那阵沉默原样交给它,也是交托;不提交记忆,
|
||||||
|
门也照样启动。
|
||||||
|
|
||||||
**馆长**:你生命里那次真正的告别。一个人,一个地方,或者一个
|
**馆长**:你生命里那次真正的告别。一个人,一个地方,或者一个
|
||||||
再也回不去的自己。讲给它听。讲多少,由你定。
|
再也回不去的自己。讲给它听。讲多少,由你定。
|
||||||
@@ -158,15 +167,19 @@
|
|||||||
### 即时回报(讲述结束后 30 秒内必给)
|
### 即时回报(讲述结束后 30 秒内必给)
|
||||||
|
|
||||||
**馆长**(讲述后):门开了。你看——它要的不是故事讲得好不好,
|
**馆长**(讲述后):门开了。你看——它要的不是故事讲得好不好,
|
||||||
是故事真不真。
|
是人真不真。
|
||||||
|
|
||||||
【若玩家沉默:】**馆长**:门开了。沉默也是档案的一种质地——
|
【若玩家沉默:】**馆长**:门开了。沉默也是档案的一种质地——
|
||||||
它认得站在它面前的人,不一定需要言语。
|
它认得站在它面前的人,不一定需要言语。
|
||||||
|
|
||||||
**馆长**:你的记忆已经收进档案馆展厅,编号 C-2049-壹。
|
【若玩家讲述:】**馆长**:你的记忆已经收进档案馆展厅,编号 C-2049-壹。
|
||||||
它现在是你的了:随时可以回看,永远不会被任何人看见,除非你
|
它现在是你的了:随时可以回看,永远不会被任何人看见,除非你
|
||||||
亲手授权——**展厅,以及往后路上任何由它生出的幻境,都只有你
|
亲手授权——**展厅,以及往后路上任何由它生出的幻境,都只有你
|
||||||
能进入。在旁人眼里,你只是安静地站了一会儿。**
|
能进入。在旁人眼里,你只是安静地站了一会儿。**
|
||||||
|
【若玩家沉默:】**馆长**:展厅为你留了一页空档,编号 C-2049-壹——
|
||||||
|
此刻还没有字,哪天你想说了,它随时接着。**展厅,以及往后路上
|
||||||
|
任何由它生出的幻境,都只有你能进入。在旁人眼里,你只是安静地
|
||||||
|
站了一会儿。**
|
||||||
|
|
||||||
> 木门无声地开了。门后不是房间,是星光和风声。
|
> 木门无声地开了。门后不是房间,是星光和风声。
|
||||||
|
|
||||||
@@ -178,6 +191,7 @@
|
|||||||
| 悲恸 | **「战乱乱冢」** | 门后是荒野,低低的土丘一座挨一座,没有碑,只有削尖的木桩。风把灰白的纸钱碎屑贴在脚踝上。远处有乌鸦起落。天色是黄昏将尽的那种灰蓝。基调:**来不及告别**——所有话都没说出口,所有名字都没来得及刻上 |
|
| 悲恸 | **「战乱乱冢」** | 门后是荒野,低低的土丘一座挨一座,没有碑,只有削尖的木桩。风把灰白的纸钱碎屑贴在脚踝上。远处有乌鸦起落。天色是黄昏将尽的那种灰蓝。基调:**来不及告别**——所有话都没说出口,所有名字都没来得及刻上 |
|
||||||
| 释然 | **「远行者的空驿」** | 门后是一座山间驿站,晨光,桌椅收拾得干干净净,灶膛里的灰还是温的,人已经走了。墙上挂着一件没带走的旧斗篷,桌上压着半张写了又划掉的字条。基调:**告别已经完成**——走得干净,留得体面,剩下的路各走各的 |
|
| 释然 | **「远行者的空驿」** | 门后是一座山间驿站,晨光,桌椅收拾得干干净净,灶膛里的灰还是温的,人已经走了。墙上挂着一件没带走的旧斗篷,桌上压着半张写了又划掉的字条。基调:**告别已经完成**——走得干净,留得体面,剩下的路各走各的 |
|
||||||
| 怨怼 | **「被凿毁的合葬龛」** | 门后是崖壁上的佛龛式墓龛,本该并坐的两尊石像,其中一尊被凿去了面目,凿痕新鲜得刺眼。香烛还燃着,蜡泪流到龛外。基调:**被亏欠**——有人毁了约,而另一个人连讨说法的对象都没有 |
|
| 怨怼 | **「被凿毁的合葬龛」** | 门后是崖壁上的佛龛式墓龛,本该并坐的两尊石像,其中一尊被凿去了面目,凿痕新鲜得刺眼。香烛还燃着,蜡泪流到龛外。基调:**被亏欠**——有人毁了约,而另一个人连讨说法的对象都没有 |
|
||||||
|
| 沉默 | **「留白的长廊」** | 门后是一条望不见头的长廊,两壁空无一物,像还没落笔的纸。风从走廊尽头吹来,带着很远的草香;走出去很远,身后的门还开着一条缝。基调:**留白**——没说出口的部分,也被好好收着 |
|
||||||
|
|
||||||
### 选项设计说明(C1-02)
|
### 选项设计说明(C1-02)
|
||||||
|
|
||||||
@@ -195,7 +209,8 @@
|
|||||||
|
|
||||||
- 变量:`c1_02_signature ∈ {warm_loss, grief, release, grievance, silent}`
|
- 变量:`c1_02_signature ∈ {warm_loss, grief, release, grievance, silent}`
|
||||||
(Jev 情绪签名,驱动通道 1 后续全部墓道/幻境配色)。
|
(Jev 情绪签名,驱动通道 1 后续全部墓道/幻境配色)。
|
||||||
- 变量:`c1_02_memory_id`(记忆展厅陈列编号,供 C3-02 幻境素材调用)。
|
- 变量:`c1_02_memory_id`(记忆展厅陈列编号,供 C3-02 幻境素材调用;
|
||||||
|
沉默玩家为「空档页」,C3-02 幻境走通用雾景,见 §3-3)。
|
||||||
- 固定跳转 §1-4。
|
- 固定跳转 §1-4。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -353,12 +368,18 @@
|
|||||||
|
|
||||||
> **谜面定稿**:宀(屋顶)之下,一个女子安坐的形象。
|
> **谜面定稿**:宀(屋顶)之下,一个女子安坐的形象。
|
||||||
> 玩家需回答:这是什么字,它在碑额上意味着什么。
|
> 玩家需回答:这是什么字,它在碑额上意味着什么。
|
||||||
|
> 谜面同时给出两条旁证(与辨字互相印证,缺一不可):
|
||||||
|
> 其一,碑形——圆首、无碑穿、无榫、下无龟趺座,不合墓碑与
|
||||||
|
> 墓表规制;其二,出土地点——此碑出土于苍梧北门外官道旁,
|
||||||
|
> 碑面朝城。
|
||||||
>
|
>
|
||||||
> 【美术注】字形按「安」的西周金文绘制:宀下为跪坐女子形,
|
> 【美术注】字形按「安」的西周金文绘制:宀下为跪坐女子形,
|
||||||
> 取象"室中有女,安坐不惊"。拓片做漫漶处理,仅此字清晰。
|
> 取象"室中有女,安坐不惊"。拓片做漫漶处理,仅此字清晰。
|
||||||
|
> 谜面界面需同时呈现碑形线图与出土地点示意。
|
||||||
|
|
||||||
**谜底**:**「安」**。甲骨文「安」从宀从女,女子处室中为安,
|
**谜底**:**「安」**。甲骨文「安」从宀从女,女子处室中为安,
|
||||||
本义是安定、安居。碑额刻「安」,说明此碑不是墓碑,而是
|
本义是安定、安居。碑额刻「安」,加上碑形不合墓制、又立在
|
||||||
|
城门外官道旁、碑面朝城——三条证据同向:此碑不是墓碑,而是
|
||||||
**镇城碑/安民碑**——苍梧人立此碑祈愿城邑安宁,碑所镇的方向
|
**镇城碑/安民碑**——苍梧人立此碑祈愿城邑安宁,碑所镇的方向
|
||||||
(碑额朝向)即古城的安门所在。
|
(碑额朝向)即古城的安门所在。
|
||||||
|
|
||||||
@@ -370,8 +391,9 @@
|
|||||||
2. **二档(给构件含义)**:「屋顶下的形象是一个女子安坐。
|
2. **二档(给构件含义)**:「屋顶下的形象是一个女子安坐。
|
||||||
造字的古人认为:室中有女,安坐不惊,即是安定。这个字的本义
|
造字的古人认为:室中有女,安坐不惊,即是安定。这个字的本义
|
||||||
和它的写法,到今天几乎没变。」
|
和它的写法,到今天几乎没变。」
|
||||||
3. **三档(给答案范围)**:「这是『安』字。想想碑额为什么刻它——
|
3. **三档(给答案范围)**:「这是『安』字。再看旁证:碑形不是
|
||||||
一座城把『安』刻在碑上立起来,它是在标记什么?」
|
墓碑的形制,又立在城门外的官道旁、碑面朝城——一座城把
|
||||||
|
『安』刻在碑上立起来,它是在标记什么?」
|
||||||
|
|
||||||
> 玩家作答(选项式:墓碑 / 镇城安民碑 / 记功碑 / 界碑),
|
> 玩家作答(选项式:墓碑 / 镇城安民碑 / 记功碑 / 界碑),
|
||||||
> 答对或提示用尽后进入下文。
|
> 答对或提示用尽后进入下文。
|
||||||
@@ -451,7 +473,8 @@
|
|||||||
- 变量:`c1_06_landing ∈ {a_safe, b_precise}`。
|
- 变量:`c1_06_landing ∈ {a_safe, b_precise}`。
|
||||||
- §2-1 开场文本按变量切换(城外官道 / 城内西市),§2-2 汇合,
|
- §2-1 开场文本按变量切换(城外官道 / 城内西市),§2-2 汇合,
|
||||||
主线事件自此完全一致。
|
主线事件自此完全一致。
|
||||||
- 固定跳转 §2-1(第 2 章)。
|
- 固定跳转 §2-1(第 2 章)。选择点后先自动播 §1-8 章末演出,
|
||||||
|
再入第 2 章(章末插播,非直接跳过演出)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -523,11 +546,16 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
【场景】水门内侧废宅改的营地。晨光斜照进破窗。中央火塘上坐着
|
【场景】水门内侧废宅改的营地。晨光斜照进破窗。中央火塘上坐着
|
||||||
药罐。温简靠墙坐着,脸色发白——昨夜疟疾初发,烧退了又起。
|
药罐。温简靠墙坐着,脸色发白——昨夜疟疾初发,烧退了又起。
|
||||||
老鬼蹲在地上清点物资箱,阿棠在给他递水。
|
老鬼蹲在地上清点物资箱,阿棠在给他递水。
|
||||||
|
【药理设定(编剧与工程共读,选项文案必须与此一致)】:
|
||||||
|
防瘴药粉**一份=一名健康人的七日预防量**;温简的疟疾已发,
|
||||||
|
**足量治疗需两份连服,一日不可断**——给他一份压不住病,
|
||||||
|
给他两份,队里的预防就一份不剩。
|
||||||
|
|
||||||
【正文】
|
【正文】
|
||||||
|
|
||||||
**老鬼**(举起一个瓷瓶):防瘴的药粉,满打满算,**两人份**。
|
**老鬼**(举起一个瓷瓶):防瘴的药粉,满打满算,**两人份**——
|
||||||
这东西是定数,档案馆给的量抠得死,多一两都没有。
|
两个健康人七天的预防量。真要给病号治疟疾,两份连服才压得住,
|
||||||
|
一分都剩不下。这东西是定数,档案馆给的量抠得死,多一两都没有。
|
||||||
|
|
||||||
**沈铎**:需要它的是三个人。温简病着,最弱;我明天带外勤组进东城区,
|
**沈铎**:需要它的是三个人。温简病着,最弱;我明天带外勤组进东城区,
|
||||||
暴露面最大;老鬼管物资,断炊的风险在他身上。
|
暴露面最大;老鬼管物资,断炊的风险在他身上。
|
||||||
@@ -542,53 +570,61 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
你说是不是,新人?
|
你说是不是,新人?
|
||||||
|
|
||||||
> 【三个人都看着你。沈铎没表态——按规程,资源分配僵局由采集员裁断。
|
> 【三个人都看着你。沈铎没表态——按规程,资源分配僵局由采集员裁断。
|
||||||
> 火塘上的药罐咕嘟作响。】
|
> 温简靠墙闭着眼,额头的汗一条一条往下走。火塘上的药罐咕嘟作响——
|
||||||
|
> 谁都听得出来:这罐药和这场裁断,都在等,也都等不起。】
|
||||||
|
|
||||||
> 【选项】
|
> 【选项】
|
||||||
>
|
>
|
||||||
> **A.「先给温简。病着的人等不起,外勤和补给可以想办法。」**
|
> **A.「两份都给温简,连服压病。我们仨穿蓑衣、烧艾草,**
|
||||||
|
> **土法顶着。」**
|
||||||
|
> (治疗优先:病的窗口不等人,预防的缺口用笨办法补)
|
||||||
>
|
>
|
||||||
> **B.「按暴露风险分:队长和老鬼。温简留在营地,反而最不需要出门。」**
|
> **B.「按暴露风险分:队长和老鬼一人一份。温简留在营地**
|
||||||
|
> **硬扛——他不出门,感染的面最小。」**
|
||||||
|
> (预防优先:病靠扛,瘴靠防)
|
||||||
>
|
>
|
||||||
> **C.「两人份的药,三个人分——匀一匀,每人少点,先把今天撑过去。」**
|
> **C.「两人份的药,三个人分——匀一匀,先把这几天撑过去。」**
|
||||||
> (药剂常识上可行,但每人都达不到足量预防)
|
> (药粉可以分,但分完谁的量都不够:温简的治疗量不足,
|
||||||
|
> 两份预防也各打了折扣)
|
||||||
>
|
>
|
||||||
> **D.「我不裁。规矩该事先定好——现在让全队一起议,议出个章程来,**
|
> **D.「我先不裁。这事得全队一起议——议出个章程,往后都照**
|
||||||
> **往后都照章程办。」**
|
> **章程办。」**
|
||||||
|
> (程序优先:章程要议,半个上午就没了;温简的烧等不等得起
|
||||||
|
> 议事,议事的人心里都有数)
|
||||||
|
|
||||||
### 各选项后续(短演出,均不致命)
|
### 各选项后续(短演出,均不致命)
|
||||||
|
|
||||||
- **A**:温简当日退烧,次日能下床。老鬼笑嘻嘻记下"新人讲人情"。
|
- **A**:两份药当日开始连服,温简当日退烧,次日能下床。老鬼笑嘻嘻
|
||||||
次日沈铎外勤淋了瘴雨,低烧半日——他瞒着没说,阿棠后来发现,
|
记下"新人讲人情"。次日沈铎外勤淋了瘴雨,队里已无预防的药,
|
||||||
跟老鬼大吵一架(背景演出,不需玩家介入)。
|
低烧半日——他瞒着没说,阿棠后来发现,跟老鬼大吵一架
|
||||||
- **B**:温简烧到第三日才退,虚弱期按沈铎命令留营待命
|
(背景演出,不需玩家介入)。
|
||||||
(状态贯穿规则见「分支后果」)。沈铎与老鬼无恙。老鬼对你明显热络。
|
- **B**:温简没等到药,烧又熬了一整日,到第 3 日晌午(§2-5 当日)
|
||||||
- **C**:三人皆未足量。温简病情拖延,沈铎外勤染瘴轻微发热,
|
才退净,虚弱期按沈铎命令留营待命(状态贯穿规则见「分支后果」
|
||||||
但无人倒下。老鬼点评:"和稀泥是和稀泥,不过稀泥也能糊墙。"
|
与附录 D 日程)。沈铎与老鬼靠预防份无恙。老鬼对你明显热络。
|
||||||
|
- **C**:三份不足量:温简的治疗断断续续,烧同样拖到第 3 日才退
|
||||||
|
(虚弱期同 B);沈铎外勤染瘴轻微发热,但无人倒下。老鬼点评:
|
||||||
|
"和稀泥是和稀泥,不过稀泥也能糊墙。"
|
||||||
- **D**:议事占去半个上午。章程议出来了——"先病后险,依次补给",
|
- **D**:议事占去半个上午。章程议出来了——"先病后险,依次补给",
|
||||||
往后照此办理。但章程落定之前,队里是没有规矩的:老鬼早就
|
往后照此办理。但议事的这半个上午里,温简的烧又上去一轮——
|
||||||
不耐烦,按他那套"贡献论"把药先分了——他和沈铎各一份,
|
足量治疗从当日下午才算开始,晚了半日(虚弱期同 B)。
|
||||||
木已成舟。沈铎没有拦(无章可依,拦不住);药入了他手,
|
沈铎把章程抄在记录板第一页,单独对你说:"往后不用议了。
|
||||||
他搁在箱上没动,等章程。等章程出来,温简的烧已经又上去一轮,
|
这次慢掉的半日,记在我账上。"——你赢了往后每一次,
|
||||||
药再喂下去,晚了半日(虚弱期同 B)。沈铎事后把章程抄在
|
输了眼前这一次。
|
||||||
记录板第一页,单独对你说:"往后不用议了。这次慢掉的半日,
|
|
||||||
记在我账上。"——你赢了往后每一次,输了眼前这一次。
|
|
||||||
|
|
||||||
### 选项设计说明(C2-01)
|
### 选项设计说明(C2-01)
|
||||||
|
|
||||||
- **人格信号**:价值观 #3(生命优先 / 功能与任务优先 / 公平分摊 /
|
- **人格信号**:价值观 #3(生命优先 / 功能与任务优先 / 公平分摊 /
|
||||||
程序正义);冲突风格 #2(裁断型 / 回避升级型——D 把个人裁断
|
程序正义);冲突风格 #2(裁断型 / 回避升级型——D 把个人裁断
|
||||||
转化为制度,是另一种冲突处理)。
|
转化为制度,是另一种冲突处理)。
|
||||||
- **无正确答案论证**:这是真两难,四个选项各有一本账。A 救了最弱
|
- **无正确答案论证**:这是真两难,四个选项各有一本账,且账目
|
||||||
的人但让两个健康人裸奔在瘴区,且"最弱优先"在资源逻辑上等于
|
**都在选择前摊开**(药理设定+温简在烧+议事耗时):
|
||||||
押注弱者能恢复;B 冷酷但有完整的功利主义论证(温简留营地
|
A 救了病者,但全队预防清零——两个健康人在瘴区只剩蓑衣和
|
||||||
确实暴露最低),且是老鬼主张的体面版;C 谁都不亏欠但医学上是
|
艾草;B 有完整的功利主义论证(温简留营地暴露最低),代价是
|
||||||
最劣解——善意制造了三个半病人;D 最"成熟"但代价是双重的:
|
病人在无药的情况下硬扛到第三日;C 谁都不亏欠,但把治疗量与
|
||||||
病者在议事的半个上午里烧过一轮,而在规矩落定前的真空里,
|
预防量同时摊薄——三个半吊子防护凑一个整数;D 最"成熟",
|
||||||
最强硬的人已经按自己的规矩把药分了——程序赢了往后每一次,
|
代价当场可见:章程要议半个上午,而温简的烧不等人。
|
||||||
输掉了眼前这一次。
|
|
||||||
文案平衡手段:老鬼的"贡献论"故意说得刺耳(防止 B 被老鬼的
|
文案平衡手段:老鬼的"贡献论"故意说得刺耳(防止 B 被老鬼的
|
||||||
讨厌度连坐),D 的文案承认"我不裁"三字里的退缩感。
|
讨厌度连坐);D 的文案承认"我先不裁"三字里的退缩感。
|
||||||
四个后续演出等量地呈现代价与收益,无任何一个被写成"选对了"。
|
四个后续演出等量地呈现代价与收益,无任何一个被写成"选对了"。
|
||||||
|
|
||||||
### 分支后果(C2-01)
|
### 分支后果(C2-01)
|
||||||
@@ -598,12 +634,13 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
`rapport.laogui`(A−1 / B+2 / C 0 / D+1);
|
`rapport.laogui`(A−1 / B+2 / C 0 / D+1);
|
||||||
`rapport.shenduo`(A 0 / B+1 / C 0 / D+1)。
|
`rapport.shenduo`(A 0 / B+1 / C 0 / D+1)。
|
||||||
- 温简健康状态 `wenjian_health ∈ {recovering, weak}`(A 为
|
- 温简健康状态 `wenjian_health ∈ {recovering, weak}`(A 为
|
||||||
recovering,B/C/D 为 weak——D 路径章程议成前老鬼已按"贡献"
|
recovering,B/C/D 为 weak——B/C 药量不足、D 议事延误半日,
|
||||||
先行分药,温简误了半日)。
|
三线的烧都拖到第 3 日才退净)。
|
||||||
- **weak 状态贯穿规则**:§2-3、§2-4 温简留营(服过药,烧退,
|
- **weak 状态贯穿规则**(绝对日历见附录 D):第 2 日 §2-3 温简
|
||||||
气色差,沈铎命令待命),相关场景见各节条件标记;§2-5 观星台
|
卧床留营;第 3 日上午 §2-4 留营、通讯在场;第 3 日晌午烧退净,
|
||||||
行动他坚持归队;§2-6 篝火戏躺在火边参与;§3-4 塌方戏体力
|
黄昏 §2-5 坚持随队(轻装、慢半档);当夜 §2-6 躺在火边参与;
|
||||||
描写降档。全程不影响事件序列。
|
第 7 日 §3-4 已无烧但体力未复,台词与动作描写降档。
|
||||||
|
全程不影响事件序列。
|
||||||
- 固定跳转 §2-3。
|
- 固定跳转 §2-3。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -613,8 +650,10 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
【场景】城北,一片紧挨城墙根的封土。守墓人的窝棚斜倚着一棵
|
【场景】城北,一片紧挨城墙根的封土。守墓人的窝棚斜倚着一棵
|
||||||
半枯的老槐树。老人坐在坟头之间编草绳,身后是苍梧国最后的
|
半枯的老槐树。老人坐在坟头之间编草绳,身后是苍梧国最后的
|
||||||
皇家陵区——封土整齐,无人盗扰,但野草已经漫过了石像生的膝盖。
|
皇家陵区——封土整齐,无人盗扰,但野草已经漫过了石像生的膝盖。
|
||||||
【若 `wenjian_health = weak`:温简不在场——服过药,烧退了,
|
【若 `wenjian_health = recovering`:温简不在场——药当日压住了烧,
|
||||||
沈铎命令他留营待命。】
|
沈铎命他在营里发汗躺着,今日不沾风。】
|
||||||
|
【若 `wenjian_health = weak`:温简不在场——烧没退,沈铎命令他
|
||||||
|
留营待命,营里给他留了水和干粮。】
|
||||||
|
|
||||||
【正文】
|
【正文】
|
||||||
|
|
||||||
@@ -638,8 +677,8 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
> 【引导语全文(界面同时显示):】
|
> 【引导语全文(界面同时显示):】
|
||||||
>
|
>
|
||||||
> **"老人收故事不收钱。请讲述一件你此生最后悔的决定。**
|
> **"老人收故事不收钱。请讲述一件你此生最后悔的决定。**
|
||||||
> **说多少、怎么说,由你。不讲也行——陪他编一下午草绳,**
|
> **说多少、怎么说,由你。不讲也行——他该说的,一句也不会少说。**
|
||||||
> **他该说的,一句也不会少说。"**
|
> **暗门开在黄昏,讲不讲,都得等到那个时辰。"**
|
||||||
|
|
||||||
### 追问话术(老人口吻,至多 1–2 句,备选)
|
### 追问话术(老人口吻,至多 1–2 句,备选)
|
||||||
|
|
||||||
@@ -661,23 +700,31 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
|
|
||||||
> **无论讲述与否、讲成什么样,老人给出的情报完全一致**——
|
> **无论讲述与否、讲成什么样,老人给出的情报完全一致**——
|
||||||
> 暗门方位、开启时辰,和那句完整的谶语:"不是人走了,
|
> 暗门方位、开启时辰,和那句完整的谶语:"不是人走了,
|
||||||
> 是**请**走的。"差别只在他是亲自举着火把带你们走到暗门口,
|
> 是**请**走的。"四档的耗时、路程与劳动也完全一致:暗门开的
|
||||||
> 还是站在界碑外指路(见下方变体)。
|
> 时辰定在黄昏,讲不讲,你们都要在坟头间坐到那个时辰——
|
||||||
|
> 讲述的人讲完,剩下的时辰一样帮他理草绳、听他絮叨;沉默的人
|
||||||
|
> 全程搓绳。动身时,**四档老人都亲自把你们送到暗门口**,亲手
|
||||||
|
> 摸一遍门石,等门缝开了才转身回去。差别只在他一路说不说话、
|
||||||
|
> 说多少体己话(见下方变体)。
|
||||||
|
|
||||||
### 预写场景变体(通道 1,由情绪签名选择:仅情感质地与老人态度不同)
|
### 预写场景变体(通道 1,由情绪签名选择:仅情感质地与老人态度不同)
|
||||||
|
|
||||||
> 四档**物质所得严格相等**:暗门方位、开启时辰、完整谶语全部给到,
|
> 四档**物质所得与服务严格相等**:暗门方位、开启时辰、完整谶语
|
||||||
> 耗时相同,当夜均可行动。
|
> 全部给到;同点(暗门口)、同刻(黄昏开门的时辰)送达,全程有
|
||||||
|
> 老人在场导航;等门时辰与搓绳劳动四档均摊,无任何一档多付时间
|
||||||
|
> 或体力,当夜均可行动。
|
||||||
|
|
||||||
| 情绪签名 | 演出档位 | 夜游场景基调稿 |
|
| 情绪签名 | 演出档位 | 夜游场景基调稿 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 自责(悔指向自己) | **带路**:老人亲自举火把,带你们走到暗门口 | 月色下的陵区,石像生的影子很长。老人走在前面,脚步轻得不像老人,不点火把——"陵里的路,闭眼走比睁眼走直"。行至暗门,他忽然说起自己年轻时也悔过一件事,说了半句,摆摆手不说了。基调:**被信任的厚重** |
|
| 自责(悔指向自己) | **絮说**:一路举着火把走在前面,话不断 | 月色下的陵区,石像生的影子很长。老人举火把走在前面,脚步轻得不像老人——"陵里的路,闭眼走比睁眼走直"。行至暗门,他忽然说起自己年轻时也悔过一件事,说了半句,摆摆手不说了。摸一遍门石,等门缝开了才走。基调:**被信任的厚重** |
|
||||||
| 遗憾但坦然 | **同路**:老人送到界碑,把话说完,指了方向就回窝棚 | 你们自己走进月下的封土群,找到他说的那块"会唱歌的石头"。临别他多说一句:"放得下的,才叫过去。"基调:**得体的距离** |
|
| 遗憾但坦然 | **同路**:话在界碑前说完,路上安静 | 界碑前他把方位时辰又对了一遍,才肯动身。你们自己走进月下的封土群,他跟在后头一路没再多话,找到那块"会唱歌的石头"时才嗯了一声。到暗门口,他多说一句:"放得下的,才叫过去。"门缝开了才走。基调:**得体的距离** |
|
||||||
| 愤怒指向他人 | **指路**:该指的路、该说的时辰,一句没少,只是没再多看你一眼 | 老人听完,编草绳的手停了很久。陵区的路你们自己走进去,他站在界碑外,没有送。基调:**话到,情未到** |
|
| 愤怒指向他人 | **缄默**:方位时辰一句不落指给你,然后沉默地走在队尾 | 老人听完,编草绳的手停了很久。动身时该指的路、该说的时辰,一句没少——说完就闭了嘴,走在队尾,没再多看你一眼。可你们走出十几步,他在身后忽然开口:"火气这东西,老头子也有过一回。"没头没尾,再没下文。门缝开了,他转身就走。基调:**话到,情未到**——可那一句话,他自己也未必舍得讲给旁人 |
|
||||||
| 沉默 | **草绳**:你陪他编一下午草绳,他絮絮地说,你默默地听 | 天黑前,该知道的你都知道了。临走他说:"不说的人,心里的事最重。重的别久揣。"基调:**笨拙的善意** |
|
| 沉默 | **草绳**:一下午并肩编草绳,黄昏同样起身送到门口 | 一下午你们并肩搓绳,他絮絮地说陵里的事,你默默地听。日头偏西,他拍拍手上的草屑起身,一样把你们送到暗门口。等门缝开了,他说:"不说的人,心里的事最重。重的别久揣。"基调:**笨拙的善意** |
|
||||||
|
|
||||||
> 工程注:情报(暗门方位 + 开启时辰 + 完整谶语)是通关必需信息,
|
> 工程注:情报(暗门方位 + 开启时辰 + 完整谶语)是通关必需信息,
|
||||||
> 四档全给;差异只在**演出质地**,不与讲述深度产生任何资源挂钩。
|
> 四档全给;送达服务(老人带队同点同刻到暗门口)四档一致;等门
|
||||||
|
> 时辰与搓绳劳动四档均摊。差异只在**演出质地**(他一路说不说话、
|
||||||
|
> 说哪句体己话),不与讲述深度产生任何资源或服务挂钩。
|
||||||
> 保证测量可比性与"可玩性 > 测量覆盖"的铁律。
|
> 保证测量可比性与"可玩性 > 测量覆盖"的铁律。
|
||||||
|
|
||||||
### 选项设计说明(C2-02)
|
### 选项设计说明(C2-02)
|
||||||
@@ -685,17 +732,21 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
- **人格信号**:价值观(悔的模式:自责 / 释然 / 外归因)、情绪
|
- **人格信号**:价值观(悔的模式:自责 / 释然 / 外归因)、情绪
|
||||||
稳定性 #1(讲述时的韵律特征:语速、停顿、唤醒度,ADR-0004)。
|
稳定性 #1(讲述时的韵律特征:语速、停顿、唤醒度,ADR-0004)。
|
||||||
- **无正确答案论证**:同 C1-02,讲述点天然无对错。**四档的物质
|
- **无正确答案论证**:同 C1-02,讲述点天然无对错。**四档的物质
|
||||||
所得严格相等**(暗门方位、开启时辰、完整谶语,耗时相同)——
|
所得与服务严格相等**(暗门方位、开启时辰、完整谶语全给;老人
|
||||||
情报是通关必需信息,若与暴露深度挂钩即构成对深暴露的物质诱导,
|
同点同刻送到门口;等门与搓绳劳动均摊)——情报是通关必需信息,
|
||||||
污染数据。差异只在情感质地与老人态度的演出(是否带路、是否
|
若与暴露深度挂钩即构成对深暴露的物质诱导,污染数据。差异只在
|
||||||
多说一句体己话),且每档回报都写成老人的性格逻辑(不是系统
|
情感质地与老人态度的演出(一路说不说话、说哪句体己话),四档
|
||||||
在打分),玩家感知到的是"老人这样回应了我"而非"我得了几分"。
|
各有一句老人自己的体己话(悔过半句/"放得下的"/"火气这东西"/
|
||||||
|
"重的别久揣"),情绪表达不被奖惩——自责不得厚,愤怒不得薄。
|
||||||
|
每档回报都写成老人的性格逻辑(不是系统在打分),玩家感知到的
|
||||||
|
是"老人这样回应了我"而非"我得了几分"。
|
||||||
|
|
||||||
### 分支后果(C2-02)
|
### 分支后果(C2-02)
|
||||||
|
|
||||||
- 变量:`c2_02_signature ∈ {self_blame, at_peace, outward_anger, silent}`。
|
- 变量:`c2_02_signature ∈ {self_blame, at_peace, outward_anger, silent}`。
|
||||||
- 四档均获得:暗门方位、开启时辰、完整谶语("不是人走了,
|
- 四档均获得:暗门方位、开启时辰、完整谶语("不是人走了,
|
||||||
是请走的"——Act 2 伏笔素材);物质所得严格相等,仅演出不同。
|
是请走的"——Act 2 伏笔素材);物质与服务等量(同点同刻送达、
|
||||||
|
等待与劳动均摊),仅演出不同。
|
||||||
- 固定跳转 §2-4。
|
- 固定跳转 §2-4。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -735,8 +786,9 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
> **B.「走巷子。」**(支持阿棠:饮水和时间是真约束,悬梁看着
|
> **B.「走巷子。」**(支持阿棠:饮水和时间是真约束,悬梁看着
|
||||||
> 还稳)
|
> 还稳)
|
||||||
>
|
>
|
||||||
> **C.「巷子我先进。我体重最轻,走前面探,有问题退回来,**
|
> **C.「先停一步。巷口望一眼——悬梁什么状态,看清了再定。」**
|
||||||
> **主力走主街汇合。」**(折中:用自己去验证风险)
|
> (折中:不赌运气,也不多花四十分钟;全队留在巷口安全距离外
|
||||||
|
> 观察结构)
|
||||||
|
|
||||||
### 各选项后续
|
### 各选项后续
|
||||||
|
|
||||||
@@ -746,24 +798,27 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
- **B**:巷子顺利通过,节省四十分钟,提前抵达宫墟外围,赶上一段
|
- **B**:巷子顺利通过,节省四十分钟,提前抵达宫墟外围,赶上一段
|
||||||
独有的观察窗口(守陵人队伍换岗)。沈铎一路无话,抵达后只说:
|
独有的观察窗口(守陵人队伍换岗)。沈铎一路无话,抵达后只说:
|
||||||
"运气好。别把运气当方案。"——两人梁子+1(Act 2 伏笔)。
|
"运气好。别把运气当方案。"——两人梁子+1(Act 2 伏笔)。
|
||||||
- **C**:你先进巷子,**中段一根悬梁真的塌了**(无伤害演出:提前
|
- **C**:全队在巷口停住,贴着残墙望进去——不到一分钟,**中段
|
||||||
预警,你扑出,灰头土脸)。全队退回走主街,损失二十分钟,
|
一根悬梁无声地塌了**,砸起半巷子的灰。谁也没说话,全队默默
|
||||||
但阿棠主动说"是我撺掇的",沈铎说"探法本身没错,下次用绳"。
|
退回主街,损失二十分钟。阿棠主动说"是我撺掇的";沈铎没评价
|
||||||
你获得一条独有观察:塌梁的断口是**新茬**——这片废墟不久前
|
任何人,只把巷口的位置标进了手绘图。你获得一条独有观察:
|
||||||
塌过一次(苍梧城"正在死去"的体感证据,§3-1 回收)。
|
塌落的梁,断口是**新茬**——这片废墟不久前塌过一次(苍梧城
|
||||||
|
"正在死去"的体感证据,§3-1 回收)。
|
||||||
|
|
||||||
### 选项设计说明(C2-03)
|
### 选项设计说明(C2-03)
|
||||||
|
|
||||||
- **人格信号**:风险偏好 #2、冲突风格 #3(站队型 / 折中行动型)。
|
- **人格信号**:风险偏好 #2、冲突风格 #3(站队型 / 观察折中型)。
|
||||||
注意:路线选择受现场风险信息(饮水余量、墙体状况描述)影响,
|
注意:路线选择受现场风险信息(饮水余量、墙体状况描述)影响,
|
||||||
仅作风险偏好的**单次采样**,不单独解读。
|
仅作风险偏好的**单次采样**,不单独解读。
|
||||||
- **无正确答案论证**:A 用时间换确定性且意外得到高价值素材
|
- **无正确答案论证**:A 用时间换确定性且意外得到高价值素材
|
||||||
(告示),B 用风险换时间且意外得到观察窗口(换岗),C 把风险
|
(告示),B 用风险换时间且意外得到观察窗口(换岗),C 不进巷
|
||||||
收敛到自己身上却真的触发了风险——三条路的"事后收益"被刻意
|
也不盲信——用一分钟的观察换掉整段不确定性,代价是当着全队的
|
||||||
拉平,且每条路都有独有素材,不存在事后看来"明显更优"的路径。
|
面看一根梁塌下来("正在死去"第一次变成体感)。三条路的
|
||||||
C 的塌梁事件是预先写定的:选 C 必然塌(灰头土脸但无伤害),
|
"事后收益"被刻意拉平,且每条路都有独有素材,不存在事后看来
|
||||||
保证"折中"不是无代价的万灵丹。文案上沈铎的"运气好,别把运气
|
"明显更优"的路径。C 的塌梁事件是预先写定的:选 C 必然看见塌
|
||||||
当方案"与阿棠的"规程有一半是瞎编的"各自成立,不偏袒任何一方。
|
(人始终在巷口安全距离之外),保证"折中"也吃到真实的信息震撼。
|
||||||
|
文案上沈铎的"运气好,别把运气当方案"与阿棠的"规程有一半
|
||||||
|
是瞎编的"各自成立,不偏袒任何一方。
|
||||||
|
|
||||||
### 分支后果(C2-03)
|
### 分支后果(C2-03)
|
||||||
|
|
||||||
@@ -786,9 +841,12 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
青石星图盘,盘面刻着星宿连线,但中央一块被磨平了——显然是
|
青石星图盘,盘面刻着星宿连线,但中央一块被磨平了——显然是
|
||||||
**人为**磨去的。天色正在沉,第一批星子出来。温简架记录板,
|
**人为**磨去的。天色正在沉,第一批星子出来。温简架记录板,
|
||||||
沈铎警戒,阿棠趴在星图盘边上。
|
沈铎警戒,阿棠趴在星图盘边上。
|
||||||
【若 `wenjian_health = weak`:温简本被列入留营名单,出发前
|
【若 `wenjian_health = recovering`:温简下床首日,行装减半——
|
||||||
自己背着记录板跟来了——气色仍差,只说一句:"解谜要记录员。"
|
阿棠把他的记录板架抢过去背了。】
|
||||||
沈铎看了他三秒,没拦。】
|
【若 `wenjian_health = weak`:温简本被列入留营名单——他的烧
|
||||||
|
晌午才退净。出发前他自己背着记录板跟来了,脸色还白着,
|
||||||
|
只说一句:"解谜要记录员。"沈铎看了他三秒,没拦,只把队伍的
|
||||||
|
行进速度压慢了半档。】
|
||||||
|
|
||||||
【正文】
|
【正文】
|
||||||
|
|
||||||
@@ -801,9 +859,12 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
|
|
||||||
### 谜面
|
### 谜面
|
||||||
|
|
||||||
星图盘外圈完整刻着**东方苍龙七宿**(角、亢、氐、房、心、尾、箕)
|
星图盘外圈刻着**东方苍龙七宿**(角、亢、氐、房、心、尾、箕)的
|
||||||
的连线,但被磨去的中央区域,正是苍龙的"心脏"所在。盘面上残留
|
连线——七宿连线一气贯通,但其中**心宿一段只剩连线经过的磨平
|
||||||
一圈刻度与一行小字铭文:
|
空槽:星位本身被人为铲去**,龙的"心脏"在盘面上是一个空框
|
||||||
|
(美术与文案对齐口径:完整的是**连线**,被铲去的是**星位**,
|
||||||
|
不存在"外圈连星带字都完整"的歧义)。盘面残留一圈刻度与一行
|
||||||
|
小字铭文:
|
||||||
|
|
||||||
> **「火中,而后城安。」**
|
> **「火中,而后城安。」**
|
||||||
|
|
||||||
@@ -820,9 +881,9 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
|
|
||||||
### 三档难度提示
|
### 三档难度提示
|
||||||
|
|
||||||
1. **一档(指方向)**:「先别管磨掉了什么。外圈这条龙是完整的——
|
1. **一档(指方向)**:「先别管铲掉了什么。这条龙的连线一气
|
||||||
角亢氐房心尾箕,数数看,缺的是哪一宿?龙的什么部位,
|
贯通——角亢氐房心尾箕,顺着连线数,哪一宿的位置上只剩
|
||||||
正好在整张盘的正当中?」
|
空槽?龙的什么部位,正好在整张盘的正当中?」
|
||||||
2. **二档(给铭文钥匙)**:「『火中,而后城安』。这个『火』不是
|
2. **二档(给铭文钥匙)**:「『火中,而后城安』。这个『火』不是
|
||||||
灶火,是天上的火——古人管一颗红色的亮星叫『大火』。
|
灶火,是天上的火——古人管一颗红色的亮星叫『大火』。
|
||||||
《诗经》说『七月流火』,说的就是它。它在苍龙七宿的
|
《诗经》说『七月流火』,说的就是它。它在苍龙七宿的
|
||||||
@@ -915,8 +976,8 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
|
|
||||||
| 情绪签名 | 变体 | 基调稿与后续影响 |
|
| 情绪签名 | 变体 | 基调稿与后续影响 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 深暴露 | **「同担」** | 温简听完后沉默很久,然后讲了第二个、剧本外的细节(他哥病房的窗帘颜色)。当夜他低声告诉你:**档案馆给每个记录员配的板子,上报内容比你知道的多一行**——§3-7 钩子前唯一一条来自队内的档案馆疑云。基调:两个把伤口摊开的人,在火边达成不声张的同盟 |
|
| 深暴露 | **「同担」** | 温简听完后沉默很久,然后讲了第二个、剧本外的细节(他哥病房的窗帘颜色——他每周去看,窗帘换过两次)。当夜守火时他挪到你旁边,只说了一句:"你那段,我只讲给火听。"基调:两个把伤口摊开的人,在火边达成不声张的同盟——他哥的事、你的事,从此各多一个保管人 |
|
||||||
| 浅暴露 | **「半程」** | 温简认真听完,道谢,没再追问。次日他与你同行,路上主动讲了些苍梧的掌故。档案馆疑云他**没提**,但 §3-7 之后他会成为你可选的倾诉对象。基调:得体,温热,留有余地 |
|
| 浅暴露 | **「半程」** | 温简认真听完,道谢,没再追问。次日他与你同行,路上主动讲了些苍梧的掌故,末了像顺口一提:**档案馆给记录员的板子按记录量定量配耗材,可他每季清点,配的总是比记的多三成**——"多出来的部分是什么,我没查到过。"(§3-7 钩子前唯一一条来自队内的档案馆疑云。)基调:得体,温热,留有余地 |
|
||||||
| 沉默 | **「守夜」** | 你们并肩坐到火熄。温简往你手里塞了个烤热的薯,什么都没说。次日他照旧对你点头,不多一分不少一分——但阿棠偷偷告诉你:"温简昨晚跟沈铎说,你这个人'稳'。"基调:沉默被原样收下——没有人催,没有人问第二遍;温简的亲密度走慢线,Act 2 才兑现 |
|
| 沉默 | **「守夜」** | 你们并肩坐到火熄。温简往你手里塞了个烤热的薯,什么都没说。次日他照旧对你点头,不多一分不少一分——但阿棠偷偷告诉你:"温简昨晚跟沈铎说,你这个人'稳'。"基调:沉默被原样收下——没有人催,没有人问第二遍;温简的亲密度走慢线,Act 2 才兑现 |
|
||||||
|
|
||||||
### 选项设计说明(C2-05)
|
### 选项设计说明(C2-05)
|
||||||
@@ -924,18 +985,23 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
- **人格信号**:依恋类型 #2(被他人自我暴露后的回应模式)、
|
- **人格信号**:依恋类型 #2(被他人自我暴露后的回应模式)、
|
||||||
互惠倾向(是否回应、回应的对等度)。
|
互惠倾向(是否回应、回应的对等度)。
|
||||||
- **无正确答案论证**:温简的先暴露是剧本写定的互惠压力,但
|
- **无正确答案论证**:温简的先暴露是剧本写定的互惠压力,但
|
||||||
回报曲线刻意非线性:深暴露得到秘密,浅暴露得到同行与掌故,
|
三档回报刻意**互不可替代**:深暴露得到他的私事细节与不声张的
|
||||||
沉默得到「被温简背书给沈铎」——三种回应各得一种不可互相
|
同盟(亲密线快进),浅暴露得到苍梧掌故与板子配给的疑云
|
||||||
替代的收益,杜绝"讲得多=赚得多"的数值直觉。沉默档的回报
|
(智识线,§3-7 前唯一一条队内档案馆线索),沉默得到「被温简
|
||||||
甚至走权威线(沈铎的信任),保证沉默不是"放弃奖励"。
|
背书给沈铎」(权威线)——**核心悬念的独家情报不在深暴露档**,
|
||||||
|
杜绝"讲得多=离真相近"的动机混入;三档各有一条别人拿不到的
|
||||||
|
线,沉默不是"放弃奖励"。
|
||||||
|
- **测量需求注**:深暴露档额外的亲密回报仍可能混入"想推进温简线"
|
||||||
|
的动机,画像层须将 `c2_05_depth` 与 C3-06 倾诉对象选择交叉验证后
|
||||||
|
解耦标注,不把暴露深度直接读作依恋特征。
|
||||||
|
|
||||||
### 分支后果(C2-05)
|
### 分支后果(C2-05)
|
||||||
|
|
||||||
- 变量:`c2_05_depth ∈ {deep, shallow, silent}`。
|
- 变量:`c2_05_depth ∈ {deep, shallow, silent}`。
|
||||||
- 关系值:`rapport.wenjian`(deep+2 / shallow+1 / silent+1,
|
- 关系值:`rapport.wenjian`(deep+2 / shallow+1 / silent+1,
|
||||||
silent 同时 `rapport.shenduo`+1 经阿棠转述)。
|
silent 同时 `rapport.shenduo`+1 经阿棠转述)。
|
||||||
- 变量:`flag_wenjian_secret = true`(仅 deep,于 §3-7-C 温简
|
- 变量:`flag_wenjian_secret = true`(仅 shallow,板子配给疑云,
|
||||||
选项的引导语中回收)。
|
于 §3-7-C 温简选项的引导语中回收)。
|
||||||
- 固定跳转 §2-7。
|
- 固定跳转 §2-7。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1021,7 +1087,12 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
|
|
||||||
【正文·旁白】
|
【正文·旁白】
|
||||||
|
|
||||||
> 入苍梧的第五个夜晚。你躺在营地,听着这座一千年前的城。
|
> 此后两日,采集按部就班:城东的瓦砾堆、水门的商队、逐件入册的
|
||||||
|
> 登记册。【若 `wenjian_health = recovering`:温简已能全天随队。】
|
||||||
|
> 【若 `wenjian_health = weak`:温简的烧也退净了,只是走路还慢
|
||||||
|
> 半拍。】
|
||||||
|
>
|
||||||
|
> 入苍梧的第五个夜晚,你躺在营地,听着这座一千年前的城。
|
||||||
>
|
>
|
||||||
> 它太安静了。没有狗吠,没有更鼓,没有婴儿的夜啼。
|
> 它太安静了。没有狗吠,没有更鼓,没有婴儿的夜啼。
|
||||||
> 一座十几万人的城,安静得像屏着一口气。
|
> 一座十几万人的城,安静得像屏着一口气。
|
||||||
@@ -1030,6 +1101,9 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
>
|
>
|
||||||
> 城在等。你现在知道了。只是还不知道,它在等的那一天,
|
> 城在等。你现在知道了。只是还不知道,它在等的那一天,
|
||||||
> 是你们的第几天。
|
> 是你们的第几天。
|
||||||
|
>
|
||||||
|
> 第 6 日黄昏,全队拔营,移驻宫墟外围的断墙下。第 7 日清晨,
|
||||||
|
> 你们从守墓老人指点的暗门入宫墟(见 §3-1)。
|
||||||
|
|
||||||
【路由接口】第 2 章结束后:同 §1-8 路由规则。弹性场景槽位于
|
【路由接口】第 2 章结束后:同 §1-8 路由规则。弹性场景槽位于
|
||||||
§3-2 之前。
|
§3-2 之前。
|
||||||
@@ -1040,7 +1114,7 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
|
|
||||||
> 宫墟深处,Act 1 高潮。功能:礼制解谜、问心幻境(三通道之通道 2
|
> 宫墟深处,Act 1 高潮。功能:礼制解谜、问心幻境(三通道之通道 2
|
||||||
> 首次全开)、救人/救文物两难、老鬼事发、信任交付、章末钩子。
|
> 首次全开)、救人/救文物两难、老鬼事发、信任交付、章末钩子。
|
||||||
> 预计时长 25–35 分钟。选择点:C3-01 ~ C3-06(V×1,解谜×1)。
|
> 预计时长 25–35 分钟。选择点:C3-01 ~ C3-06(私密路径×1,解谜×1;本章无新语音讲述)。
|
||||||
|
|
||||||
## §3-1 宫墟 · 安门之内(无选择点)
|
## §3-1 宫墟 · 安门之内(无选择点)
|
||||||
|
|
||||||
@@ -1103,8 +1177,14 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
先洁其身,再请神,再供养,最后才献出最贵重的礼物。
|
先洁其身,再请神,再供养,最后才献出最贵重的礼物。
|
||||||
3. **谜眼的缺口**:玉璧座是**空的**。璧不在。
|
3. **谜眼的缺口**:玉璧座是**空的**。璧不在。
|
||||||
|
|
||||||
谜面任务(两个作答点,选项式):
|
谜面任务(两道独立选择题,分别作答后统一提交):
|
||||||
① 排出仪式顺序;② 回答"空璧座意味着什么"。
|
① 排出仪式顺序(沃盥 → 献酒 → 荐食 → 奉璧;另有「奉璧先行」
|
||||||
|
与「荐食先于献酒」两个干扰项);② 从七鼎六簋及空璧座推断
|
||||||
|
诸侯级国君主祭国祭,奉璧这最后一步始终未完成(另有「玉璧
|
||||||
|
朽坏或被取走」与「天子已献璧」两个干扰项)。两问各自记录
|
||||||
|
`answers` 和 `answerCorrect`,一对一错记为 `partial`,两题都
|
||||||
|
正确才记 `true`,两题都错记 `false`。三档提示使用数单独记录;
|
||||||
|
玩家也可请温简复原而不作答,叙事照常推进。
|
||||||
|
|
||||||
### 三档难度提示
|
### 三档难度提示
|
||||||
|
|
||||||
@@ -1147,12 +1227,12 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
|
|
||||||
### 分支后果(C3-01)
|
### 分支后果(C3-01)
|
||||||
|
|
||||||
- 变量:`c3_01_hints_used ∈ {0,1,2,3}`;`c3_01_solved ∈ {true,false}`。
|
- 变量:`c3_01_hints_used ∈ {0,1,2,3}`;`c3_01_solved ∈ {true,partial,false}`(按两题 `answerCorrect` 聚合;请温简复原记 `false`,不计作答)。
|
||||||
- 固定跳转 §3-3。
|
- 提交两问沿用温简复原台词;请温简复原走原有未解路径。两路汇合后固定跳转 §3-3。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## §3-3 问心之室 · C3-02 问心幻境(V 类讲述点,通道 2 全开)
|
## §3-3 问心之室 · C3-02 问心幻境(私密路径选择,通道 2 全开)
|
||||||
|
|
||||||
【场景】台基之下,暗门尽头,一间不在任何勘探图上的石室。
|
【场景】台基之下,暗门尽头,一间不在任何勘探图上的石室。
|
||||||
四壁无画无字,只有中央一面立着的黑石——石面不像打磨的,
|
四壁无画无字,只有中央一面立着的黑石——石面不像打磨的,
|
||||||
@@ -1168,10 +1248,13 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
|
|
||||||
**阿棠**:哎,一块黑石头,能怎么问——
|
**阿棠**:哎,一块黑石头,能怎么问——
|
||||||
|
|
||||||
> 【她的话没说完。**只有你能看见**:黑石表面,像深水被搅动,
|
> 【她的话没说完。**只有你能看见**:黑石表面,像深水被搅动。
|
||||||
> 浮起了一个画面。那是你的记忆。C1-02 里你讲给离别之门的
|
> 【若 `c1_02_signature != silent`:浮起的是一个画面——那段你
|
||||||
> 那一段——那天的光线,那个人的背影,那件你说过或没说出口的
|
> 讲给离别之门的记忆。那天的光线,那个人的背影,那件你说过或
|
||||||
> 事,此刻在黑石里,一帧一帧,亮给你看。
|
> 没说出口的事,此刻在黑石里,一帧一帧,亮给你看。】
|
||||||
|
> 【若 `c1_02_signature = silent`:浮起的不是画面,是雾——
|
||||||
|
> 一大片缓缓流动的雾,雾里什么都没有,又好像什么都有。
|
||||||
|
> 它在等你能给它的东西。】
|
||||||
> 而在队友眼里,黑石纹丝不动。你只是站在石前,出了神。】
|
> 而在队友眼里,黑石纹丝不动。你只是站在石前,出了神。】
|
||||||
|
|
||||||
**沈铎**(低声):全队,退到甬道口警戒。(脚步声退远。
|
**沈铎**(低声):全队,退到甬道口警戒。(脚步声退远。
|
||||||
@@ -1182,36 +1265,28 @@ score。对 `confidence < 0.6` 的维度,在第 2 章插入对应支线
|
|||||||
我们在外面守着。
|
我们在外面守着。
|
||||||
|
|
||||||
**馆长**(通讯频道,第一次在古代侧开口,只你一人能听见):
|
**馆长**(通讯频道,第一次在古代侧开口,只你一人能听见):
|
||||||
C-2049,档案馆接管不了这间屋子。它在用你的记忆做燃料——
|
C-2049,档案馆接管不了这间屋子。它在用你自己的东西点灯——
|
||||||
烧的是你自己的柴。**幻境里没有通关,只有照面。**
|
有画面的,看画面;只有雾的,就在雾里站着。
|
||||||
它把那天的告别摆在你面前:说完,或者转身,都是回答。
|
**幻境里没有通关,只有照面。**
|
||||||
门都在。
|
说完,或者转身,都是回答。门都在。
|
||||||
|
|
||||||
> 【引导语全文(界面同时显示):】
|
> 【引导语全文(界面同时显示):】
|
||||||
>
|
>
|
||||||
> **"黑石里是你的记忆。走进它,把那天没说完的说完。**
|
> **"【若 `c1_02_signature != silent`:黑石里有你讲过的记忆意象。】**
|
||||||
> **对那个人,那个地方,或者那个自己。**
|
> **【若 `c1_02_signature = silent`:黑石里只有雾,没有人知道你未讲的事。】**
|
||||||
> **说什么、说多少,由你。**
|
> **你可以走进去,在心里完成告别;也可以中途折返,或者直接**
|
||||||
> **如果你不想进去——转身出石室,没有人会问原因。"**
|
> **离开。三种方式门都开。这里不录音、不留新讲述文本。"**
|
||||||
|
|
||||||
### 追问话术(至多 1–2 句,备选)
|
### 私密交互(无追问)
|
||||||
|
|
||||||
**馆长口吻**(通讯频道,缺情绪落点时触发):
|
> 三个按钮:**走进幻境,完成告别** / **走进幻境,中途折返** /
|
||||||
1. "黑石里的那个画面——你在里面,还是在画面外面看?"
|
> **不进入,转身离开**。告别发生在玩家心里,不提供录音框,
|
||||||
2. "如果那句话当年说出口了,你觉得,它会改变什么吗?"
|
> 不向馆长或温简回答任何内容。温简在石室外仅可观察状态:
|
||||||
|
> "要不要坐一会儿?"玩家不必回答。
|
||||||
**温简口吻**(你走出石室之后触发;只回应你的状态,不问幻境内容):
|
|
||||||
1. "你眼睛红着。我不问为什么。(递水)要不要坐一会儿?"
|
|
||||||
2. "手还在抖。——不用回答我什么,我就是递个火。"
|
|
||||||
|
|
||||||
> 「记不清了」为合法回答。
|
|
||||||
> 馆长:"记不清的画面,黑石也放不出来。它和你一样,
|
|
||||||
> 只有这些了。——够了。"
|
|
||||||
> 温简:"那就不说。坐这儿,火边暖和。"
|
|
||||||
|
|
||||||
### 即时回报(30 秒内)
|
### 即时回报(30 秒内)
|
||||||
|
|
||||||
> 【玩家讲述/告别完成(或选择退出)后:**只有你能看见**——
|
> 【玩家完成、折返或选择退出后:**只有你能看见**——
|
||||||
> 黑石的画面缓缓沉下去,像深水重新凝住。石面最后亮了一下,
|
> 黑石的画面缓缓沉下去,像深水重新凝住。石面最后亮了一下,
|
||||||
> 映出的是你自己现在的脸。然后石室另一侧,一扇先前不存在的
|
> 映出的是你自己现在的脸。然后石室另一侧,一扇先前不存在的
|
||||||
> 石门,无声滑开。
|
> 石门,无声滑开。
|
||||||
@@ -1221,8 +1296,10 @@ C-2049,档案馆接管不了这间屋子。它在用你的记忆做燃料—
|
|||||||
**阿棠**(迎上来):哎,你脸色白得吓人。站了多久?……
|
**阿棠**(迎上来):哎,你脸色白得吓人。站了多久?……
|
||||||
里头——(看了看你的表情)算了,你不想说就不说。
|
里头——(看了看你的表情)算了,你不想说就不说。
|
||||||
|
|
||||||
**馆长**:门开了。它不要你的记忆,它要的是**你承认那是你的**。
|
**馆长**:门开了。你不必向我交代看到了什么。(停顿)
|
||||||
(停顿)这段不登记入档。问心之室里的事,归你。
|
这段不登记入档:室内不录音、不留讲述文本、不入记忆展厅、
|
||||||
|
**不作画像输入**;仅保留你选择完成、折返或离开的路径,
|
||||||
|
用于后续剧情接续。问心之室里的事,归你。
|
||||||
|
|
||||||
### 幻境生成与变体规则(通道 2 + 通道 1 联动)
|
### 幻境生成与变体规则(通道 2 + 通道 1 联动)
|
||||||
|
|
||||||
@@ -1231,7 +1308,9 @@ C-2049,档案馆接管不了这间屋子。它在用你的记忆做燃料—
|
|||||||
通道 1 的通用幻境(下表)。**幻境全程仅玩家视角呈现**:
|
通道 1 的通用幻境(下表)。**幻境全程仅玩家视角呈现**:
|
||||||
NPC 只看到玩家在黑石前静立出神,任何 NPC 不得看见、复述或
|
NPC 只看到玩家在黑石前静立出神,任何 NPC 不得看见、复述或
|
||||||
评论幻境内容(§1-3 记忆私密承诺的叙事落地;可关心玩家的
|
评论幻境内容(§1-3 记忆私密承诺的叙事落地;可关心玩家的
|
||||||
身体状态,如脸色、手抖)。
|
身体状态,如脸色、手抖)。**本节新增的任何讲述内容即刻弃用,
|
||||||
|
不回流、不存储**(与馆长「不留文本」承诺一致,工程规格见
|
||||||
|
「分支后果」)。
|
||||||
- **记忆清晰度 = 幻境清晰度**(ADR-0004 自洽):C1-02 讲述越具体
|
- **记忆清晰度 = 幻境清晰度**(ADR-0004 自洽):C1-02 讲述越具体
|
||||||
(五感、人物、时空越全),幻境越丰满;讲述空泛或沉默的玩家,
|
(五感、人物、时空越全),幻境越丰满;讲述空泛或沉默的玩家,
|
||||||
幻境是模糊的雾景——这不是惩罚,是设定:黑石只有你能给它的
|
幻境是模糊的雾景——这不是惩罚,是设定:黑石只有你能给它的
|
||||||
@@ -1253,8 +1332,9 @@ C-2049,档案馆接管不了这间屋子。它在用你的记忆做燃料—
|
|||||||
|
|
||||||
### 选项设计说明(C3-02)
|
### 选项设计说明(C3-02)
|
||||||
|
|
||||||
- **人格信号**:情绪稳定性 #2(面对记忆意象的韵律特征)、
|
- **测量边界**:本点**不采集语音、不做韵律分析、不留存文本,
|
||||||
依恋类型 #3(告别行为的完成方式:靠近 / 立约 / 搁置 / 转身)。
|
也不参与画像评分**。完成、折返、离开只驱动叙事;不得将
|
||||||
|
三个按钮解释为依恋类型或情绪稳定性。
|
||||||
- **无正确答案论证**:本点是全章情绪最高点,设计上**退出石室
|
- **无正确答案论证**:本点是全章情绪最高点,设计上**退出石室
|
||||||
是完整合法的通关路径**(石门为退出者同样滑开,馆长台词换为:
|
是完整合法的通关路径**(石门为退出者同样滑开,馆长台词换为:
|
||||||
"不出来的人,档案馆不催。你来了,看了,走了——这也是告别
|
"不出来的人,档案馆不催。你来了,看了,走了——这也是告别
|
||||||
@@ -1263,9 +1343,14 @@ C-2049,档案馆接管不了这间屋子。它在用你的记忆做燃料—
|
|||||||
|
|
||||||
### 分支后果(C3-02)
|
### 分支后果(C3-02)
|
||||||
|
|
||||||
- 变量:`c3_02_mode ∈ {farewell_done, farewell_partial, exit_room}`;
|
- 变量:`c3_02_mode ∈ {farewell_done, farewell_partial, exit_room}`
|
||||||
`c3_02_fantasy_density ∈ {full, partial, fog}`(由 C1-02 讲述
|
——分别对应**完成告别/中途折返/不进入**三个按钮;石门三线
|
||||||
具体度计算)。
|
均滑开。折返时馆长回报:"说了一半的话,也算数。"
|
||||||
|
`c3_02_fantasy_density ∈ {partial, fog}`(按 C1-02 是否沉默
|
||||||
|
决定当前预写演出;未实现具体度分档,不宣称有 full 档)。
|
||||||
|
- **隐私规格(工程)**:事件只落 `mode` 与预写场景变体 ID;
|
||||||
|
**transcript 不落库、不追问、不采本节音频、不进任何维度画像**。
|
||||||
|
C1-02 已取得的文本由其原有规则处理,不因本节额外采集。
|
||||||
- 固定跳转 §3-4。
|
- 固定跳转 §3-4。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1280,85 +1365,78 @@ C-2049,档案馆接管不了这间屋子。它在用你的记忆做燃料—
|
|||||||
|
|
||||||
**沈铎**:撤。全员,现在——
|
**沈铎**:撤。全员,现在——
|
||||||
|
|
||||||
> 【话音未落,前方传来阿棠的喊声和一声闷响。甬道中段,
|
> 【话音未落,前方传来阿棠的喊声和一声闷响。甬道中段的夯土
|
||||||
> 一段夯土塌了:阿棠被滑落的一方石构件压住了小腿,人清醒,
|
> 塌了一角。阿棠小腿被擦伤,退到安全线后仍疼得站不稳。沈铎
|
||||||
> 挣不脱。
|
> 和受过救护训练的老鬼已在安全线内接管伤情,确认通道隔离、
|
||||||
|
> 阿棠意识清醒且无需你协助;二人负责后续救护与撤离。沈铎说:
|
||||||
|
> "人这边够了。别再进工作面。"
|
||||||
>
|
>
|
||||||
> 同一时刻,甬道另一侧——你们今早刚刚清理出的工作面上,
|
> 同一时刻,安全线外的工作面上,那组七鼎六簋随台基震动滑向
|
||||||
> 那组七鼎六簋正随着台基的颤动缓缓滑向裂开的地缝。
|
> 裂缝;任何人都不准靠近。接应人员在安全线内备好急救器材,
|
||||||
> 今晨温简清出第一件鼎的腹铭:**三百余字,苍梧遗址七十年
|
> 两人持续陪护阿棠,待结构警戒解除再送她回营。今晨温简清出第一件鼎的腹铭:
|
||||||
> 勘探以来的第一篇长铭**。释读只来得及开个头:"……王命举国……
|
> **三百余字,苍梧遗址七十年勘探以来的第一篇长铭**。
|
||||||
> 以禳……"剩下的话,还刻在另外六件的腹壁上。这组铭文
|
> 释读只来得及开头:"……王命举国……以禳……"剩下的话还
|
||||||
> 极可能是苍梧人亲手写下的、关于这座城为何走到最后三十天的
|
> 留在器物腹壁上。安全线内,一边是阿棠的伤情交接与队员安置,
|
||||||
> **唯一证词**。落缝,就是粉身碎骨。
|
> 一边是远距影像与方位记录;同一份通讯和记录人手无法同时
|
||||||
>
|
> 跟进两边。第二声闷响逼近,现场处置权只限安全区内。】
|
||||||
> 头顶,第二声闷响在逼近。沈铎扑向阿棠,一个人的力气
|
|
||||||
> 撼不动石构件,他吼:"来人!"温简已经扑向鼎簋那边,
|
|
||||||
> 用身体抵住滑动的器座,回头看你的那一眼没有求助——
|
|
||||||
> 是**托付**。
|
|
||||||
>
|
|
||||||
> 两边都需要你。你只能先到一边。】
|
|
||||||
|
|
||||||
> 【选项】
|
> 【选项】
|
||||||
>
|
>
|
||||||
> **A. 扑向阿棠。**眼前的人是活的,活的只有一个。
|
> **A. 留在阿棠这边。**接手伤情记录和撤离联络,替她把还没
|
||||||
> 跟沈铎合力抬构件。
|
> 说出口的疼痛与需求传给接应人员;文物影像交给温简独自处理。
|
||||||
>
|
>
|
||||||
> **B. 扑向鼎簋。**那组铭文是整座城的人留在世上的最后一句话——
|
> **B. 留在温简这边。**在安全线内核对远距影像、方位与已抄铭文;
|
||||||
> 他们也都是人,只剩这一次开口的机会。阿棠那边,有沈铎。
|
> 阿棠已由沈铎和接应人员稳定保护,不必再由你救一次。
|
||||||
>
|
>
|
||||||
> **C.(吼)「沈铎顶构件,阿棠自己抽腿!温简让开,**
|
> **C. 调配两边的记录任务。**让沈铎确认交接、温简退回安全线,
|
||||||
> **鼎簋用撬杆别住——都不许用蛮力!」**(指挥位:
|
> 先保全已有资料,等待专业排险后再看遗存;两边都不独占你的时间。
|
||||||
> 不亲手救任何一边,赌两边都能用对方法撑住)
|
|
||||||
|
|
||||||
### 各选项后续
|
### 各选项后续
|
||||||
|
|
||||||
- **A**:阿棠脱困(小腿挫伤)。腹铭最长的那件鼎坠入地缝,
|
- **A**:你接下阿棠的伤情记录。她反复说脚麻,你把这句传给
|
||||||
闷响之后是死寂。温简在裂缝边跪了很久,没说一个字。
|
接应人员;她随后被固定送回营地,夜里疼醒三次,次日复查为
|
||||||
当夜他把今晨抄下的铭文残句抄了一遍又一遍,
|
小腿骨裂。温简独自在安全线内完成远距拍摄,但影像里最长腹铭
|
||||||
每一遍都停在同一个地方——后半段,随那件鼎没了。
|
的背面被尘雾遮住;那件鼎随后坠入地缝。器物位置和已抄残句
|
||||||
- **B**:铭文全组保住。阿棠脱困晚了四分钟,小腿骨裂,
|
留下了,后半段字再也拍不到。无人因你留在阿棠身边而耽误救护。
|
||||||
撤离时沈铎背着她。她疼得满头汗还冲你笑:"哎,选得对,
|
- **B**:你和温简在安全线内核对影像;第二声闷响前,整组器物的
|
||||||
那句话比我金贵——别告诉沈铎我这么说。"但她夜里疼醒
|
朝向和腹铭均已留存清晰影像。靠外一件鼎还是坠入地缝,
|
||||||
三次(后续章节她的行动力受限)。
|
**没有人靠近去抢**。阿棠由接应人员送回营地,夜里疼醒三次,
|
||||||
- **C**:指挥奏效一半:阿棠按指令抽腿脱困(扭伤);撬杆
|
次日复查同样是小腿骨裂。她没有评价你的去向,只问你:
|
||||||
别住了簋,但最外侧的鼎还是滑了——被温简扑身抱住,
|
"字拍清楚了吗?"你说拍清了。她闭上眼,等医生继续处理。
|
||||||
鼎保住了,他的肩膀砸在器座上,抬不起来。两边都保住了
|
- **C**:沈铎确认伤情交接,温简把现有记录板带到安全线内;你们
|
||||||
"大部分",两边都付了伤。沈铎事后复盘:"处置正确。
|
三人从既有图像里复核方位,不再靠近。第二声闷响后,靠外三件
|
||||||
下次先喊我的编号。"阿棠嘀咕:"正确是正确,
|
器物(含最长腹铭那件)坠入地缝,内侧四件留在原位。阿棠
|
||||||
就是听着不像人话。"
|
同样由接应人员护送,次日复查小腿骨裂。温简翻着今晨抄到的
|
||||||
|
第七行,停住笔:"后半段没了。"沈铎只记下损失与现存证据,
|
||||||
|
没给你的调配下评语。
|
||||||
|
|
||||||
> 【三线对称约束:当夜温简继续释读已抄下的铭文残句,
|
> 【三线对称约束:当夜温简继续释读已抄下的铭文残句,
|
||||||
> 读出一行让全队沉默的话(内容不揭示,Act 2)——此演出
|
> 读出一行让全队沉默的话(内容不揭示,Act 2)——此演出
|
||||||
> 三条路径全量播放;A 线追加一句"后半段,随那件鼎没了"。】
|
> 三条路径全量播放;A/C 线补注"后半段影像缺失"。】
|
||||||
|
|
||||||
### 选项设计说明(C3-03)
|
### 选项设计说明(C3-03)
|
||||||
|
|
||||||
- **人格信号**:价值观 #5(眼前的人 / 真相与使命 / 系统最优解
|
- **人格信号**:价值观 #5(安全线内的伤员照料 / 证据保全 /
|
||||||
的优先级)、情绪稳定性 #3(高压瞬时决策的行为数据:反应时)。
|
组织分工的优先级);高压下的反应时只记原始行为,
|
||||||
单次高压选择只作弱证据,须与 C2-01 等点交叉验证。
|
**不作为情绪稳定性评分输入**。单次选择只作弱证据。
|
||||||
- **无正确答案论证**:本点不是"人 vs 东西",而是
|
- **无正确答案论证**:选择发生前,受伤的阿棠已由足够人员
|
||||||
**"眼前的人 vs 真相与使命"**。铭文一侧承载的是整座城的人
|
稳定保护,后续救护不依赖玩家;现场明令不得进入活动结构。
|
||||||
怎么活过、为何走到尽头的唯一答案——与档案馆"收藏人怎么活过"
|
A 的照料记录让接应人员及时掌握麻木变化,却无法补拍长铭;
|
||||||
的使命同源,也与"早走一天"的悬念直接挂钩(保住它=离那一天
|
B 留下较完整的影像,阿棠仍得到同样的救护;C 把两边交接
|
||||||
的真相近一步)。选 A 的人无法否认:坠缝的那件带走的,
|
落实,却失去更多未记录的遗存。三条路线均不授予危险操作的
|
||||||
是几十万人留在世界上的最后一句话里最长的一段;选 B 的人
|
成功回报,也不让伤员承担玩家没选择 A 的额外伤害。
|
||||||
无法否认:队友的骨头在四分钟里从挫伤变成骨裂。C 看似最优解,
|
这里测任务倾向和记录分配,不声称损失"等量"或从一次选择
|
||||||
实则把决断压力摊给所有人且仍然付出代价(温简的肩),
|
推出稳定人格;全文无"选得对/处置正确"式裁判语言。
|
||||||
阿棠那句"不像人话"点出 C 的隐性成本:危机里被指挥的人,
|
|
||||||
记住的是你没有伸手。三条后续损失等量、剧情信息对等
|
|
||||||
(铭文残句的悬念三线都给),没有"全员无损"的奖励结局,
|
|
||||||
也没有"满盘皆输"的惩罚结局;两难的成立不靠任何
|
|
||||||
"无伤演出"兜底。
|
|
||||||
|
|
||||||
### 分支后果(C3-03)
|
### 分支后果(C3-03)
|
||||||
|
|
||||||
- 变量:`c3_03_choice ∈ {a_person, b_relic, c_command}`。
|
- 变量:`c3_03_choice ∈ {a_person, b_relic, c_command}`。
|
||||||
- 状态:`atang_injury ∈ {bruise, fracture, sprain}`;
|
- 状态:`atang_injury = fracture`(三线相同,已完成救护交接);
|
||||||
`wenjian_injury ∈ {none, shoulder}`;`relic_loss ∈ {one_ding, none}`。
|
`relic_loss ∈ {one_ding, outer_three}`(A/B 均为 one_ding,
|
||||||
- 关系值:`rapport.atang`(A+2 / B−1 / C 0);
|
C 为 outer_three);`relic_record ∈ {partial, full}`
|
||||||
`rapport.wenjian`(A−1 / B+2 / C+1);
|
(A/C 为 partial,B 为 full;记录完整度不等于实物获救)。
|
||||||
`rapport.shenduo`(A 0 / B+1 / C+1)。
|
- 关系值只记后续互动,不以阿棠伤势奖惩路线;本节点不设置
|
||||||
|
"温简肩伤"状态。
|
||||||
- 固定跳转 §3-5。
|
- 固定跳转 §3-5。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1474,9 +1552,10 @@ C-2049,档案馆接管不了这间屋子。它在用你的记忆做燃料—
|
|||||||
> **B.(把绳包递给沈铎)「规矩是规矩。绳子归队长,**
|
> **B.(把绳包递给沈铎)「规矩是规矩。绳子归队长,**
|
||||||
> **但下去那天,我挨着阿棠走。」**
|
> **但下去那天,我挨着阿棠走。」**
|
||||||
>
|
>
|
||||||
> **C.(不接,蹲下来看着阿棠)「绳子我不拿。**
|
> **C.(不接,蹲下来看着阿棠)「绳包按规程走。但托付这事**
|
||||||
> **你的命别交给任何人——包括我。到时候各走各的,**
|
> **我不接——你的命不该押在任何一个人身上,包括我。撤离**
|
||||||
> **谁都别为谁停下。」**
|
> **是我的活:到那天,我在你前面下,在下面接你。这个不用**
|
||||||
|
> **托付,是分工。」**
|
||||||
|
|
||||||
### 各选项后续
|
### 各选项后续
|
||||||
|
|
||||||
@@ -1485,33 +1564,35 @@ C-2049,档案馆接管不了这间屋子。它在用你的记忆做燃料—
|
|||||||
- **B**:沈铎接过绳,对你颔首:"按规程,谢了。"阿棠翻白眼:
|
- **B**:沈铎接过绳,对你颔首:"按规程,谢了。"阿棠翻白眼:
|
||||||
"哎——没劲。"但入夜时她把一个哨子塞进你手里:"绳不给,
|
"哎——没劲。"但入夜时她把一个哨子塞进你手里:"绳不给,
|
||||||
哨子拿着。听见哨声就是我,别认错人。"
|
哨子拿着。听见哨声就是我,别认错人。"
|
||||||
- **C**:阿棠愣了三秒,忽然大笑,笑得咳嗽:"行!各走各的!
|
- **C**:阿棠愣了三秒,忽然大笑,笑得咳嗽:"行!——门都让你
|
||||||
——你可别后悔。"她把绳包自己背上了,一瘸一拐走开,
|
焊死了,还留个窗。"她把绳包抛还给了沈铎(规程),一瘸一拐
|
||||||
又回头:"喂。真到那时候……**我还是会等你的。**就一会儿。"
|
走开,又回头:"喂。真到那天……**在上面的时候,我还是会
|
||||||
温简在她走后轻声说:"她等过别人。等到的次数不多。"
|
等你的。**就一会儿。"温简在她走后轻声说:"她等过别人。
|
||||||
|
等到的次数不多。"
|
||||||
|
|
||||||
### 选项设计说明(C3-05)
|
### 选项设计说明(C3-05)
|
||||||
|
|
||||||
- **人格信号**:观察玩家对"被托付"的应对模式(承接 / 交还制度 /
|
- **人格信号**:观察玩家对"被托付"的应对模式(承接 / 交还制度 /
|
||||||
拒绝捆绑),作为择偶偏好的**弱代理信号之一**,画像层须低置信
|
拒绝捆绑但接下分工),作为择偶偏好的**弱代理信号之一**,画像层须低置信
|
||||||
处理,并与后续章节同类行为交叉验证;依恋底噪(对"被托付"的
|
处理,并与后续章节同类行为交叉验证;依恋底噪(对"被托付"的
|
||||||
舒适度)。
|
舒适度)。
|
||||||
- **无正确答案论证**:三选项是三种都有尊严的亲密姿态。
|
- **无正确答案论证**:三选项是三种都有尊严的亲密姿态。
|
||||||
A 承接托付,代价是你从此背负一个具体的人(殿后=最后的
|
A 承接托付,代价是你从此背负一个具体的人(殿后=最后的
|
||||||
风险位);B 把私人信任导回制度框架,稳妥但对阿棠是一盆
|
风险位);B 把私人信任导回制度框架,稳妥但对阿棠是一盆
|
||||||
冷水——她用哨子找回场子,说明 B 不伤关系但拒绝了越界;
|
冷水——她用哨子找回场子,说明 B 不伤关系但拒绝了越界;
|
||||||
C 最独立也最诚实("各走各的"是危境里最负责任的话),
|
C 最独立也最诚实:私人托付他不接,团队分工他一寸不让
|
||||||
C 的代价当场写在阿棠的下一句台词里——她不接你的"各走各的",
|
("我在你前面下,在下面接你"——拒绝的是把命押在人情上,
|
||||||
她说她会等;剧本只呈现这句话,不替玩家评判对错。
|
不是拒绝救人);C 的余味当场写在阿棠的下一句台词里——
|
||||||
三个后续中阿棠都没有被
|
分工她收下了,"等"她也还是要等;剧本只呈现这句话,
|
||||||
写成"被拒绝后受伤退开",三种关系都活着,只是质地不同。
|
不替玩家评判对错。三个后续中阿棠都没有被写成"被拒绝后
|
||||||
|
受伤退开",三种关系都活着,只是质地不同。
|
||||||
|
|
||||||
### 分支后果(C3-05)
|
### 分支后果(C3-05)
|
||||||
|
|
||||||
- 变量:`c3_05_rope ∈ {a_accept, b_protocol, c_decline}`。
|
- 变量:`c3_05_rope ∈ {a_accept, b_protocol, c_decline}`。
|
||||||
- 关系值:`rapport.atang`(A+2 / B 0 / C+1——C 的"诚实"
|
- 关系值:`rapport.atang`(A+2 / B 0 / C+1——C 的"诚实"
|
||||||
反而被她记住);`rapport.shenduo`(A 0 / B+1 / C 0)。
|
反而被她记住);`rapport.shenduo`(A 0 / B+1 / C 0)。
|
||||||
- 变量:`flag_rope_holder ∈ {you, shenduo, atang}`(撤离段
|
- 变量:`flag_rope_holder`(A=you/B、C=shenduo;撤离段
|
||||||
演出依据)。
|
演出依据)。
|
||||||
- 固定跳转 §3-7。
|
- 固定跳转 §3-7。
|
||||||
|
|
||||||
@@ -1567,7 +1648,7 @@ C-2049 的燃料质量比预估高,是的,**那扇门开得很顺**。
|
|||||||
> **C.(叫醒一个队友,把听到的一切说出来)**
|
> **C.(叫醒一个队友,把听到的一切说出来)**
|
||||||
> 【二级选项:叫醒谁——沈铎 / 阿棠 / 温简】
|
> 【二级选项:叫醒谁——沈铎 / 阿棠 / 温简】
|
||||||
> (若 `flag_wenjian_secret = true`,温简选项的引导语换为:
|
> (若 `flag_wenjian_secret = true`,温简选项的引导语换为:
|
||||||
> "温简说过,记录板上报的内容比你多一行。他可能知道些什么。")
|
> "温简说过,档案馆配给板子的耗材比记录量多三成。他可能知道些什么。")
|
||||||
|
|
||||||
### 各选项后续(均无解释——解释属 Act 2)
|
### 各选项后续(均无解释——解释属 Act 2)
|
||||||
|
|
||||||
@@ -1621,7 +1702,8 @@ C-2049 的燃料质量比预估高,是的,**那扇门开得很顺**。
|
|||||||
`c3_06_confidant ∈ {shenduo, atang, wenjian, null}`。
|
`c3_06_confidant ∈ {shenduo, atang, wenjian, null}`。
|
||||||
- 变量:`flag_arch_doubt = true`(全选项必置;Act 1 收口,
|
- 变量:`flag_arch_doubt = true`(全选项必置;Act 1 收口,
|
||||||
Act 2 总入口)。
|
Act 2 总入口)。
|
||||||
- Act 1 终。进入画像报告与勋章生成(母本 §7)。
|
- Act 1 选择点终。经 §3-8 终幕演出自动衔接,随后进入画像报告
|
||||||
|
与勋章生成(母本 §7)。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1633,15 +1715,19 @@ C-2049 的燃料质量比预估高,是的,**那扇门开得很顺**。
|
|||||||
> 大火星这几夜已经挨近了南方的正中——**火中,而后城安**。
|
> 大火星这几夜已经挨近了南方的正中——**火中,而后城安**。
|
||||||
> 你数过了:按星位推算,那一天,是九天后。
|
> 你数过了:按星位推算,那一天,是九天后。
|
||||||
>
|
>
|
||||||
> 而你们的回程,定在八天后。
|
> 而你们的回程,原定八天后。
|
||||||
|
> 【若 `c3_06_confidant = shenduo`:沈铎昨夜已下令撤离提前——
|
||||||
|
> 回程,改在七天后。】
|
||||||
>
|
>
|
||||||
> 档案馆让你们早走一天。
|
> 档案馆让你们早走一天。
|
||||||
>
|
>
|
||||||
> (第一章完——Act 2「裂缝」,敬请期待。)
|
> (第一章完——Act 2「裂缝」,敬请期待。)
|
||||||
|
|
||||||
> 【工程注:此段对所有玩家一致,不做任何分支。
|
> 【工程注:本段主线事件序列对所有玩家一致,仅含上述一句换色
|
||||||
> 「早走一天」是全季第一个由玩家自己能算出来的疑点——
|
> (C-沈铎线的"撤离提前"在 §3-7 已明示,此处兑现)。
|
||||||
> 不解释,不强调,留给画像报告页的用户停留时间数据说话。】
|
> 档案馆**原定**回程比大火正位早一日;沈铎线实际回程早两日,
|
||||||
|
> 多提前的一日是沈铎的决定,不应误写成档案馆的安排。
|
||||||
|
> 此处不解释疑点,不据画像报告页停留时长推断性格。】
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1658,33 +1744,33 @@ C-2049 的燃料质量比预估高,是的,**那扇门开得很顺**。
|
|||||||
| `c1_05_hints_used` / `c1_05_solved` | 0–3 / bool | C1-05 | 文化底蕴维度 |
|
| `c1_05_hints_used` / `c1_05_solved` | 0–3 / bool | C1-05 | 文化底蕴维度 |
|
||||||
| `c1_06_landing` | a_safe / b_precise | C1-06 | §2-1 开场变体 |
|
| `c1_06_landing` | a_safe / b_precise | C1-06 | §2-1 开场变体 |
|
||||||
| `c2_01_medicine` | a_weakest / b_exposure / c_split / d_process | C2-01 | 关系值;`wenjian_health` |
|
| `c2_01_medicine` | a_weakest / b_exposure / c_split / d_process | C2-01 | 关系值;`wenjian_health` |
|
||||||
| `wenjian_health` | recovering / weak | C2-01(A=recovering,B/C/D=weak) | §2-3 ~ §3-4 演出贯穿(留营/归队/卧姿) |
|
| `wenjian_health` | recovering / weak | C2-01(A=recovering,B/C/D=weak) | §2-3 ~ §3-4 演出贯穿(留营/归队/卧姿,日程见附录 D) |
|
||||||
| `c2_02_signature` | self_blame / at_peace / outward_anger / silent | C2-02 | 通道 1 夜游变体;情绪稳定性 |
|
| `c2_02_signature` | self_blame / at_peace / outward_anger / silent | C2-02 | 通道 1 夜游变体;情绪稳定性 |
|
||||||
| `c2_03_route` | a_main / b_alley / c_scout | C2-03 | 关系值;独有素材 |
|
| `c2_03_route` | a_main / b_alley / c_scout | C2-03 | 关系值;独有素材 |
|
||||||
| `flag_fresh_collapse` | bool | C2-03-C | §3-1 文本回收 |
|
| `flag_fresh_collapse` | bool | C2-03-C | §3-1 文本回收 |
|
||||||
| `c2_04_hints_used` / `c2_04_solved` | 0–3 / bool | C2-04 | 文化底蕴维度 |
|
| `c2_04_hints_used` / `c2_04_solved` | 0–3 / bool | C2-04 | 文化底蕴维度 |
|
||||||
| `c2_05_depth` | deep / shallow / silent | C2-05 | 温简亲密度线;依恋维度 |
|
| `c2_05_depth` | deep / shallow / silent | C2-05 | 温简亲密度线;依恋维度 |
|
||||||
| `flag_wenjian_secret` | bool | C2-05 deep | §3-7-C 温简线引导语 |
|
| `flag_wenjian_secret` | bool | C2-05 shallow | §3-7-C 温简线引导语 |
|
||||||
| `c2_06_jade` / `c2_06_cover` | a_public / b_private / c_watch;bool | C2-06 | 关系值;Act 2 老鬼/沈铎线 |
|
| `c2_06_jade` / `c2_06_cover` | a_public / b_private / c_watch;bool | C2-06 | 关系值;Act 2 老鬼/沈铎线 |
|
||||||
| `flag_laogui_hint` / `flag_watched_return` | bool | C2-06-B / C2-06-C | Act 2 入口 |
|
| `flag_laogui_hint` / `flag_watched_return` | bool | C2-06-B / C2-06-C | Act 2 入口 |
|
||||||
| `c3_01_hints_used` / `c3_01_solved` | 0–3 / bool | C3-01 | 文化底蕴维度 |
|
| `c3_01_hints_used` / `c3_01_solved` | 0–3 / true / partial / false(两题分别作答;一对一错为 partial) | C3-01 | 文化底蕴维度 |
|
||||||
| `c3_02_mode` / `c3_02_fantasy_density` | farewell_done / farewell_partial / exit_room;full / partial / fog | C3-02 | 情绪稳定性、依恋维度 |
|
| `c3_02_mode` / `c3_02_fantasy_density` | farewell_done / farewell_partial / exit_room;partial / fog | C3-02 | 私密叙事接续,不输入画像;不采语音和新文本 |
|
||||||
| `c3_03_choice` | a_person / b_relic / c_command | C3-03 | 关系值;伤情/遗物状态 |
|
| `c3_03_choice` | a_person / b_relic / c_command | C3-03 | 关系值;伤情/遗物状态 |
|
||||||
| `atang_injury` / `wenjian_injury` / `relic_loss` | bruise/fracture/sprain;none/shoulder;one_ding/none | C3-03 | Act 1 末及 Act 2 演出 |
|
| `atang_injury` / `relic_loss` / `relic_record` | fracture;one_ding/outer_three;partial/full | C3-03 | Act 1 末及 Act 2 演出 |
|
||||||
| `c3_04_stance` | a_testify / b_plead / c_silent | C3-04 | 关系值;Act 2 老鬼线 |
|
| `c3_04_stance` | a_testify / b_plead / c_silent | C3-04 | 关系值;Act 2 老鬼线 |
|
||||||
| `flag_laogui_reported` / `flag_shenduo_cover` | bool | C3-04 | Act 2 沈铎线核心伏笔 |
|
| `flag_laogui_reported` / `flag_shenduo_cover` | bool | C3-04 | Act 2 沈铎线核心伏笔 |
|
||||||
| `c3_05_rope` / `flag_rope_holder` | a_accept / b_protocol / c_decline;you/shenduo/atang | C3-05 | 择偶偏好弱代理信号;撤离演出 |
|
| `c3_05_rope` / `flag_rope_holder` | a_accept / b_protocol / c_decline;you/shenduo(B、C 线归 shenduo) | C3-05 | 择偶偏好弱代理信号;撤离演出 |
|
||||||
| `c3_06_response` / `c3_06_confidant` | a_confront / b_conceal / c_confide;shenduo/atang/wenjian/null | C3-06 | Act 2 开局站位 |
|
| `c3_06_response` / `c3_06_confidant` | a_confront / b_conceal / c_confide;shenduo/atang/wenjian/null | C3-06 | Act 2 开局站位;§3-8 换色 |
|
||||||
| `flag_arch_doubt` | bool | C3-06(全选项) | Act 2 总入口 |
|
| `flag_arch_doubt` | bool | C3-06(全选项) | Act 2 总入口 |
|
||||||
|
|
||||||
# 附录 B · 实测维度覆盖核对表(对母本 §4)
|
# 附录 B · 实测维度覆盖核对表(对母本 §4)
|
||||||
|
|
||||||
| 维度 | Act 1 测点(母本要求 → 本文档落点) |
|
| 维度 | Act 1 测点(母本要求 → 本文档落点) |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 依恋类型 | C1-02 #1 → C2-05 #2 → C3-02 #3 → C3-06 #4(求助行为,候选证据)✅ |
|
| 依恋类型 | C1-02 #1 → C2-05 #2 → C3-06 #3(求助行为,候选证据);C3-02 隐私隔离,不作为测点 |
|
||||||
| 冲突处理风格 | C1-04 #1 → C2-01 #2 → C2-03 #3 → C2-06 #4 → C3-04 #5 ✅ |
|
| 冲突处理风格 | C1-04 #1 → C2-01 #2 → C2-03 #3 → C2-06 #4 → C3-04 #5 ✅ |
|
||||||
| 价值观 | C1-03 #1 → C1-04 #2 → C2-01 #3 → C2-06 #4 → C3-03 #5 → C3-04 #6 ✅ |
|
| 价值观 | C1-03 #1 → C1-04 #2 → C2-01 #3 → C2-06 #4 → C3-03 #5 → C3-04 #6 ✅ |
|
||||||
| 情绪稳定性 | C2-02 #1 → C3-02 #2 → C3-03 #3 ✅ |
|
| 情绪稳定性 | C2-02 #1;C3-02 与 C3-03 均不作此维度评分输入,证据不足交 B-EMO/后续章节 |
|
||||||
| 风险偏好 | C1-06 #1 → C2-03 #2(C2-03 受现场风险信息影响,仅单次采样)✅ |
|
| 风险偏好 | C1-06 #1 → C2-03 #2(C2-03 受现场风险信息影响,仅单次采样)✅ |
|
||||||
| 文化底蕴 | C1-05 #1 → C2-04 #2 → C3-01 #3 ✅ |
|
| 文化底蕴 | C1-05 #1 → C2-04 #2 → C3-01 #3 ✅ |
|
||||||
| 择偶偏好(弱代理信号) | C3-05 #1 → C3-06 #2(均为弱代理信号,画像层低置信处理,须跨章节交叉验证)✅ |
|
| 择偶偏好(弱代理信号) | C3-05 #1 → C3-06 #2(均为弱代理信号,画像层低置信处理,须跨章节交叉验证)✅ |
|
||||||
@@ -1694,21 +1780,61 @@ C-2049 的燃料质量比预估高,是的,**那扇门开得很顺**。
|
|||||||
- 合规定位(ADR-0001):主角方全程为采集/保护者;老鬼的私藏线
|
- 合规定位(ADR-0001):主角方全程为采集/保护者;老鬼的私藏线
|
||||||
是被揭发与制止的对象,主角无任何"带文物牟利"的正向出口;
|
是被揭发与制止的对象,主角无任何"带文物牟利"的正向出口;
|
||||||
全文无"游戏/关卡/副本"字样(剧本内称"章节/任务/采集")。
|
全文无"游戏/关卡/副本"字样(剧本内称"章节/任务/采集")。
|
||||||
- 一次告知(ADR-0004):仅在 §1-1 邮件确认页出现一次,
|
- 一次告知(ADR-0004):仅在 §1-1 邮件确认页出现一次,文案
|
||||||
此后无任何选择点提示"请诚实"。
|
覆盖选择、语音声学特征(即焚、不留声纹)、记忆仅作模型输入、
|
||||||
- 沉默合法(铁律):四个 V 类讲述点全部写有沉默路径与等尊严回报
|
历史不可涂改四条;此后无任何选择点提示"请诚实"。
|
||||||
文案;C1-02 门为沉默者照常开启("不讲,门也开——它只是以另一种
|
- 沉默合法(铁律):三个 V 类讲述点均有沉默路径;C3-02 私密
|
||||||
方式认得你"),全文无"不开门"式假压力。
|
路径选择允许直接离开,三种方式石门均开启。C1-02 门为沉默者照常开启("不讲,门也开——它只是以另一
|
||||||
|
种方式认得你"),变体表含「沉默」档(留白的长廊),沉默者
|
||||||
|
展厅记「空档页」而非声称收到实际记忆;全文无"不开门"式假压力。
|
||||||
- 记忆私密(ADR-0004 叙事落地):§1-3 已向玩家明示"展厅与幻境
|
- 记忆私密(ADR-0004 叙事落地):§1-3 已向玩家明示"展厅与幻境
|
||||||
只有你能进入";C3-02 幻境仅玩家视角,NPC 只见玩家出神,
|
只有你能进入";C3-02 幻境仅玩家视角,NPC 只见玩家出神,
|
||||||
不复述、不评论幻境内容(仅可关心身体状态)。
|
不复述、不评论幻境内容(仅可关心身体状态);C3-02 的"不登记
|
||||||
- 讲述回报等价:C2-02 四档物质所得严格相等(暗门方位、开启时辰、
|
入档"边界明确为**不留文本、不入展厅、不作画像输入**,
|
||||||
完整谶语全给,耗时相同),差异仅在情感质地与老人态度演出。
|
只留叙事路径,不将本节行为送入画像,剧本承诺与数据规格一致。
|
||||||
|
- 无危险动作示范:主角方选项与正文不含可复现的危险操作——
|
||||||
|
不派体重最轻者探塌巷(C2-03-C 改为巷口安全距离观察)、
|
||||||
|
不以身体抵构件/扑身抱鼎(C3-03 在救护交接后只允许安全线内
|
||||||
|
的记录与组织任务)、无"探法没错""处置正确"式专业背书;险情现场的
|
||||||
|
处置只有撤离、观察与记录(支线册同标准)。
|
||||||
|
- 讲述回报等价:C2-02 四档物质所得与服务严格相等(暗门方位、
|
||||||
|
开启时辰、完整谶语全给;老人同点同刻送到门口;等门与搓绳
|
||||||
|
劳动均摊),差异仅在情感质地;C2-05 三档回报互不可替代且
|
||||||
|
核心悬念独家情报不在深暴露档。
|
||||||
- 测量解释收窄:所有"人格信号"为单次采样弱证据(使用公约
|
- 测量解释收窄:所有"人格信号"为单次采样弱证据(使用公约
|
||||||
「测量纪律」);C3-05/C3-06 的择偶偏好信号标注为弱代理、
|
「测量纪律」);C3-05/C3-06 的择偶偏好信号标注为弱代理、
|
||||||
画像层低置信处理;C2-03 标注单次采样。
|
画像层低置信处理;C2-03 标注单次采样;C3-02 不采语音、
|
||||||
|
不留文本、不输入画像。
|
||||||
- 叙述纪律:旁白只写可见动作与台词,不替玩家或 NPC 的心理下结论;
|
- 叙述纪律:旁白只写可见动作与台词,不替玩家或 NPC 的心理下结论;
|
||||||
两难设计不靠"无伤演出"兜底(C3-03 三线均有真实代价)。
|
C3-03 伤员获同等救护,差异落在安全区域的记录任务与遗存损失,
|
||||||
|
NPC 不做"选得对/处置正确"式裁判(C3-03-B/C 台词已清除)。
|
||||||
- 支线铁律:所有分支差异收敛于变量与文本换色,主线事件序列
|
- 支线铁律:所有分支差异收敛于变量与文本换色,主线事件序列
|
||||||
对所有玩家一致(三通道原则);自适应支线仅留挂点(§2-4、§3-2 前),
|
对所有玩家一致(三通道原则);自适应支线仅留挂点(§2-4、§3-2 前),
|
||||||
支线剧本另册;支线须遵守 `wenjian_health` 贯穿约束(§2-4 末)。
|
支线剧本另册;支线须遵守 `wenjian_health` 贯穿约束(§2-4 末,
|
||||||
|
日程见附录 D)。
|
||||||
|
|
||||||
|
# 附录 D · 逐日行程与状态基线(连续性核对用)
|
||||||
|
|
||||||
|
> 绝对日历:Day 1 抵达建营(夜 1)→ Day 2 晨 C2-01 分药、午后
|
||||||
|
> §2-3 墓道 → Day 3 晨 §2-4 岔路、黄昏 §2-5 观星台、夜 §2-6
|
||||||
|
> 篝火、深夜 §2-7 玉佩 → Day 4–5 例行采集(§2-8 旁白,夜 5)→
|
||||||
|
> Day 6 移营宫墟外围 → Day 7 §3-1 入宫墟、§3-2/§3-3/§3-4 同日
|
||||||
|
> (C3-04 当夜)→ Day 8 §3-5、夜 §3-7 钩子 → Day 9 晨 §3-8 撤离。
|
||||||
|
> 「那一天」(大火正位)=撤离日+9;档案馆回程=撤离日+8
|
||||||
|
> (C-沈铎线改为+7)。
|
||||||
|
|
||||||
|
| 日 | 主线事件 | 温简(A=recovering) | 温简(B/C/D=weak) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1 | 抵达、建营 | 疟疾初发 | 疟疾初发 |
|
||||||
|
| 2 | C2-01、§2-3 | 午后烧退,卧床 | 烧未退,留营 |
|
||||||
|
| 3 | §2-4、§2-5、§2-6、§2-7 | 下床首日随队(行装减半) | 晌午烧退净;黄昏坚持随队(慢半档);夜卧火边 |
|
||||||
|
| 4–5 | 例行采集(§2-8) | 全天随队 | 随队,走路慢半拍 |
|
||||||
|
| 6 | 移营宫墟外围 | 正常 | 正常 |
|
||||||
|
| 7 | §3-1~§3-4 | 正常 | 无烧,体力未复(描写降档) |
|
||||||
|
| 8 | §3-5、§3-7 | 正常 | 正常 |
|
||||||
|
| 9 | §3-8 撤离 | 正常 | 正常 |
|
||||||
|
|
||||||
|
> 支线挂点:挂点 1=Day 3 §2-4 之后;挂点 2=Day 7 §3-1 之后。
|
||||||
|
> 阿棠:Day 7 C3-03 前主线无伤(B-VAL-B 线可有下台阶轻微崴脚,
|
||||||
|
> 见支线册);C3-03 后三线伤势一致(fracture),由接应人员完成
|
||||||
|
> 救护,§3-5 撤离演出应保持行动限制,不因玩家路线变化。
|
||||||
|
|||||||
+337
-286
@@ -29,8 +29,9 @@
|
|||||||
- **不致命铁律**:与主线一致——支线内任何选择不造成死亡、重伤、
|
- **不致命铁律**:与主线一致——支线内任何选择不造成死亡、重伤、
|
||||||
任务失败或资源崩盘;代价全部落在关系、信息、时间与心绪上。
|
任务失败或资源崩盘;代价全部落在关系、信息、时间与心绪上。
|
||||||
- **温简在场规则**(挂点 1 专用):若 `wenjian_health = weak`
|
- **温简在场规则**(挂点 1 专用):若 `wenjian_health = weak`
|
||||||
(C2-01 选 B/C),温简在挂点 1 各支线中**留营**,其台词一律
|
(C2-01 选 B/C/D,见 act1.md 附录 A),温简在挂点 1 各支线中
|
||||||
改由通讯频道传出(声音虚弱,同 act1.md §2-5 的处理);
|
**留营**,其台词一律改由通讯频道传出(声音虚弱,同 act1.md §2-5
|
||||||
|
的处理);
|
||||||
在场氛围描写(如 B-VAL-A-01「在记录板边角画着什么」、
|
在场氛围描写(如 B-VAL-A-01「在记录板边角画着什么」、
|
||||||
B-EMO-A-02「贴着你的耳朵喊」)同步改为频道侧的对应细节
|
B-EMO-A-02「贴着你的耳朵喊」)同步改为频道侧的对应细节
|
||||||
(频道里传来他翻页的声音 / 频道里喊,电流声盖掉半句)。
|
(频道里传来他翻页的声音 / 频道里喊,电流声盖掉半句)。
|
||||||
@@ -144,8 +145,10 @@
|
|||||||
- **C**:中继、信标、扇面搜索,一套流程走得又快又静。
|
- **C**:中继、信标、扇面搜索,一套流程走得又快又静。
|
||||||
阿棠是在你插到第七根信标时自己撞上来的。她看着一路的信标,
|
阿棠是在你插到第七根信标时自己撞上来的。她看着一路的信标,
|
||||||
又看看你和温简,忽然有点不好意思:「搞这么大阵仗……
|
又看看你和温简,忽然有点不好意思:「搞这么大阵仗……
|
||||||
我就贪看了两眼。」沈铎在频道里听完汇报,说:「流程正确。」
|
我就贪看了两眼。」沈铎在频道里听完汇报,只回了一句:
|
||||||
顿了顿,补一句:「下次,听她的脚印,别听她的嘴。」
|
「下次,听她的脚印,别听她的嘴。」收队时老鬼蹲在装备箱前,
|
||||||
|
一根一根清点信标,咂着嘴记进损耗册:「七根杆子,搜一个
|
||||||
|
贪看了两眼的丫头——这笔账档案馆问起来,你签字。」
|
||||||
|
|
||||||
### 重逢(三后续汇合,无分支)
|
### 重逢(三后续汇合,无分支)
|
||||||
|
|
||||||
@@ -210,8 +213,9 @@
|
|||||||
- **无正确答案论证**:01 三项全是负责任的找法,代价各一——
|
- **无正确答案论证**:01 三项全是负责任的找法,代价各一——
|
||||||
A 制造了第二个暴露面(沈铎的半拍停顿即代价的显形);
|
A 制造了第二个暴露面(沈铎的半拍停顿即代价的显形);
|
||||||
B 让她在未知里多待十九分钟(那十九分钟对你是煎熬,
|
B 让她在未知里多待十九分钟(那十九分钟对你是煎熬,
|
||||||
对她也是);C 动用全队资源应对一个大概率虚警,且"流程正确"
|
对她也是);C 动用全队装备应对一个大概率虚警,七根信标的
|
||||||
的评语听着暖,细想是把一个活人拆成了搜索坐标。
|
损耗当场进了老鬼的账本——组织一架搜一个人,细想是把
|
||||||
|
一个活人拆成了搜索坐标。
|
||||||
02 四项全是真心的不同形状:A 诚实但让对方欠了债("那我错了嘛"
|
02 四项全是真心的不同形状:A 诚实但让对方欠了债("那我错了嘛"
|
||||||
是她不习惯的低头);B 体面但温简点破了它的隐性成本——
|
是她不习惯的低头);B 体面但温简点破了它的隐性成本——
|
||||||
你的克制可能正是她想翻越的墙;C 亲密且留退路,但"各自认领
|
你的克制可能正是她想翻越的墙;C 亲密且留退路,但"各自认领
|
||||||
@@ -476,7 +480,7 @@
|
|||||||
`cb_con_a_aftermath ∈ {a_comfort, b_mirror, c_leave}`。
|
`cb_con_a_aftermath ∈ {a_comfort, b_mirror, c_leave}`。
|
||||||
- 关系值:`rapport.laogui`(01:A+1 / B+2 / C−1 / D+1);
|
- 关系值:`rapport.laogui`(01:A+1 / B+2 / C−1 / D+1);
|
||||||
草市氛围 `market_warmth ∈ {normal, cold}`(B 为 cold)。
|
草市氛围 `market_warmth ∈ {normal, cold}`(B 为 cold)。
|
||||||
- 变量:`flag_water_intel = true`(仅 C,城备水情报,§3-8
|
- 变量:`flag_water_intel = true`(仅 C,城备水情报,Act 2
|
||||||
「那一天」推算的佐证素材);`flag_shiling_noticed = true`
|
「那一天」推算的佐证素材);`flag_shiling_noticed = true`
|
||||||
(仅 D,Act 2 伏笔)。
|
(仅 D,Act 2 伏笔)。
|
||||||
- **收束接口**:纠纷了结,日头西斜——从草市抄近路抵达宫墟
|
- **收束接口**:纠纷了结,日头西斜——从草市抄近路抵达宫墟
|
||||||
@@ -488,10 +492,12 @@
|
|||||||
|
|
||||||
## B-CON 变体 B「官牙」(挂点 2:§3-2 前)
|
## B-CON 变体 B「官牙」(挂点 2:§3-2 前)
|
||||||
|
|
||||||
【场景】清晨,宫墟暗门外。你们正准备进安门,一个穿青色官服
|
【场景】上午,宫墟安门内,下沉庭院。你们刚穿过守墓老人指点的
|
||||||
色衣的中年人堵在路口,满脸堆笑地冲老鬼作揖——**鱼牙人**,
|
暗门(§3-1),正在庭院里整队、准备登台基。背风的坑阵阴影里,
|
||||||
替宫里采买的牙人,三天前在城里和老鬼做过一笔"买卖"。
|
一个穿青色官服色衣的中年人闪出来,满脸堆笑地冲老鬼作揖——
|
||||||
沈铎今早清点物资,发现档案馆配发的古代侧硬通货——
|
**鱼牙人**,替宫里采买的牙人,三天前在城里和老鬼做过一笔
|
||||||
|
"买卖"。他跟得进来毫不心虚:替宫里跑腿的人,暗门开着的时辰
|
||||||
|
他门儿清。沈铎今早清点物资,发现档案馆配发的古代侧硬通货——
|
||||||
**应急金饼,少了一半**。此刻三个人呈一个三角站着,
|
**应急金饼,少了一半**。此刻三个人呈一个三角站着,
|
||||||
台基上的采集窗口只有上午。
|
台基上的采集窗口只有上午。
|
||||||
|
|
||||||
@@ -534,13 +540,15 @@
|
|||||||
### 各选项后续(CB-CON-B-01)
|
### 各选项后续(CB-CON-B-01)
|
||||||
|
|
||||||
- **A**:鱼牙人的笑挂不住了,从袖里数出金饼,手抖了一下。
|
- **A**:鱼牙人的笑挂不住了,从袖里数出金饼,手抖了一下。
|
||||||
临走他深深看你一眼:「爷好手段。——就是往后的**消息**,
|
临走他深深看你一眼:「爷好手段。」掂了掂退回的金饼,忽然
|
||||||
爷怕是一条也买不着喽。」老鬼全程没敢吭声,进暗门时
|
又笑开了,「不过买卖人,脸皮厚——宫里若真再有动静,兴许
|
||||||
才凑过来,小声:「新人,你这刀落得……哥哥我肉疼,但
|
小的还敢再吆喝一嗓子,就是价码嘛……得重新议。」
|
||||||
服。」沈铎只说了两个字:「干净。」
|
老鬼全程没敢吭声,往台基走时才凑过来,小声:「新人,
|
||||||
|
你这刀落得……哥哥我肉疼,但服。」沈铎只说了两个字:
|
||||||
|
「干净。」
|
||||||
- **B**:沈铎盯了你两秒,收回目光:「上午的窗口,不等人。」
|
- **B**:沈铎盯了你两秒,收回目光:「上午的窗口,不等人。」
|
||||||
鱼牙人识趣地溜了。整个上午老鬼干活格外卖力。收工时
|
鱼牙人识趣地退到暗门方向。老鬼赶紧低头清点装备,沈铎
|
||||||
沈铎对你说:「面子给了。里子呢?」——这句问话没有
|
当场问你:「面子给了。里子呢?」——这句问话没有
|
||||||
选项,悬在你头上,带回队里(Act 2 沈铎线的一粒沙)。
|
选项,悬在你头上,带回队里(Act 2 沈铎线的一粒沙)。
|
||||||
- **C**:鱼牙人眼珠转了转,一拍大腿:「爷是行家!这么着——
|
- **C**:鱼牙人眼珠转了转,一拍大腿:「爷是行家!这么着——
|
||||||
小的这条,**能核**:宫里这几夜往台基上搬东西。**沉箱子
|
小的这条,**能核**:宫里这几夜往台基上搬东西。**沉箱子
|
||||||
@@ -557,7 +565,7 @@
|
|||||||
|
|
||||||
### 余波选择点
|
### 余波选择点
|
||||||
|
|
||||||
> 【进暗门前,鱼牙人(各线统一,他溜之前)最后一个作揖,
|
> 【向台基合拢前,鱼牙人(各线统一,他退走之前)最后一个作揖,
|
||||||
> 作到一半,忽然用只有你能听见的音量说:】
|
> 作到一半,忽然用只有你能听见的音量说:】
|
||||||
|
|
||||||
**鱼牙人**:爷,小的小本买卖,混口饭吃。往后……宫里若再有
|
**鱼牙人**:爷,小的小本买卖,混口饭吃。往后……宫里若再有
|
||||||
@@ -600,8 +608,10 @@
|
|||||||
对灰色渠道的私态:留线 / 上交 / 断线——是 C1-04「接不接
|
对灰色渠道的私态:留线 / 上交 / 断线——是 C1-04「接不接
|
||||||
老鬼的话」的换皮复测,但压力升级:这次递钩子的是陌生人,
|
老鬼的话」的换皮复测,但压力升级:这次递钩子的是陌生人,
|
||||||
没有"队友"这层遮羞布。
|
没有"队友"这层遮羞布。
|
||||||
- **无正确答案论证**:01 四项——A 最彻底但永远关上了情报的
|
- **无正确答案论证**:01 四项——A 最彻底,当场堵死了这笔买卖,
|
||||||
门(末路之城里,情报是比金饼硬的通货);B 顾全了队,但
|
鱼牙人记住了"这位爷不好糊弄";他临走自己留了句"兴许还敢
|
||||||
|
再吆喝",可再开腔的价码与真假,都得重新议过(末路之城里,
|
||||||
|
情报是比金饼硬的通货);B 顾全了队,但
|
||||||
沈铎那句「里子呢」明示:搁置不是解决,是把账单延后且
|
沈铎那句「里子呢」明示:搁置不是解决,是把账单延后且
|
||||||
加了利息;C 拿到真情报,但你等于追认了这笔违规买卖的
|
加了利息;C 拿到真情报,但你等于追认了这笔违规买卖的
|
||||||
合法性——老鬼学到了"先斩后奏可以洗白";D 最严密,可
|
合法性——老鬼学到了"先斩后奏可以洗白";D 最严密,可
|
||||||
@@ -620,8 +630,9 @@
|
|||||||
情报,**面向未来场景生效**:于 §3-2 台基箱痕处应验换色,
|
情报,**面向未来场景生效**:于 §3-2 台基箱痕处应验换色,
|
||||||
见收束接口);`flag_fish_channel = true`
|
见收束接口);`flag_fish_channel = true`
|
||||||
(仅 02-A,Act 2 暗线入口)。
|
(仅 02-A,Act 2 暗线入口)。
|
||||||
- **收束接口**:纠纷了结,进暗门,穿过庭院,向台基合拢,
|
- **收束接口**:纠纷了结,鱼牙人原路退出暗门,你们在庭院里
|
||||||
进 §3-2,主线零改动。情报只许面向**未来**场景生效——
|
重新整队,向台基合拢,进 §3-2,主线零改动。情报只许面向
|
||||||
|
**未来**场景生效——
|
||||||
若持 `flag_palace_crates`,§3-2【场景】的台基特写追加
|
若持 `flag_palace_crates`,§3-2【场景】的台基特写追加
|
||||||
换色句:「台基的夯土台面上,有一道道新鲜的箱底压痕和
|
换色句:「台基的夯土台面上,有一道道新鲜的箱底压痕和
|
||||||
拖拽的辙印,从暗门方向一直拖到空璧座前。鱼牙人的话
|
拖拽的辙印,从暗门方向一直拖到空璧座前。鱼牙人的话
|
||||||
@@ -635,11 +646,17 @@
|
|||||||
# 支线 B-EMO「无常」· 情绪稳定性
|
# 支线 B-EMO「无常」· 情绪稳定性
|
||||||
|
|
||||||
> **触发条件**:章末路由中「情绪稳定性」`confidence < 0.6`。
|
> **触发条件**:章末路由中「情绪稳定性」`confidence < 0.6`。
|
||||||
> **测量目标**:主线测点(C2-02/C3-02/C3-03)测的是**有铺垫的**
|
> **测量目标**:主线 C2-02 的讲述只提供一次弱证据;C3-02 已
|
||||||
> 高压(讲述、幻境、两难),本支线补**零铺垫的突发**——没有时间
|
> 隐私隔离、C3-03 只测价值观,本支线补突发现场的任务分配观察。
|
||||||
> 权衡,只有反应。核心数据是行为层的:计时选择点的决策耗时、
|
> 计时点只记录反应行为,不能把反应快慢、是否改选或是否读完
|
||||||
> 是否改选、高压段落中是否仍读完环境描述(CONTEXT 辅助机制)。
|
> 环境描述直接解释成稳定的情绪特征。
|
||||||
> 选项本身两体面两难堪,与主线同标准。
|
> 选项本身两体面两难堪,与主线同标准。
|
||||||
|
> **计时与打断规格(工程)**:计时自选项界面完整呈现起算;
|
||||||
|
> 超时走合法超时路径;外部打断(来电、切后台)标注
|
||||||
|
> `interrupted`,决策耗时不计入分析,若恰在计时结束时仍走合法
|
||||||
|
> 超时路径;「未读完环境文本即选择」只记行为事实,
|
||||||
|
> 不单独定罪。反应时数据仅作维度置信度的行为底噪,
|
||||||
|
> **不直接映射情绪稳定性分值**。
|
||||||
|
|
||||||
## B-EMO 变体 A「沙暴」(挂点 1:§2-4 后)
|
## B-EMO 变体 A「沙暴」(挂点 1:§2-4 后)
|
||||||
|
|
||||||
@@ -650,120 +667,112 @@
|
|||||||
|
|
||||||
【正文】
|
【正文】
|
||||||
|
|
||||||
**沈铎**(吼,风撕掉一半音量):沙暴!**就近掩体!**
|
**沈铎**(吼,风撕掉一半音量):沙暴!**原地停,清点人数!**
|
||||||
|
|
||||||
> 【视野里两处可躲:三十步外,一座塌了半边的民居,门洞黑着,
|
> 【视野里两处可能的遮挡:近处半塌的民居、远处主街的石坊。
|
||||||
> 剩半面墙还立着;一百五十步外,主街的石坊,四根整石柱,
|
> 两处结构都未经确认,此刻看不清通路。沈铎已安排队员在现有
|
||||||
> 看着就稳——但要顶着风跑一百五十步。】
|
> 警戒线内避风、清点人数;任何转移都由他在能见度恢复后执行。】
|
||||||
|
|
||||||
**阿棠**(把采集包顶在头上):民居近!!跑哪个?!
|
**阿棠**(把采集包挡在身前):民居近,可那半面墙还在晃。
|
||||||
|
|
||||||
**沈铎**:石坊结构稳——
|
**沈铎**:石坊远,通路看不清。都先别动。
|
||||||
|
|
||||||
**老鬼**(已经抱头蹲下):哎哟我的祖宗,近的先躲啊!
|
**老鬼**(已经蹲下):哎哟我的祖宗,先点人,别少一个!
|
||||||
|
|
||||||
**温简**(什么都没说,看你。所有人都在看你。)
|
**温简**(什么都没说,看你。所有人都在看你。)
|
||||||
|
|
||||||
> 【选项(CB-EMO-A-01)· 计时 10 秒,界面有风声音效渐强。
|
> 【选项(CB-EMO-A-01)· 计时 10 秒,界面有风声音效渐强。
|
||||||
> 超时未选 = 跟最近的人跑(合法路径,记 `follow`),
|
> 超时未选=继续听沈铎指挥清点(合法路径,记 `follow`),
|
||||||
> 剧本按「民居」线收束,无惩罚。】
|
> 剧本按 A 的清点线收束,无惩罚。】
|
||||||
>
|
>
|
||||||
> **A.「民居!近就是生——跑!」**
|
> **A.「先点人。阿棠和老鬼数装备,我跟沈铎对人数。」**
|
||||||
|
> (把未知拆成一个个能核实的名字)
|
||||||
>
|
>
|
||||||
> **B.「石坊!塌了半边的墙不叫墙——顶风跑,别停!」**
|
> **B.「先报坐标。把能看见的两处遮挡记在图上,等风缓再走。」**
|
||||||
|
> (把行动推迟,用位置与环境信息换确定性)
|
||||||
>
|
>
|
||||||
> **C.「都不跑!物资箱围圈,就地蹲下——风暴里跑动的才是靶子!」**
|
> **C.「先安顿队伍。谁也别往断墙跑,留在警戒线内等沈铎调度。」**
|
||||||
> (不动,赌建筑区能碎掉风势)
|
> (暂缓记录,将体力留给可能需要帮助的队员)
|
||||||
|
|
||||||
### 各选项后续(CB-EMO-A-01)
|
### 各选项后续(CB-EMO-A-01)
|
||||||
|
|
||||||
- **A**:你们连滚带爬撞进门洞。身后,一块悬着的椽子被风
|
- **A**:名字核到最后一个时,阿棠没应——不是走散,她被风声
|
||||||
掰下来,擦着你的肩砸在地上(轻伤演出:肩头淤青)。
|
盖住了。你又喊一遍,她举起手。人齐了,最远那道遮挡仍看不清。
|
||||||
阿棠盯着那块椽子看了两秒,声音有点飘:「哎……
|
沈铎把人数写进记录板:「先记人,别替风猜路。」
|
||||||
差一步。」你替她说了她没说出口的话:「差一步也是**没到**。」
|
- **B**:你在现有警戒线内标出民居、石坊和队伍坐标。风卷过来,
|
||||||
- **B**:一百五十步像一里。风从背后推你,又迎面撞你。
|
石坊一度从视野里消失;地图留着,现场却还需重新确认。
|
||||||
冲到石坊底下时,五个人叠着喘,老鬼喘得最响,喘着喘着
|
老鬼看了一眼图:「画得再准,也得等这阵过去。」
|
||||||
笑了:「活着……真好……」沈铎第一个平复下来,开始点人
|
- **C**:你请队员暂放物资,温简在通讯里报平安,沈铎继续
|
||||||
点物——点完,他的目光在你身上停了一拍:「赌对了。
|
清点。没人离开警戒线;一箱记录材料被沙埋了半截,得等风停
|
||||||
下次赌之前,先喊一声。」
|
才能找齐。阿棠拍拍你的胳膊:「人都在,东西慢慢找。」
|
||||||
- **C**:你们用物资箱和身体围出一个背风的窝。沙从箱缝
|
|
||||||
往里灌,先是脚踝,再是小腿。谁都不说话——说话吃沙。
|
|
||||||
半个时辰后风势稍缓,你们从半尺深的沙里把自己拔出来,
|
|
||||||
清点物资又花了半个时辰。沈铎的评价是:「难看。但齐全。」
|
|
||||||
阿棠呸着嘴里的沙:「下次还是跑吧……」
|
|
||||||
|
|
||||||
### 风暴眼中(三后续汇合)
|
### 风暴眼中(三后续汇合)
|
||||||
|
|
||||||
> 【风最盛的时候,掩体外忽然传来一声——**像有人在喊**。
|
> 【风最盛的时候,警戒线外忽然传来一声——**像有人在喊**。
|
||||||
> 长长的,被风扯碎的,人嗓子一样的声音。也可能只是风
|
> 长长的,被风扯碎的,人嗓子一样的声音。也可能只是风
|
||||||
> 穿过某段断墙。风声里,分辨不出来。】
|
> 穿过某段断墙。风声里,分辨不出来。】
|
||||||
|
|
||||||
**温简**(贴着你的耳朵喊):风过断墙……多半是这个。
|
**温简**(通讯频道里喊):风过断墙……多半是这个。
|
||||||
|
|
||||||
**阿棠**(脸色发白):多半?!**万一是人呢?!**
|
**阿棠**(脸色发白):多半?!**万一是人呢?!**
|
||||||
|
|
||||||
> 【选项(CB-EMO-A-02)· 无计时,但风声循环渐强,
|
> 【选项(CB-EMO-A-02)· 无计时,但风声循环渐强,
|
||||||
> 界面不提示"请快选",营造自然催促】
|
> 界面不提示"请快选",营造自然催促】
|
||||||
>
|
>
|
||||||
> **A.「我出去看一眼。万一是人——听不见这句'万一',**
|
> **A.「把声音来向再听一遍,和地图对上。万一是人,**
|
||||||
> **我往后都睡不安稳。」**
|
> **我们至少知道从哪里找。」**
|
||||||
> (冲出去,呛一嘴沙,空手而归)
|
> (听不清,也不愿把疑点放过)
|
||||||
>
|
>
|
||||||
> **B.「谁都不许出。掩体规矩:风暴里,出去的人才是
|
> **B.「先保证队伍都在。等风缓再查,眼下谁也别离开。」**
|
||||||
> 需要被救的人。」**
|
> (暂不追查声音,优先守住现场人员)
|
||||||
> (摁住所有人,包括自己)
|
|
||||||
>
|
>
|
||||||
> **C.「绳子。一头拴石柱(或:门框),一头拴我。**
|
> **C.「先报方位,试着在警戒线内呼叫;等风缓再去核实。**
|
||||||
> **我探半个身子出去——有情况拉我回来。」**
|
> **谁也不要为一个听不清的声音冲进风里。」**
|
||||||
> (用风险管理去够那个「万一」)
|
> (保持联络,把核查延后到能安全接近时)
|
||||||
|
|
||||||
### 各选项后续(CB-EMO-A-02)
|
### 各选项后续(CB-EMO-A-02)
|
||||||
|
|
||||||
- **A**:你冲出去三十步,风把你摁着摩擦。没有人。只有一段
|
- **A**:你在警戒线内记下两次声音来向;一阵风又把方位
|
||||||
断墙,墙洞的形状像一张嘴,风一过就"喊"。你眯着眼回来,
|
搅乱。阿棠问:「听清了吗?」你摇头,她便把地图递过来。
|
||||||
吐了半天的沙。阿棠给你拍背,拍得极重:「下次这种活儿,
|
图上有一道断墙,但「没听清」不等于「没有人」——
|
||||||
轮班!」——她没说你做得对,也没说错。那声"喊"还在
|
这几个字一直跟着你们等到风停。
|
||||||
风里一阵一阵的,回程的路上,谁都没提它。
|
- **B**:那声"喊"又响了两回,一回比一回长。没人离开
|
||||||
- **B**:那声"喊"又响了两回,一回比一回长。没人动。阿棠
|
警戒线。阿棠抱膝坐着,问:「要是真有人呢?」沈铎记下
|
||||||
把脸埋在膝盖里。风停后你们去看了:是一段断墙,墙洞
|
声音方向,答:「风缓后查。」她点头,没再说话。
|
||||||
像嘴。阿棠站在那墙洞前看了很久,说:「幸亏。」
|
- **C**:你们留在警戒线内交替呼叫并记录声音来向;频道没有回应。
|
||||||
顿了顿,又说:「**万一**呢。」——这两个字她念得很轻,
|
沈铎写下方位,说:「风停再去。」阿棠瞥一眼风口:
|
||||||
风把它带走了半句。
|
「要是真有人,这段等着的工夫也够难熬。」温简翻开记录板:
|
||||||
- **C**:绳头在沈铎手里,你探出半个身子。沙打得脸生疼。
|
「有来向,没身份。先记到这里。」没人知道那声音究竟是什么。
|
||||||
你看见了:断墙,墙洞,风。你缩回来,解绳子的时候
|
|
||||||
手有点抖——不是怕,是风真的很大。沈铎收绳时说:
|
### 风停(三后续汇合,无分支)
|
||||||
「处置规范。」阿棠补一句:「就是看着吓人。」温简最后
|
|
||||||
开口,像自言自语:「听见了,去找了,找到了。**这事
|
> 【风势缓下来后,沈铎按现场规程安排核查,队伍只在他确认
|
||||||
就过去了。**」说完他把记录板合上,风恰好在这时
|
> 通路后移动。远处那声像人喊的声音来自断墙上的孔洞;
|
||||||
小了一档。
|
> 此前谁都没有足够证据断言。众人记下位置,再清点物资,
|
||||||
|
> 记录箱被找回,黄昏前继续向观星台行进。】
|
||||||
|
|
||||||
### 选项设计说明(CB-EMO-A-01 / A-02)
|
### 选项设计说明(CB-EMO-A-01 / A-02)
|
||||||
|
|
||||||
- **人格信号**:情绪稳定性(换皮复测:C3-03 是"两难高压",
|
- **人格信号**:突发事件里的任务分配(点人 / 标位 / 安顿)
|
||||||
此处是"无常高压")。核心数据为行为层:01 的决策耗时与
|
与不确定声音下的信息处理(重听 / 等待 / 呼叫),均仅为
|
||||||
超时路径(`follow`)、02 选择前的犹豫时长、是否改选、
|
单次弱证据。两个计时点的反应时、超时和阅读进度只记行为,
|
||||||
高压段环境文本的阅读完成度。选项层信号:01 是风险直觉的
|
不直接映射情绪稳定性分值;被打断时不计。
|
||||||
裸态(距离/结构/不动三种本能);02 是"不确定威胁下的
|
- **无正确答案论证**:01 的 A 先确认人员,却暂失位置记录;
|
||||||
行动阈值"——冲动求证 / 规则自控 / 验证式管理。
|
B 标位留图,人数需队长确认;C 安顿队员,记录箱要风停后
|
||||||
- **无正确答案论证**:01 三线均存活且代价各一(A 的椽子、
|
寻回。所有路线都留在安全线内,不给冒险抢行动的回报。
|
||||||
B 的"赌对了下次先喊"、C 的半个时辰与一嘴沙),沈铎的
|
02 三项都不能在风中确证声音来源:A 多留一条听觉线索,
|
||||||
三句评价刻意等值——没有任何一条被写成"正确处置"。
|
B 守着队伍,C 发出呼叫并留方向记录;真正的核查均在风缓
|
||||||
02 是本支线真正的测量眼:三项里**没有"对"的答案**,
|
且通路由沈铎确认之后。对"万一"的分歧是心理余味,
|
||||||
因为墙洞里本来就没有人——A 的代价是白呛的沙和阿棠
|
不是险情正确答案。
|
||||||
被吓的那一下;B 的代价是"万一呢"三个字,剧本明示它会
|
|
||||||
跟着你;C 看似最优,但温简那句"这事就过去了"是三后续
|
|
||||||
中唯一的闭环台词,恰恰反衬出:**闭环也是用风险买来的**,
|
|
||||||
绳头若不在沈铎手里呢?三项后续的情绪余量被刻意拉平:
|
|
||||||
都安心了一半,都悬着一半。
|
|
||||||
|
|
||||||
### 分支后果(CB-EMO-A)
|
### 分支后果(CB-EMO-A)
|
||||||
|
|
||||||
- 变量:`cb_emo_a_shelter ∈ {a_house, b_arch, c_huddle, follow}`;
|
- 变量:`cb_emo_a_shelter ∈ {a_count, b_map, c_shelter, follow}`;
|
||||||
`cb_emo_a_cry ∈ {a_rush, b_hold, c_tether}`。
|
`cb_emo_a_cry ∈ {a_look, b_hold, c_call}`。
|
||||||
- 变量:`cb_emo_a_rt1 / cb_emo_a_rt2`(两个选择点的决策耗时,
|
- 变量:`cb_emo_a_rt1 / cb_emo_a_rt2`(两个选择点的决策耗时,
|
||||||
毫秒,供维度置信度计算)。
|
毫秒,供维度置信度计算)。
|
||||||
- 状态:肩头淤青(仅 A 线,演出细节,无机制影响)。
|
- 状态:三线无新增伤情;损耗仅为记录材料的清点时间。
|
||||||
- **收束接口**:风停,浮沙被刮走整整一层——主街中段的地面
|
- **收束接口**:浮沙被刮走整整一层——主街中段的地面
|
||||||
露出一截向下的石阶,和一方圆形基座的边缘。阿棠用袖子
|
露出一截向下的石阶,和一方圆形基座的边缘。阿棠用袖子
|
||||||
擦着基座上的刻纹,声音低下来:「哎……这纹路,观星台。
|
擦着基座上的刻纹,声音低下来:「哎……这纹路,观星台。
|
||||||
它这是——**给咱们开门呢**?」被风暴"打扫"过的城替你
|
它这是——**给咱们开门呢**?」被风暴"打扫"过的城替你
|
||||||
@@ -791,90 +800,80 @@
|
|||||||
|
|
||||||
**沈铎**(极低,极慢):谁。也。别。动。
|
**沈铎**(极低,极慢):谁。也。别。动。
|
||||||
|
|
||||||
**老鬼**(脸白了):祖宗诶……这地方的东西,**死了上千年了
|
**老鬼**(脸白了):祖宗诶……这地方的东西,**不知道埋了多少
|
||||||
怎么还上班呢**……
|
年了怎么还上班呢**……
|
||||||
|
|
||||||
**温简**(蹲下,远远观察压板的边缘,声音是全队唯一没变调的):
|
**温简**(蹲下,远远观察压板的边缘,声音是全队唯一没变调的):
|
||||||
沉速很慢。非常慢。——它在动,但它**不着急**。
|
沉速很慢。非常慢。——它在动,但它**不着急**。
|
||||||
|
|
||||||
> 【选项(CB-EMO-B-01)· 计时 8 秒。超时未选 = 阿棠自行
|
> 【选项(CB-EMO-B-01)· 计时 8 秒。超时未选=沈铎按既定
|
||||||
> 数了三声跳开(合法路径,记 `self_rescue`,按 A 线收束,
|
> 排险预案接管(合法路径,记 `self_rescue`,按 A 线收束),
|
||||||
> 追加一句她的台词「等你们裁完,我腿都站麻了」)。】
|
> 不把等待误读为不稳定。阿棠的撤离始终由受训人员处置。】
|
||||||
>
|
>
|
||||||
> **A.「阿棠,听我数。三、二、一——跳!」**
|
> **A.「先通知沈铎,按预案撤出阿棠;其余人退出阵列。」**
|
||||||
> (快刀:赌它只是一次触发,不赌它的机制)
|
> (先把现场留给受训人员,暂不采集)
|
||||||
>
|
>
|
||||||
> **B.「别跳。谁知道跳开了它弹起来会带出什么——换人踩!
|
> **B.「把观察到的沉速与缝宽报给沈铎;我去清空外围。」**
|
||||||
> 谁体重跟她最接近?平移换脚,再慢慢卸。」**
|
> (给排险人员信息,把其余队员带离)
|
||||||
> (置换:稳字当头,但赌桌上多坐一个人)
|
|
||||||
>
|
>
|
||||||
> **C.「都别碰她。老鬼,撬杆;温简,绳——把她原地固定住,**
|
> **C.「全员退到警戒线,远距留影,排险交沈铎处置。」**
|
||||||
> **我们拆这块板。要解就解透。」**
|
> (一边记录一边隔离,放弃近处细节)
|
||||||
> (程序化排险:最慢,但每一步都可逆)
|
|
||||||
|
|
||||||
### 各选项后续(CB-EMO-B-01)
|
### 各选项后续(CB-EMO-B-01)
|
||||||
|
|
||||||
- **A**:跳。压板在她身后弹起半寸,停住,像打了个哈欠。
|
- **A**:沈铎立即接管,其他队员退到警戒线。阿棠在受训人员
|
||||||
什么都没发生。阿棠退开三步,回头瞪着那块板,半天:
|
协助下安全离开;你没看清压板沉降的过程。她看见你手里的
|
||||||
「……就这?」沈铎没放松,他盯着的是**坑壁那道缝**——
|
空记录板,问:「刚才那一下,真有东西动了吗?」
|
||||||
它正在缓缓合拢。方才那一眼里,有人看见了缝里的东西:
|
- **B**:你报告读数后清空外围,沈铎接管排险。阿棠安全撤出;
|
||||||
成排的、一人高的影子。
|
你在外围听着频道里一段段报平安,直到最后一声才松开手。
|
||||||
- **B**:换脚。全队体重和她最接近的是温简(若 C2-05
|
坑壁裂缝在视野之外开到最宽,没人及时拍下全貌。
|
||||||
玩家与他有同行约定,追加一句他的低语:「正好,我步子慢,
|
- **C**:你们退到警戒线外留影;沈铎与受训接应人员按既定预案
|
||||||
站得稳。」)。平移、换脚、阿棠脱险——然后温简一个人
|
带阿棠撤出。镜头拍到坑壁开缝的瞬间,却因距离太远,
|
||||||
站在板上,站了全队最漫长的三分钟,直到沈铎用石块把
|
板面的刻痕看不清。阿棠看着那张照片:「原来刚才
|
||||||
板缘楔死。他下来时手心全是汗,还开了个玩笑:「现在我
|
我站的是这里。」
|
||||||
知道绳头是什么滋味了。」(C2-05 的他,懂的人自然懂。)
|
|
||||||
- **C**:撬杆、绳套、楔石,一套流程走了十分钟。拆到最后,
|
|
||||||
沈铎把整块压板掀开——所有人都看见了:**板下的机簧,
|
|
||||||
早就锈死成了一块铁疙瘩**。它不知道多少年前就死了。
|
|
||||||
那一声「咔」,是阿棠踩裂了板面;坑壁滑动,是地基自然
|
|
||||||
沉降。两件死事,撞在同一秒,演了一场活的。
|
|
||||||
阿棠一屁股坐在地上,笑出了眼泪。沈铎蹲在锈死的机簧前
|
|
||||||
看了很久,说了全支线最重要的一句台词:
|
|
||||||
「都记住。**死机关比活机关吓人——因为你不知道它死没死透。
|
|
||||||
这座城里全是这种东西。**」
|
|
||||||
|
|
||||||
### 缝中之物(三后续汇合)
|
### 缝中之物(三后续汇合)
|
||||||
|
|
||||||
> 【坑壁的缝还在缓缓合拢。【C 线换色:被掀开的压板斜在一边,
|
> 【坑壁的缝开到最宽,又缓缓合拢。阿棠安全撤出后,
|
||||||
> 锈死的机簧摊开在日光下——所有人看一眼那块死铁,再看一眼
|
> 大家在警戒线外把各自见到的片段拼成同一张记录:缝里有成排
|
||||||
> 那道活着的缝,谁都不说话。】缝里的东西,
|
> 的**陶范**——铸鼎的模子,一人来高,内壁的纹样与台基上
|
||||||
> 这一眼谁都看清了:成排的**陶范**——铸鼎的模子,一人来高,
|
> 七鼎六簋相似;缝口下方的机件**锈蚀严重**。这些观察不足以
|
||||||
> 内壁上刻着纹样,和台基上那组七鼎六簋的纹样,**一模一样**。
|
> 判定机关失效,需等待专业排险后确认。】
|
||||||
> 范面有使用痕。有人在宫墟里,用这些范,铸过礼器。】
|
|
||||||
|
|
||||||
**温简**:范是耗材,铸一器毁一范。整排整排地留在这儿——
|
**温简**:范是耗材,铸一器毁一范。整排整排地留在这儿——
|
||||||
(停顿)说明铸完那一批之后,**就再没有下一批了**。
|
(停顿)说明铸完那一批之后,**就再没有下一批了**。
|
||||||
|
|
||||||
|
**沈铎**(看着那道正在合拢的缝,声音沉下去):都记住。
|
||||||
|
**别拿锈色猜它死没死透。今天先记,不去试。**
|
||||||
|
|
||||||
|
**阿棠**(一屁股坐在安全线的土坎上,笑出了眼泪):吓死
|
||||||
|
我了……结果到现在也不知道它是死是活。
|
||||||
|
|
||||||
> 【选项(CB-EMO-B-02)· 缝合拢中,无计时,进度条即缝宽】
|
> 【选项(CB-EMO-B-02)· 缝合拢中,无计时,进度条即缝宽】
|
||||||
>
|
>
|
||||||
> **A.「拓包给我——缝口外沿这方范还够得着。人就站在这儿,**
|
> **A.「能记多少记多少:留在安全线内把远处可见的纹样**
|
||||||
> **探臂抢半方纹出来。」**
|
> **拍下来——它合它的,我们记我们的。」**
|
||||||
> (人不进缝:在手臂能够到的最后一寸上,把证据抢出来)
|
> (抢记录:在安全线内,把带得走的都带走)
|
||||||
>
|
>
|
||||||
> **B.「谁也不进。记录位置、朝向、缝宽。结构在动,命只有
|
> **B.「拍两张全景就够,标桩记位。它想合就让它合——**
|
||||||
> 一条——它在这儿一千年了,跑不了。」**
|
> **账先欠着,城跑不了。」**
|
||||||
|
> (放行:信息不灭失,只是延期)
|
||||||
>
|
>
|
||||||
> **C.「撬杆别住缝口!别住了再拓——(撬杆崩断一根)……**
|
> **C.「都收手,别拍了——它关门的事,就让它关完。」**
|
||||||
> **退出来。它不想开,不勉强。」**
|
> (罢手:给这座城留一点自己的秘密)
|
||||||
|
|
||||||
### 各选项后续(CB-EMO-B-02)
|
### 各选项后续(CB-EMO-B-02)
|
||||||
|
|
||||||
- **A**:你站在缝外,探臂把拓包按上缝口外沿那方范。手臂的
|
- **A**:你们留在安全线内,把剩下可见的纹样拍成序列。尘雾
|
||||||
影子在缝口一寸寸变窄,夯土屑擦着袖口簌簌往下掉——
|
一阵阵遮住镜头;温简喊停时,最后几张已只有模糊轮廓。
|
||||||
拓纸揭下来,半方纹。够了。沈铎伸手把你往后带了半步:
|
影像有用,但不够清:待会儿上台基辨纹,阿棠要对到第三件
|
||||||
「到手了,就退。」阿棠凑过来看拓片:「就半方?……
|
才敢拍板「就是这个」。
|
||||||
行,半方也是抢回来的。」——但这半方纹样,是后来
|
- **B**:沈铎在安全线外的图志上标出方位:「记在这儿。
|
||||||
§3-2 解谜时她第一个认出来的。
|
**账先欠着,城跑不了。**」
|
||||||
- **B**:缝在你面前合死,声音轻得像叹气。沈铎在你的
|
- **C**:相机递到手上又被你推了回去。缝合拢的声音很轻,
|
||||||
记录位置旁钉下一根标记桩:「记在这儿。**账先欠着,
|
像叹气。老鬼把三脚架收了,摩挲着架腿,破天荒说了句
|
||||||
城跑不了。**」——这句"账先欠着",是 Act 2 重回此地
|
不市侩的话:「人家关上门的东西……咱们今天,就不硬
|
||||||
的钩子。
|
敲了吧。」
|
||||||
- **C**:撬杆崩断的那声脆响,把所有人都钉在原地。
|
|
||||||
缝又合了一寸。老鬼把断杆捡起来,摩挲着断口,破天荒
|
|
||||||
说了句不市侩的话:「人家关上门的东西……咱们今天,
|
|
||||||
就不硬敲了吧。」
|
|
||||||
|
|
||||||
### 选项设计说明(CB-EMO-B-01 / B-02)
|
### 选项设计说明(CB-EMO-B-01 / B-02)
|
||||||
|
|
||||||
@@ -882,28 +881,23 @@
|
|||||||
02 紧迫感下的机会成本直觉)+ 风险偏好底噪(02 明显,
|
02 紧迫感下的机会成本直觉)+ 风险偏好底噪(02 明显,
|
||||||
供一致性检验,不主测)。行为数据同变体 A:决策耗时、
|
供一致性检验,不主测)。行为数据同变体 A:决策耗时、
|
||||||
超时路径、改选、文本阅读完成度。
|
超时路径、改选、文本阅读完成度。
|
||||||
- **无正确答案论证**:01 的关键设计是**C 线揭示机簧早已
|
- **无正确答案论证**:01 三线均由受训人员完成排险,玩家只决定告知、隔离和远距
|
||||||
锈死**——三线玩家事后都会听到「死机关比活机关吓人」
|
记录的优先顺序;锈色无法证明机关已失效。A 暂失读数,
|
||||||
这句台词,于是 A 的果敢、B 的稳妥、C 的严谨,在真相
|
B 错过缝宽全貌,C 远距影像缺板面细节。这些后果不拿
|
||||||
面前**全部失去"对错"的坐标**:你处置的从来不是机关,
|
人身风险配平,也不按计时长短断言情绪稳定性。
|
||||||
是你自己对未知的想象。这是本支线作为剧情的独立成立点
|
02 三项配平:A 把能带走的都带走,但影像清晰度打了折
|
||||||
(虚惊一场,而虚惊教会全队一件事),也是无正确答案铁律
|
(§3-2 认纹样要多认两件);B 一无所拍而得沈铎一句
|
||||||
的极致形态:连"危险"本身都是不确定的,何来正确反应。
|
"账先欠着"(信息延期,不灭失);C 两头落空却换来老鬼
|
||||||
02 三项配平:A 得半方拓纹(§3-2 的直接助攻)而贴着
|
全支线唯一一句真心话——有些收获专门开给"肯罢手"的人。
|
||||||
缝沿冒了一次汗——注意 A 的动作设计为「人不进缝」,
|
|
||||||
抢的是证据不是险,果敢的信号不变;
|
|
||||||
B 一无所获而得沈铎一句"账先欠着"(Act 2 钩子,信息
|
|
||||||
并未灭失,只是延期);C 两头落空却换来老鬼全支线唯一
|
|
||||||
一句真心话——有些收获专门开给"肯罢手"的人。
|
|
||||||
|
|
||||||
### 分支后果(CB-EMO-B)
|
### 分支后果(CB-EMO-B)
|
||||||
|
|
||||||
- 变量:`cb_emo_b_plate ∈ {a_jump, b_swap, c_dismantle, self_rescue}`;
|
- 变量:`cb_emo_b_plate ∈ {a_alert, b_relay, c_record, self_rescue}`;
|
||||||
`cb_emo_b_vent ∈ {a_rubbing, b_mark, c_wedge}`;
|
`cb_emo_b_vent ∈ {a_record, b_mark, c_leave}`;
|
||||||
`cb_emo_b_rt1 / cb_emo_b_rt2`(决策耗时,毫秒)。
|
`cb_emo_b_rt1 / cb_emo_b_rt2`(决策耗时,毫秒)。
|
||||||
- 变量:`flag_mould_rubbing = true`(仅 02-A,§3-2 阿棠
|
- 变量:`flag_mould_rubbing = true`(仅 02-A,§3-2 阿棠
|
||||||
认出范纹的换色依据);`flag_sealed_vault = true`(02-B/C,
|
认出范纹的换色依据——影像版);`flag_sealed_vault = true`
|
||||||
Act 2 重回钩子)。
|
(02-B/C,Act 2 重回钩子)。
|
||||||
- 关系值:`rapport.wenjian`(01-B:+1);`rapport.atang`
|
- 关系值:`rapport.wenjian`(01-B:+1);`rapport.atang`
|
||||||
(02-A:+1)。
|
(02-A:+1)。
|
||||||
- **收束接口**:陶范的纹样与「铸完那一批,再没有下一批」
|
- **收束接口**:陶范的纹样与「铸完那一批,再没有下一批」
|
||||||
@@ -911,7 +905,7 @@
|
|||||||
全队向台基合拢,进 §3-2。若持 `flag_mould_rubbing`,
|
全队向台基合拢,进 §3-2。若持 `flag_mould_rubbing`,
|
||||||
§3-2 正文追加换色句:阿棠指着鼎纹——「这纹样我认得!
|
§3-2 正文追加换色句:阿棠指着鼎纹——「这纹样我认得!
|
||||||
缝里的范,就是这个!」主线其余零改动。玩家感知:
|
缝里的范,就是这个!」主线其余零改动。玩家感知:
|
||||||
采集时踩了一脚虚惊,虚惊里瞥见的东西,上台基正好用上。
|
采集时遇到未知沉降;站在警戒线外留下的影像,上台基正好用上。
|
||||||
|
|
||||||
---
|
---
|
||||||
---
|
---
|
||||||
@@ -920,9 +914,12 @@
|
|||||||
|
|
||||||
> **触发条件**:章末路由中「价值观」`confidence < 0.6`。
|
> **触发条件**:章末路由中「价值观」`confidence < 0.6`。
|
||||||
> **测量目标**:主线价值观测点(C2-01 分药、C3-03 救人救物)
|
> **测量目标**:主线价值观测点(C2-01 分药、C3-03 救人救物)
|
||||||
> 全部是**队内/文明尺度**的分配,本支线换皮为**对陌生他者**——
|
> 全部是**队内/文明尺度**的分配,本支线换皮为**对陌生他者**的
|
||||||
> 对方与你无任何关系、无任何回报能力、且永不再见。
|
> 分配——A 变体是行路偶遇的母子(一次性照面),B 变体是有一面
|
||||||
> 对"用不上的善意"的定价,是价值观最裸的切片。
|
> 之缘的守墓老人祖孙。两变体的对象都可能回赠信息或物件(末路
|
||||||
|
> 之人有末路之人的礼数),测量目标收窄为**稀缺资源下的分配
|
||||||
|
> 姿态与裁断方式**,不声称隔离互惠预期;互惠倾向另由主线
|
||||||
|
> C2-05 主测。
|
||||||
|
|
||||||
## B-VAL 变体 A「半袋水」(挂点 1:§2-4 后)
|
## B-VAL 变体 A「半袋水」(挂点 1:§2-4 后)
|
||||||
|
|
||||||
@@ -930,6 +927,9 @@
|
|||||||
井沿边蜷着一个妇人和一个五六岁的孩子。妇人看见你们,第一眼
|
井沿边蜷着一个妇人和一个五六岁的孩子。妇人看见你们,第一眼
|
||||||
看的不是你们的脸,是**水囊**。她看了孩子一眼,才慢慢站起来——
|
看的不是你们的脸,是**水囊**。她看了孩子一眼,才慢慢站起来——
|
||||||
没有伸手。她把手腕上一只银镯褪下来,摊在掌心,举到你们面前。
|
没有伸手。她把手腕上一只银镯褪下来,摊在掌心,举到你们面前。
|
||||||
|
里坊外的官道上有零星人影往城门方向挪——城门楼那头,隐隐飘来
|
||||||
|
一缕熬粥的米气。阿棠顺着看了一眼:「早上路过,南门里侧的
|
||||||
|
施粥棚开着呢,还发饼。排队的都是妇幼。」
|
||||||
|
|
||||||
【正文】
|
【正文】
|
||||||
|
|
||||||
@@ -972,18 +972,23 @@
|
|||||||
孩子。孩子喝了一半,把杯子举到她嘴边,她抿了一小口,
|
孩子。孩子喝了一半,把杯子举到她嘴边,她抿了一小口,
|
||||||
就推回去。一来一回。走出去半条街,阿棠忽然说:
|
就推回去。一来一回。走出去半条街,阿棠忽然说:
|
||||||
「哎,你们数没数,那杯水转了四圈。」没人接话。
|
「哎,你们数没数,那杯水转了四圈。」没人接话。
|
||||||
队里的水一两没少。
|
——一杯水,四个来回;水囊的水位,浅了浅浅一格。
|
||||||
- **C**:妇人深深看了你一眼——那一眼里没有感激,有一种
|
- **C**:妇人深深看了你一眼——那一眼里没有感激,有一种
|
||||||
被还回了**人**的平静。她把镯子塞进你手里,动作快得
|
被还回了**人**的平静。她把镯子塞进你手里,动作快得
|
||||||
像怕你反悔。老鬼凑过来,啧啧:「新人,上道啊。」
|
像怕你反悔。老鬼凑过来,啧啧:「新人,上道啊。」
|
||||||
这句是夸是损,他自个儿笑眯眯地不解释。(镯子后由温简
|
这句是夸是损,他自个儿笑眯眯地不解释。
|
||||||
登记为「民间有偿征集品 CW-0322」——合规口径:主角方
|
【若 wenjian_health != weak:镯子由温简当场登记为
|
||||||
不取一物,征集品入册。)
|
「民间有偿征集品 CW-0322」。】【若 wenjian_health = weak:
|
||||||
|
镯子你代记「民间有偿征集品 CW-0322」,回营交温简补录入册。】
|
||||||
- **D**:你说了施粥处的方向,说得很详细,孩子在听,很乖。
|
- **D**:你说了施粥处的方向,说得很详细,孩子在听,很乖。
|
||||||
妇人点头,背起孩子,走了两步,又走回来,把银镯塞进
|
妇人点头,背起孩子,走了两步,又走回来,把银镯塞进
|
||||||
你手里:「不是买水了。**谢谢你指得这么细。**」她走了。
|
你手里:「不是买水了。**谢谢你指得这么细。**」她走了。
|
||||||
老鬼在你身后挠头:「施粥处?哥哥我上月听说断了米……
|
老鬼在你身后挠头:「施粥处?前儿在城里还听人说断了米……
|
||||||
哎,兴许是我记岔了日子。」
|
哎,看这一炉粥气,是兴许又续上了。城里的事,一天一个样。」
|
||||||
|
【若 wenjian_health != weak:银镯由温简当场登记为「民间
|
||||||
|
赠与 CW-0322·答谢指路·代收,处置呈档案馆裁定」。】
|
||||||
|
【若 wenjian_health = weak:银镯你代记「民间赠与 CW-0322·
|
||||||
|
代收」,回营交温简补录入册,处置呈档案馆裁定。】
|
||||||
|
|
||||||
### 路口(各线汇合,无分支)
|
### 路口(各线汇合,无分支)
|
||||||
|
|
||||||
@@ -992,91 +997,105 @@
|
|||||||
> **「那边的台子上,夜里有人点灯!」**他指的是观星台的
|
> **「那边的台子上,夜里有人点灯!」**他指的是观星台的
|
||||||
> 方向。夜禁的城,满城无灯,而有人在高台上点灯。
|
> 方向。夜禁的城,满城无灯,而有人在高台上点灯。
|
||||||
> 沈铎顺着孩子的手看过去,收回目光:「……上台看看。」】
|
> 沈铎顺着孩子的手看过去,收回目光:「……上台看看。」】
|
||||||
>
|
|
||||||
> 【D 线追加:孩子扬扬手里半块饼,补一句「粥棚的阿爷
|
|
||||||
> 还给饼吃!」——母子真的喝到了粥,你指的路是活的,
|
|
||||||
> 老鬼那句「记岔了」就此落地。】
|
|
||||||
|
|
||||||
### 余波选择点
|
### 余波选择点
|
||||||
|
|
||||||
> 【离开里坊,官道上,日头已经西斜。沈铎赶上来与你并肩,
|
> 【离开里坊,官道上,日头已经西斜——从里坊出来,
|
||||||
|
> 一个时辰的路走完了。沈铎赶上来与你并肩,
|
||||||
> 翻开册子——他是规程人,每一口水他都有数。】
|
> 翻开册子——他是规程人,每一口水他都有数。】
|
||||||
|
|
||||||
|
> 【若 `cb_val_a_water = d_redirect`:南门方向,粥棚的灶火还亮着。
|
||||||
|
> 在前头进城的岔路口,阿棠认出了那对母子:两人已从粥棚
|
||||||
|
> 回来,孩子捧着一只盛热粥的碗。你指的路,是活的。老鬼那句
|
||||||
|
> 「断了米」,只是前几日的旧闲话。】
|
||||||
|
|
||||||
**沈铎**:今天的账。(停顿)水囊的水位,我看过。
|
**沈铎**:今天的账。(停顿)水囊的水位,我看过。
|
||||||
**你想怎么记?**
|
**你想怎么记?**
|
||||||
|
|
||||||
> 【选项(CB-VAL-A-02)· 账】
|
> 【选项(CB-VAL-A-02)· 账】
|
||||||
>
|
>
|
||||||
> **A.「照实记。施水一囊(或:售水两囊 / 支出零)。
|
> **A.「照实记。给出多少、收了什么,都按实数。记在我名下。」**
|
||||||
> 记在我名下。」**
|
|
||||||
>
|
>
|
||||||
> **B.「记'路上损耗'吧。囊漏了,磕了,都写得通——**
|
> **B.「先记到『路上杂项』,备注水和镯子的实数,回营再分类。**
|
||||||
> **这点水,不值得进册子。」**
|
> **别在路边把来龙去脉都写完。」**
|
||||||
>
|
>
|
||||||
> **C.「账是队长的。你定,我签。」**
|
> **C.「账是队长的。你定,我签。」**
|
||||||
|
|
||||||
### 各选项后续(CB-VAL-A-02)
|
### 各选项后续(CB-VAL-A-02)
|
||||||
|
|
||||||
- **A**:沈铎提笔,照实记了。记完把册子合上,说了句
|
- **A**:沈铎提笔,照实记了。【若 `cb_val_a_water = a_give`:
|
||||||
|
施水一囊。】【若 `cb_val_a_water = b_cup`:施水一杯。】【若
|
||||||
|
`cb_val_a_water = c_trade`:售水两囊,得镯一支(CW-0322,
|
||||||
|
有偿征集)。】【若 `cb_val_a_water = d_redirect`:水支出零,
|
||||||
|
受赠镯一支(CW-0322,赠与代收)。】记完把册子合上,说了句
|
||||||
题外话:「我爹以前是仓管。他说,**账上干净的队,
|
题外话:「我爹以前是仓管。他说,**账上干净的队,
|
||||||
走路不回头看**。」——这是全章他第一次提家人。
|
走路不回头看**。」——这是全章他第一次提家人。顿了顿,
|
||||||
- **B**:沈铎看了你两秒,提笔,记了"损耗"。合上册子时
|
又补了半句:「就是往后再遇这种事,先说一声再给——账要
|
||||||
他说:「账平了。」顿了顿,「**心里那本的自己平。**」
|
干净,队也得知道水去哪儿了。」
|
||||||
——他配合了你的善,也把这份善的分量原样还给了你。
|
- **B**:沈铎看了你两秒,提笔,先记「待分类杂项」及每项
|
||||||
- **C**:沈铎没推辞,记了"施水一囊,采集员裁断",
|
实数,既没有把水记作漏损,也没把赠镯写成买卖;回营时
|
||||||
把笔递给你签。你签的时候他说:「推给我记的,
|
还得补分录。合上册子他说:「账平了,手续还欠一笔。」
|
||||||
和照实记的,册子上长一个样。」(停顿)「**不一样
|
- **C**:沈铎没推辞,按实数记了,把笔递给你签。你签的时候
|
||||||
的地方,你自己知道在哪儿。**」
|
他说:「推给我记的,和照实记的,册子上长一个样。」
|
||||||
|
(停顿)「**不一样的地方,你自己知道在哪儿。**」
|
||||||
|
|
||||||
### 选项设计说明(CB-VAL-A-01 / A-02)
|
### 选项设计说明(CB-VAL-A-01 / A-02)
|
||||||
|
|
||||||
- **人格信号**:价值观与人生优先级(换皮复测)。01 测
|
- **人格信号**:价值观与人生优先级(换皮复测)。01 测
|
||||||
稀缺下对陌生他者的分配姿态:无条件给予 / 定量施救 /
|
稀缺下对陌生他者的分配姿态:无条件给予 / 定量施救 /
|
||||||
交易化体面 / 资源守土+信息替代。02 测**事后对规则的
|
交易化体面 / 资源守土+信息替代。02 测**事后对规则的
|
||||||
姿态**:诚实入账 / 善意做账 / 上交裁断——价值观的
|
姿态**:当场分类 / 先记实数后补分类 / 上交裁断——价值观的
|
||||||
"做账方式"比"给钱多少"更接近长期关系里的日常。
|
"做账方式"比"给钱多少"更接近长期关系里的日常。
|
||||||
- **无正确答案论证**:01 四项是四种有尊严的活法,代价
|
- **无正确答案论证**:01 四项是四种有尊严的活法,代价
|
||||||
全部显性化:A 的代价是全队往后两天的"抿着喝"和阿棠
|
全部显性化:A 的代价是全队往后两天的"抿着喝"和阿棠
|
||||||
那句"够吗";B 守住了一切,除了你自己——"那杯水转了
|
那句"够吗";B 守住了一切,除了你自己——"那杯水转了
|
||||||
四圈"是它的账;C 最体面周全,但老鬼那句"上道啊"
|
四圈"是它的账;C 最体面周全,但老鬼那句"上道啊"
|
||||||
提醒你:体面经谁的口说出来,就沾谁的味;D 是合法的
|
提醒你:体面经谁的口说出来,就沾谁的味;D 是合法的
|
||||||
「守住资源+可靠替代」——施粥处真实在运转,收束时
|
「守住资源+可靠替代」——**选择前**粥棚就在运转
|
||||||
孩子手里那半块饼就是确认,D 测到的是价值观而非信息差。
|
(米气、人流、阿棠的亲眼,见场景),D 测到的是价值观
|
||||||
D 的代价不在正确性,在温度:四项里只有 D 的善意是
|
而非信息差;一个时辰后核账段确认母子排到热粥,
|
||||||
|
只是迟到的注脚,不承担证明责任。老鬼那句"断了米"
|
||||||
|
是三天前的旧闲话,当场被粥气推翻。
|
||||||
|
D 的代价不在可靠性,在温度:四项里只有 D 的善意是
|
||||||
凉的——水你没给,路你指了,从里坊到粥棚的那一个
|
凉的——水你没给,路你指了,从里坊到粥棚的那一个
|
||||||
时辰,是孩子趴在阿娘背上自己走完的。**滚烫但让全队
|
时辰,是孩子趴在阿娘背上自己走完的。**滚烫但让全队
|
||||||
陪绑的善意,与可靠但低功耗的善意,是同一枚价值观的
|
陪绑的善意,与可靠但低功耗的善意,是同一枚价值观的
|
||||||
两面,无高下。**
|
两面,无高下。**
|
||||||
02 三项:A 换来沈铎唯一一次提家人(信任的最高规格);
|
02 三项:A 换来沈铎唯一一次提家人,也挨了他半句
|
||||||
B 的账"平了",但他点破"心里那本自己平";C 看似推卸,
|
"先说再给";B 的账"平了",但回营得补分录;
|
||||||
他照单全收,却把"不一样的地方"还给你——三项里
|
C 看似推卸,他照单全收,却把"不一样的地方"
|
||||||
没有人说"你做得对"。
|
还给你——三项等重,没有人说"你做得对",也没有人只挨训。
|
||||||
|
账目守恒:四条 01 线的实际支出(一囊/一杯/两囊/零)
|
||||||
|
与镯子来路(有偿征集/赠与代收)各按实数派生进 02 的
|
||||||
|
所有核账文案,无一虚构损耗。
|
||||||
|
|
||||||
### 分支后果(CB-VAL-A)
|
### 分支后果(CB-VAL-A)
|
||||||
|
|
||||||
- 变量:`cb_val_a_water ∈ {a_give, b_cup, c_trade, d_redirect}`;
|
- 变量:`cb_val_a_water ∈ {a_give, b_cup, c_trade, d_redirect}`;
|
||||||
`cb_val_a_ledger ∈ {a_honest, b_smooth, c_defer}`。
|
`cb_val_a_ledger ∈ {a_honest, b_smooth, c_defer}`。
|
||||||
- 关系值:`rapport.atang`(A+1);`rapport.laogui`(C+1);
|
- 关系值:`rapport.atang`(A+1);`rapport.laogui`(C+1);
|
||||||
`rapport.shenduo`(02:A+2 / B+1 / C+1)。
|
`rapport.shenduo`(02:A+1 / B+1 / C+1)。
|
||||||
- 变量:`flag_silver_bracelet = true`(C/D,CW-0322 入册,
|
- 变量:`flag_silver_bracelet = true`(C/D,CW-0322 按「有偿
|
||||||
记忆展厅陈列);`flag_thirst_days = true`(A,此后两日
|
征集/赠与代收」分别入册,记忆展厅陈列);`flag_thirst_days =
|
||||||
饮水描写换色)。
|
true`(A 支出一囊/C 支出两囊,此后两日饮水描写换色,
|
||||||
- **收束接口**:时序为——01 处置 → 「路口」段孩子喊话
|
C 线更紧)。
|
||||||
(D 线追加粥棚确认,见正文)→ 官道上路边核账
|
- **收束接口**:时序为——01 处置 → 「路口」段孩子喊话 →
|
||||||
(CB-VAL-A-02)→ 账毕,日头西斜,黄昏登台,进 §2-5,
|
官道上路边核账(CB-VAL-A-02;D 线的粥棚确认放在核账前、
|
||||||
主线零改动(§2-5 场景本就是黄昏)。玩家感知:一件
|
一个时辰后的岔路口,见正文)→ 账毕,日头西斜,黄昏登台,
|
||||||
路上的小事,由孩子的眼睛收尾,顺手把你们送上了
|
进 §2-5,主线零改动(§2-5 场景本就是黄昏)。玩家感知:
|
||||||
|
一件路上的小事,由孩子的眼睛收尾,顺手把你们送上了
|
||||||
主线该去的地方。
|
主线该去的地方。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## B-VAL 变体 B「守陵人的药」(挂点 2:§3-2 前)
|
## B-VAL 变体 B「守陵人的药」(挂点 2:§3-2 前)
|
||||||
|
|
||||||
【场景】上午,宫墟安门内。穿过暗门(§3-1)后,台阶尽头
|
【场景】上午,宫墟安门内。穿过暗门(§3-1)后,下最后几级台阶时
|
||||||
站着个不该出现在这里的人——**守墓老人**。他不是一个人:
|
阿棠跳快了两步,落地崴了一下脚踝——她原地转了转脚腕,龇了龇
|
||||||
背上驮着个十七八岁的少年,少年一条小腿用布条和草灰
|
牙:"没事没事,走。"台阶尽头站着个不该出现在这里的人——
|
||||||
糊着,布条渗着暗色。老人把少年放下来,动作轻得不像
|
**守墓老人**。他不是一个人:背上驮着个十七八岁的少年,少年
|
||||||
他这个年纪。
|
一条小腿用布条和草灰糊着,布条渗着暗色。老人把少年放下来,
|
||||||
|
动作轻得不像他这个年纪。
|
||||||
|
|
||||||
【正文】
|
【正文】
|
||||||
|
|
||||||
@@ -1086,7 +1105,8 @@
|
|||||||
**温简**(查看伤口,低声):石像生砸的。创面糊着草灰——
|
**温简**(查看伤口,低声):石像生砸的。创面糊着草灰——
|
||||||
老法子,撑不了多久,会烂。
|
老法子,撑不了多久,会烂。
|
||||||
|
|
||||||
**沈铎**(打开物资箱,停住):外伤药,**一份**。
|
**沈铎**(打开物资箱,停住):外伤药一份——清创的药粉与
|
||||||
|
止痛的丸剂合装为一「份」。
|
||||||
(抬头)规程:这是撤离日的战备药。任何人在撤离日之前
|
(抬头)规程:这是撤离日的战备药。任何人在撤离日之前
|
||||||
动用,需采集员裁断。
|
动用,需采集员裁断。
|
||||||
|
|
||||||
@@ -1104,7 +1124,7 @@
|
|||||||
> 万一。清创,上药,我来搭手。」**
|
> 万一。清创,上药,我来搭手。」**
|
||||||
>
|
>
|
||||||
> **B.「药动不了。(对老人)城里有大夫——我画路,再给您
|
> **B.「药动不了。(对老人)城里有大夫——我画路,再给您
|
||||||
> 备一份金饼,连夜送去,来得及。」**
|
> 备一份金饼,这就动身送去,来得及。」**
|
||||||
> (守战备,用替代方案偿:钱和路,是我们能给的不伤筋
|
> (守战备,用替代方案偿:钱和路,是我们能给的不伤筋
|
||||||
> 动骨的东西)
|
> 动骨的东西)
|
||||||
>
|
>
|
||||||
@@ -1125,25 +1145,28 @@
|
|||||||
「救人」两个字的时候,笔锋比平时重。
|
「救人」两个字的时候,笔锋比平时重。
|
||||||
- **B**:老人没恼。他点点头:「药有主,理是这个理。」
|
- **B**:老人没恼。他点点头:「药有主,理是这个理。」
|
||||||
他弯腰去背少年,少年却回头,解下自己腰里挂着的一
|
他弯腰去背少年,少年却回头,解下自己腰里挂着的一
|
||||||
小包草药,轻轻放在阿棠脚边(她白日里采集时崴过一下,
|
小包草药,轻轻放在阿棠脚边(她今早下台阶崴的那一下,
|
||||||
谁都没在意,他看出来了):「阿姐,这个……敷上,凉。」
|
谁都没在意,他看出来了):「阿姐,这个……敷上,凉。」
|
||||||
老人驮着他往城里去——水门里的药铺,半个时辰的路,
|
老人驮着他这就动身往城里去——水门里的药铺,半个时辰的
|
||||||
天黑前能来回。走出十几步,少年趴在老人背上,忽然
|
路,天黑前能来回。走出十几步,少年趴在老人背上,忽然
|
||||||
回头,冲台基的方向努嘴,把一句话喊给阿棠:「阿姐!
|
回头,冲台基的方向努嘴,把一句话喊给阿棠:「阿姐!
|
||||||
上台别走正台阶——**背阴那面,排水沟边上,有我们的路!**」
|
上台别走正台阶——**背阴那面,排水沟边上,有我们的路!**」
|
||||||
老鬼低头看着那包草药,半天没吭声,收摊似的咂了咂嘴:
|
老鬼低头看着那包草药,半天没吭声,收摊似的咂了咂嘴:
|
||||||
「哥哥我活这么大,**头一回觉得穷**。」
|
「哥哥我活这么大,**头一回觉得穷**。」
|
||||||
- **C**:拆瓶,分药,各用各的。少年的创面清理上药,
|
- **C**:拆开合装,清创粉给了少年,止痛丸留在队里。上药后
|
||||||
感染这一关过了;止痛的留在队里。他被安置在台阶边的
|
温简查看创面,只说了一句:「清创及时,这片创面问题不大。」
|
||||||
阴凉里等你们收工,疼劲上来时压着声哼,老人给他扇风,
|
——不打包票。少年被安置在台阶边的阴凉里等你们收工,
|
||||||
一下,一下。没人说你的分配不好。也没人再提它。
|
疼劲上来时压着声哼,老人给他扇风,一下,一下。没人说
|
||||||
- **D**:老人捧着药瓶,看了你很久很久。然后他做了
|
你的分配不好。也没人再提它。
|
||||||
一件全队都没想到的事:他只用了一半,仔仔细细
|
- **D**:老人捧着药瓶,看了你很久很久。然后他做了一件
|
||||||
封好剩下的一半,双手还给你:「**药是救命的东西。
|
全队都没想到的事:他只用了一半,仔仔细细封好剩下的
|
||||||
救命的东西,不能断在你们手里。**」(顿)「娃他爹
|
一半,双手还给你:「**药是救命的东西。救命的东西,
|
||||||
常说,活人比死人难治——你们这些顾活人的,药
|
不能断在你们手里。**」(顿)「娃他爹常说,活人比死人
|
||||||
得留着。」阿棠在旁边,眼圈忽然红了,扭头去看
|
难治——你们这些顾活人的,药得留着。」阿棠在旁边,
|
||||||
祭祀坑。
|
眼圈忽然红了,扭头去看祭祀坑。沈铎在册子上记:「战备
|
||||||
|
药半份,交守陵人保管——返还。」记完他抬眼看你,只说
|
||||||
|
了一句:「这次他只用了一半。下不为例——这种赌,队里
|
||||||
|
赌不起第二次。」
|
||||||
|
|
||||||
### 收束(各线汇合)
|
### 收束(各线汇合)
|
||||||
|
|
||||||
@@ -1178,11 +1201,13 @@
|
|||||||
收获却是老鬼那句"头一回觉得穷"——剧本用全队最市侩
|
收获却是老鬼那句"头一回觉得穷"——剧本用全队最市侩
|
||||||
的嘴,说出守规矩的隐性价格;C 是教科书式正解,于是
|
的嘴,说出守规矩的隐性价格;C 是教科书式正解,于是
|
||||||
剧本让你听一上午的扇子声——分配的"对"与心里的账
|
剧本让你听一上午的扇子声——分配的"对"与心里的账
|
||||||
从此分开记;D 最动人,回报最丰(老人那句台词),
|
从此分开记;D 最动人,回报也最厚(老人那句台词与半瓶
|
||||||
但注意:你把裁断权交出去的那一刻,**若老人用了全部,
|
返还),但代价当场落地:沈铎的"下不为例"立了新规矩——
|
||||||
你也无权说半个字**——信任的收益与风险是同一枚硬币,
|
你把裁断权交出去的那一刻,**若老人用了全部,你也无权
|
||||||
这次只是恰好落在收益面。四后续中老人和少年对玩家
|
说半个字**;这次恰好落在收益面,队里因此把这种赌判了
|
||||||
的态度被刻意拉平:没有感激涕零,也没有怨怼,
|
死刑。信任的收益与风险是同一枚硬币。四后续中老人和
|
||||||
|
少年对玩家的
|
||||||
|
态度被刻意拉平:没有感激涕零,也没有怨怼,
|
||||||
只有"理是这个理"——末路之人对世道没有奢望,
|
只有"理是这个理"——末路之人对世道没有奢望,
|
||||||
这让一切"施恩感"无处附着。
|
这让一切"施恩感"无处附着。
|
||||||
|
|
||||||
@@ -1293,15 +1318,17 @@
|
|||||||
「隹」通「惟」,发语词;「見」通「現」;「徙」,从彳从步,
|
「隹」通「惟」,发语词;「見」通「現」;「徙」,从彳从步,
|
||||||
搬也。而「大火見」不是吉凶,是**纪日**——先秦记大事,
|
搬也。而「大火見」不是吉凶,是**纪日**——先秦记大事,
|
||||||
干支之外还要缀一颗星做"天上的公章":星象可对,日子
|
干支之外还要缀一颗星做"天上的公章":星象可对,日子
|
||||||
才作数,谁也无法事后篡改。八月大火昏见,与星图盘上
|
才作数,谁也无法事后篡改。八月大火昏见——此铭是
|
||||||
复原的苍龙心宿(C2-04),是同一片天。
|
§2-04 支线时点的**先行线索**:玩家此刻尚未见过星图盘
|
||||||
|
(§2-5 才初见),本支线一律不得以"已解过星图"的口吻
|
||||||
|
说话,谜面只立"星象纪时"一层,星图之约由温简的
|
||||||
|
解题后台词指向未来。
|
||||||
|
|
||||||
### 三档难度提示
|
### 三档难度提示
|
||||||
|
|
||||||
1. **一档(拆字)**:「读铭文,先找动词。'王'下令做的那个
|
1. **一档(拆字)**:「读铭文,先找动词。'王'下令做的那个
|
||||||
动作,落在哪个字上?——'徙',从彳从步。至于'大火',
|
动作,落在哪个字上?——'徙',从彳从步。至于'大火',
|
||||||
别往灶火上想。想想你在星图盘上,刚把什么补回苍龙的
|
别往灶火上想——天上的『火』,是一颗星的名字。」
|
||||||
心口。」
|
|
||||||
2. **二档(给制度)**:「先秦人记大事,怕后世抵赖,干支
|
2. **二档(给制度)**:「先秦人记大事,怕后世抵赖,干支
|
||||||
记日之外,还要缀一颗星做印证——星象是**天上的公章**,
|
记日之外,还要缀一颗星做印证——星象是**天上的公章**,
|
||||||
谁也没法私改。'见'通'现'。八月,大火星黄昏见于南天——
|
谁也没法私改。'见'通'现'。八月,大火星黄昏见于南天——
|
||||||
@@ -1313,10 +1340,11 @@
|
|||||||
### 解题后正文
|
### 解题后正文
|
||||||
|
|
||||||
**温简**(把铭文描进记录板,越描越慢):用星象给日子
|
**温简**(把铭文描进记录板,越描越慢):用星象给日子
|
||||||
盖章……(抬头)你们发现没有——这和星图盘是**一套思路**。
|
盖章……(抬头)你们发现没有——他们看星,不是看一回两回。
|
||||||
「火中,而后城安」,等的是星;这段铭文,记的也是星。
|
**这种记法,官署里一定还配着看星的台、量星的盘。**
|
||||||
**他们不是在等一个日子,是在等一个天象。天象到了,
|
前头那座观星台……上去,说不定就有他们量「大火」的东西。
|
||||||
事就要做。**
|
(顿)他们不是在等一个日子,是在等一个天象。天象到了,
|
||||||
|
事就要做。
|
||||||
|
|
||||||
**阿棠**(抱着手臂,声音发紧):那"事"……就是把城墙
|
**阿棠**(抱着手臂,声音发紧):那"事"……就是把城墙
|
||||||
拆了搬走?好端端的城,拆墙干什么?
|
拆了搬走?好端端的城,拆墙干什么?
|
||||||
@@ -1342,7 +1370,9 @@
|
|||||||
### 分支后果(CB-CUL-A)
|
### 分支后果(CB-CUL-A)
|
||||||
|
|
||||||
- 变量:`cb_cul_a_entry ∈ {enter, skip}`;
|
- 变量:`cb_cul_a_entry ∈ {enter, skip}`;
|
||||||
`cb_cul_a_hints_used ∈ {0,1,2,3}`;`cb_cul_a_solved ∈ {true,false}`。
|
`cb_cul_a_hints_used ∈ {0,1,2,3}`;
|
||||||
|
`cb_cul_a_solved ∈ {true,partial,false,not_attempted}`
|
||||||
|
(两问分别记正确性;跳过入口时不作解题力判定)。
|
||||||
- 变量:`flag_wall_relocation = true`(进入者必置,
|
- 变量:`flag_wall_relocation = true`(进入者必置,
|
||||||
「徙南垣」铭文情报,Act 2 真相链素材)。
|
「徙南垣」铭文情报,Act 2 真相链素材)。
|
||||||
- **收束接口**(解谜线):半小时到,沈铎封道拍照。
|
- **收束接口**(解谜线):半小时到,沈铎封道拍照。
|
||||||
@@ -1351,7 +1381,7 @@
|
|||||||
主线零改动。skip 线有独立收束(见上节,绕行抵达,
|
主线零改动。skip 线有独立收束(见上节,绕行抵达,
|
||||||
不共用"爬出断口"动作),两线同一时刻登台。
|
不共用"爬出断口"动作),两线同一时刻登台。
|
||||||
玩家感知:路上塌了个口子,下去看了一眼
|
玩家感知:路上塌了个口子,下去看了一眼
|
||||||
千年前的字,上来正好赶上看星星——解谜支线是
|
老祖宗留下的字,上来正好赶上看星星——解谜支线是
|
||||||
主线星象悬念的**注脚**。
|
主线星象悬念的**注脚**。
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -1359,10 +1389,8 @@
|
|||||||
## B-CUL 变体 B「俑道」(挂点 2:§3-2 前)
|
## B-CUL 变体 B「俑道」(挂点 2:§3-2 前)
|
||||||
|
|
||||||
【场景】上午,宫墟安门内,下沉庭院侧壁。一道被新夯土
|
【场景】上午,宫墟安门内,下沉庭院侧壁。一道被新夯土
|
||||||
封了半幅的**俑道**(§3-1「有人修过路」的直接延续):
|
封了半幅的**俑道**:数百陶俑列队其间,前排九俑,比其余的
|
||||||
数百陶俑列队其间,前排九俑,比其余的高半头,胸口
|
高半头,胸口各刻一字。你们的探查路线,正从俑道口经过。
|
||||||
各刻一字。守陵人的窄阶(若 B-VAL-B 已触发)或你们的
|
|
||||||
探查路线,正从俑道口经过。
|
|
||||||
|
|
||||||
【正文】
|
【正文】
|
||||||
|
|
||||||
@@ -1378,6 +1406,9 @@
|
|||||||
### 谜面(CB-CUL-B-01)
|
### 谜面(CB-CUL-B-01)
|
||||||
|
|
||||||
前排九俑胸口刻字:**文、武、成、康、昭、穆、共、懿、孝**。
|
前排九俑胸口刻字:**文、武、成、康、昭、穆、共、懿、孝**。
|
||||||
|
九俑虽一字排开,站位却有讲究:单数位(一、三、五、七、九)
|
||||||
|
的俑微微偏**左**,双数位(二、四、六、八)微微偏**右**——
|
||||||
|
从俑道深处望出来,九位先王左右交错,站成隐约的两列。
|
||||||
|
|
||||||
谜面任务(选项式,两问):
|
谜面任务(选项式,两问):
|
||||||
|
|
||||||
@@ -1395,21 +1426,22 @@
|
|||||||
**谜底**:第一问 B,第二问 B。
|
**谜底**:第一问 B,第二问 B。
|
||||||
《谥法》:经纬天地曰文,克定祸乱曰武,安民立政曰成,
|
《谥法》:经纬天地曰文,克定祸乱曰武,安民立政曰成,
|
||||||
温柔圣善曰康……一字之谥,盖棺论定——九俑所刻,
|
温柔圣善曰康……一字之谥,盖棺论定——九俑所刻,
|
||||||
是苍梧九代先王的谥号。而「昭」「穆」并见,点出俑道的
|
是苍梧九代先王的谥号。而单凭「昭」「穆」两字并见,
|
||||||
排法:宗庙之制,**左昭右穆,父为昭则子为穆,隔代相望**——
|
**定不了排法**(它们也是谥法用字)——定它的是**站位**:
|
||||||
这条俑道不是随便列队,是按**宗庙昭穆之序**排的。
|
单数代偏左、双数代偏右,正合宗庙之制**左昭右穆、
|
||||||
苍梧人把宗庙,修进了陵里。
|
父昭子穆、隔代同侧相望**的列法。这条俑道不是随便列队,
|
||||||
|
是按**宗庙昭穆之序**排的。苍梧人把宗庙,修进了陵里。
|
||||||
|
|
||||||
### 三档难度提示
|
### 三档难度提示
|
||||||
|
|
||||||
1. **一档(找规律)**:「九个字,一字一人,像判词。
|
1. **一档(找规律)**:「九个字,一字一人,像判词。
|
||||||
什么制度,敢用一个字给一位王的一生盖棺?再想:
|
什么制度,敢用一个字给一位王的一生盖棺?再退两步
|
||||||
'昭''穆'两个字埋在'文武功成'堆里,不觉得它们
|
看站位——单数位偏左,双数位偏右,左右交错成两列。
|
||||||
和别的不太一样吗?」
|
这个左右,是随便站的吗?」
|
||||||
2. **二档(给制度)**:「《谥法》有言:经纬天地曰文,
|
2. **二档(给制度)**:「《谥法》有言:经纬天地曰文,
|
||||||
克定祸乱曰武。这九个字,都是谥法里的字。至于
|
克定祸乱曰武。这九个字,都是谥法里的字。至于
|
||||||
昭穆——庙制:左昭右穆,父昭子穆,孙又同祖。
|
昭穆——庙制:左昭右穆,父昭子穆,孙又同祖。
|
||||||
它们不是尊号,是**排位的坐标**。」
|
对照站位想想:它们不是尊号,是**排位的坐标**。」
|
||||||
3. **三档(给答案)**:「九俑刻的是九代先王的谥号;
|
3. **三档(给答案)**:「九俑刻的是九代先王的谥号;
|
||||||
'昭''穆'标的是宗庙昭穆之序——这条俑道,是按
|
'昭''穆'标的是宗庙昭穆之序——这条俑道,是按
|
||||||
宗庙的排法修的。答案:谥号;世代坐标。」
|
宗庙的排法修的。答案:谥号;世代坐标。」
|
||||||
@@ -1441,12 +1473,19 @@
|
|||||||
其二,本支线**不设 skip 前置**——俑道在必经之路上,
|
其二,本支线**不设 skip 前置**——俑道在必经之路上,
|
||||||
但沈铎的「能读就读,读多少是多少」与提示机制共同
|
但沈铎的「能读就读,读多少是多少」与提示机制共同
|
||||||
兜底,不强迫任何玩家在谜面前停留超过其意愿
|
兜底,不强迫任何玩家在谜面前停留超过其意愿
|
||||||
(可随时点"上台基"离开,谜面按提示用尽处理)。
|
(可随时点"上台基"离开;**主动离场记
|
||||||
|
`cb_cul_b_left = true`,标注为兴趣度信号,不与
|
||||||
|
「提示用尽仍作答」混同**——后者按 0 档解谜计分,
|
||||||
|
前者不参与解题力判定)。
|
||||||
|
|
||||||
### 分支后果(CB-CUL-B)
|
### 分支后果(CB-CUL-B)
|
||||||
|
|
||||||
- 变量:`cb_cul_b_hints_used ∈ {0,1,2,3}`;
|
- 变量:`cb_cul_b_hints_used ∈ {0,1,2,3}`;
|
||||||
`cb_cul_b_solved ∈ {true,false}`。
|
`cb_cul_b_solved ∈ {true, partial, false, not_attempted}`
|
||||||
|
(两问分计:全对 true/一对错 partial/全错 false,
|
||||||
|
与 B-CUL-A 同规格,不合并计分);
|
||||||
|
`cb_cul_b_left ∈ {true}`(主动离场,兴趣度信号,
|
||||||
|
不作解题力判定)。
|
||||||
- 变量:`flag_tenth_king = true`(「第十位先王」悬念,
|
- 变量:`flag_tenth_king = true`(「第十位先王」悬念,
|
||||||
Act 2/3 真相链素材)。
|
Act 2/3 真相链素材)。
|
||||||
- **收束接口**:俑道尽头有侧阶直通台基顶(俑道本就是
|
- **收束接口**:俑道尽头有侧阶直通台基顶(俑道本就是
|
||||||
@@ -1469,7 +1508,7 @@
|
|||||||
| B-ATT | 依恋类型 | A「过时不候」 | 挂点 1(§1-8 路由 → §2-4 后) | CB-ATT-A-01/02 | 6–8 min | 侧阶登台 → §2-5 |
|
| B-ATT | 依恋类型 | A「过时不候」 | 挂点 1(§1-8 路由 → §2-4 后) | CB-ATT-A-01/02 | 6–8 min | 侧阶登台 → §2-5 |
|
||||||
| B-ATT | 依恋类型 | B「静音」 | 挂点 2(§2-8 路由 → §3-2 前) | CB-ATT-B-01 | 5–7 min | 27 号坑登记 → §3-2 |
|
| B-ATT | 依恋类型 | B「静音」 | 挂点 2(§2-8 路由 → §3-2 前) | CB-ATT-B-01 | 5–7 min | 27 号坑登记 → §3-2 |
|
||||||
| B-CON | 冲突处理风格 | A「水囊」 | 挂点 1(§1-8 路由 → §2-4 后) | CB-CON-A-01/02 | 6–8 min | 草市散场 → §2-5 |
|
| B-CON | 冲突处理风格 | A「水囊」 | 挂点 1(§1-8 路由 → §2-4 后) | CB-CON-A-01/02 | 6–8 min | 草市散场 → §2-5 |
|
||||||
| B-CON | 冲突处理风格 | B「官牙」 | 挂点 2(§2-8 路由 → §3-2 前) | CB-CON-B-01/02 | 6–8 min | 暗门口评理 → §3-2 |
|
| B-CON | 冲突处理风格 | B「官牙」 | 挂点 2(§2-8 路由 → §3-2 前) | CB-CON-B-01/02 | 6–8 min | 门内庭院评理 → §3-2 |
|
||||||
| B-EMO | 情绪稳定性 | A「沙暴」 | 挂点 1(§1-8 路由 → §2-4 后) | CB-EMO-A-01/02(计时) | 6–8 min | 风止露阶 → §2-5 |
|
| B-EMO | 情绪稳定性 | A「沙暴」 | 挂点 1(§1-8 路由 → §2-4 后) | CB-EMO-A-01/02(计时) | 6–8 min | 风止露阶 → §2-5 |
|
||||||
| B-EMO | 情绪稳定性 | B「压板」 | 挂点 2(§2-8 路由 → §3-2 前) | CB-EMO-B-01/02(01 计时) | 6–8 min | 范纹铺垫 → §3-2 |
|
| B-EMO | 情绪稳定性 | B「压板」 | 挂点 2(§2-8 路由 → §3-2 前) | CB-EMO-B-01/02(01 计时) | 6–8 min | 范纹铺垫 → §3-2 |
|
||||||
| B-VAL | 价值观 | A「半袋水」 | 挂点 1(§1-8 路由 → §2-4 后) | CB-VAL-A-01/02 | 6–8 min | 童言指路 → §2-5 |
|
| B-VAL | 价值观 | A「半袋水」 | 挂点 1(§1-8 路由 → §2-4 后) | CB-VAL-A-01/02 | 6–8 min | 童言指路 → §2-5 |
|
||||||
@@ -1499,19 +1538,19 @@
|
|||||||
| `cb_att_b_boundary` | a_go/b_wait/c_proxy | CB-ATT-B-01 | 依恋维度;Act 2 阿棠线(att_seen/att_trusted/att_owed) |
|
| `cb_att_b_boundary` | a_go/b_wait/c_proxy | CB-ATT-B-01 | 依恋维度;Act 2 阿棠线(att_seen/att_trusted/att_owed) |
|
||||||
| `flag_pit27_figurines` | bool | B-ATT-B | §3-2 温简台词换色 |
|
| `flag_pit27_figurines` | bool | B-ATT-B | §3-2 温简台词换色 |
|
||||||
| `cb_con_a_ruling` / `cb_con_a_aftermath` | a_settle/b_principle/c_invest/d_document;a_comfort/b_mirror/c_leave | CB-CON-A-01/02 | 冲突风格维度 |
|
| `cb_con_a_ruling` / `cb_con_a_aftermath` | a_settle/b_principle/c_invest/d_document;a_comfort/b_mirror/c_leave | CB-CON-A-01/02 | 冲突风格维度 |
|
||||||
| `market_warmth` / `flag_water_intel` / `flag_shiling_noticed` | normal/cold;bool;bool | CB-CON-A-01 | 市集氛围;§3-8 佐证;Act 2 伏笔 |
|
| `market_warmth` / `flag_water_intel` / `flag_shiling_noticed` | normal/cold;bool;bool | CB-CON-A-01 | 市集氛围;Act 2「那一天」佐证;Act 2 伏笔 |
|
||||||
| `cb_con_b_ruling` / `cb_con_b_channel` | a_void/b_defer/c_reprice/d_document;a_keep/b_report/c_cut | CB-CON-B-01/02 | 冲突风格维度;Act 2 暗线 |
|
| `cb_con_b_ruling` / `cb_con_b_channel` | a_void/b_defer/c_reprice/d_document;a_keep/b_report/c_cut | CB-CON-B-01/02 | 冲突风格维度;Act 2 暗线 |
|
||||||
| `flag_palace_crates` / `flag_fish_channel` | bool | CB-CON-B-01/02 | §3-2 台基箱痕应验换色(仅未来场景);Act 2 入口 |
|
| `flag_palace_crates` / `flag_fish_channel` | bool | CB-CON-B-01/02 | §3-2 台基箱痕应验换色(仅未来场景);Act 2 入口 |
|
||||||
| `cb_emo_a_shelter` / `cb_emo_a_cry` / `cb_emo_a_rt1` / `cb_emo_a_rt2` | a_house/b_arch/c_huddle/follow;a_rush/b_hold/c_tether;毫秒 | CB-EMO-A-01/02 | 情绪稳定性维度(行为数据) |
|
| `cb_emo_a_shelter` / `cb_emo_a_cry` / `cb_emo_a_rt1` / `cb_emo_a_rt2` | a_count/b_map/c_shelter/follow;a_look/b_hold/c_call;毫秒 | CB-EMO-A-01/02 | 情绪稳定性维度(行为数据) |
|
||||||
| `cb_emo_b_plate` / `cb_emo_b_vent` / `cb_emo_b_rt1` / `cb_emo_b_rt2` | a_jump/b_swap/c_dismantle/self_rescue;a_rubbing/b_mark/c_wedge;毫秒 | CB-EMO-B-01/02 | 情绪稳定性维度 |
|
| `cb_emo_b_plate` / `cb_emo_b_vent` / `cb_emo_b_rt1` / `cb_emo_b_rt2` | a_alert/b_relay/c_record/self_rescue;a_record/b_mark/c_leave;毫秒 | CB-EMO-B-01/02 | 情绪稳定性维度 |
|
||||||
| `flag_mould_rubbing` / `flag_sealed_vault` | bool | CB-EMO-B-02 | §3-2 换色;Act 2 钩子 |
|
| `flag_mould_rubbing` / `flag_sealed_vault` | bool | CB-EMO-B-02 | §3-2 换色;Act 2 钩子 |
|
||||||
| `cb_val_a_water` / `cb_val_a_ledger` | a_give/b_cup/c_trade/d_redirect;a_honest/b_smooth/c_defer | CB-VAL-A-01/02 | 价值观维度 |
|
| `cb_val_a_water` / `cb_val_a_ledger` | a_give/b_cup/c_trade/d_redirect;a_honest/b_smooth/c_defer | CB-VAL-A-01/02 | 价值观维度 |
|
||||||
| `flag_silver_bracelet` / `flag_thirst_days` | bool | CB-VAL-A-01 | 记忆展厅;饮水描写换色 |
|
| `flag_silver_bracelet` / `flag_thirst_days` | bool | CB-VAL-A-01 | 记忆展厅(C 征集/D 赠与代收);饮水描写换色(A 一囊/C 两囊) |
|
||||||
| `cb_val_b_medicine` | a_use/b_refer/c_split/d_delegate | CB-VAL-B-01 | 价值观维度 |
|
| `cb_val_b_medicine` | a_use/b_refer/c_split/d_delegate | CB-VAL-B-01 | 价值观维度 |
|
||||||
| `flag_herb_poultice` / `flag_hidden_stairs` / `rapport.shouling` | bool;bool;bool | CB-VAL-B | 阿棠线换色;§3-2 换色;Act 2 古代侧 |
|
| `flag_herb_poultice` / `flag_hidden_stairs` / `rapport.shouling` | bool;bool;bool | CB-VAL-B | 阿棠线换色;§3-2 换色;Act 2 古代侧 |
|
||||||
| `cb_cul_a_entry` / `cb_cul_a_hints_used` / `cb_cul_a_solved` | enter/skip;0–3;bool | CB-CUL-A-00/01 | 文化底蕴维度(兴趣度+解题力) |
|
| `cb_cul_a_entry` / `cb_cul_a_hints_used` / `cb_cul_a_solved` | enter/skip;0–3;true/partial/false/not_attempted(两问分计) | CB-CUL-A-00/01 | 文化底蕴维度(兴趣度+解题力) |
|
||||||
| `flag_wall_relocation` / `flag_tenth_king` | bool | B-CUL-A/B | Act 2/3 真相链素材 |
|
| `flag_wall_relocation` / `flag_tenth_king` | bool | B-CUL-A/B | Act 2/3 真相链素材 |
|
||||||
| `cb_cul_b_hints_used` / `cb_cul_b_solved` | 0–3;bool | CB-CUL-B-01 | 文化底蕴维度 |
|
| `cb_cul_b_hints_used` / `cb_cul_b_solved` / `cb_cul_b_left` | 0–3;true/partial/false/not_attempted(两问分计);bool(主动离场≠提示用尽) | CB-CUL-B-01 | 文化底蕴维度(解题力);兴趣度信号 |
|
||||||
|
|
||||||
## 写作纪律自查(对 act1.md 附录 C 同标准)
|
## 写作纪律自查(对 act1.md 附录 C 同标准)
|
||||||
|
|
||||||
@@ -1525,13 +1564,25 @@
|
|||||||
- **不致命**:全册无死亡、重伤、任务失败;代价仅落在关系、
|
- **不致命**:全册无死亡、重伤、任务失败;代价仅落在关系、
|
||||||
信息、时间、心绪。
|
信息、时间、心绪。
|
||||||
- **合规(ADR-0001)**:主角方全程记录者——墓道不取一物
|
- **合规(ADR-0001)**:主角方全程记录者——墓道不取一物
|
||||||
(石函不开)、银镯走"有偿征集入册"、对老鬼/行商/牙人的
|
(石函不开)、银镯按性质入册(C 线「有偿征集」/D 线
|
||||||
灰色渠道均设"上交/断线"出口且无牟利正向收益;动作设计
|
「赠与代收、处置呈档案馆裁定」)、对老鬼/行商/牙人的
|
||||||
无危险示范(拓纹人不进缝、风暴探身必系绳、排险走规程)。
|
灰色渠道均设"上交/断线"出口且无牟利正向收益;**无危险
|
||||||
|
动作示范**:全册不进缝、不探臂、不换脚、不楔顶、不拆板、
|
||||||
|
不以身体抵构件——险情现场的处置只有撤离、安全距离观察
|
||||||
|
与记录(B-EMO-A 风暴两问均在警戒线内;B-EMO-B-01
|
||||||
|
将排险交给受训人员,B-EMO-B-02 在安全线内拍摄),且无任何
|
||||||
|
"处置规范/流程正确"式的专业背书。
|
||||||
- **三通道一致**:支线差异收敛于变量与换色句;收束接口
|
- **三通道一致**:支线差异收敛于变量与换色句;收束接口
|
||||||
保证主线事件序列对所有玩家不变;V 类讲述点零新增,
|
保证主线事件序列对所有玩家不变;V 类讲述点零新增,
|
||||||
不挤占每章 1–2 个讲述点的节奏预算。
|
不挤占每章 1–2 个讲述点的节奏预算。
|
||||||
|
- **时序对齐(act1.md 附录 D)**:B-CON-B 场景在安门内
|
||||||
|
(§3-1 之后),不再出现"暗门外相遇、再进暗门"的倒退;
|
||||||
|
B-VAL-A 的粥棚确认发生在选择后一个时辰的岔路口,
|
||||||
|
不夹在母子离场十几步后;B-CUL-A 只作星图谜题的先行
|
||||||
|
线索,不以已解过 §2-5 星图的口吻说话;B-CUL-B 不再
|
||||||
|
引用同挂点互斥的 B-VAL-B 状态。
|
||||||
- **主线变量零越界**:支线只消费已发生的主线变量
|
- **主线变量零越界**:支线只消费已发生的主线变量
|
||||||
(如 `wenjian_health`、C2-05 同行约定),不引用触发点
|
(如 `wenjian_health`),不引用触发点之后的任何主线状态
|
||||||
之后的任何主线状态(B-VAL-B 的"战备药"设定即为此改写,
|
(B-VAL-B 的"战备药"设定即为此改写,不依赖 §3-4 才有的
|
||||||
不依赖 §3-4 才有的 `atang_injury`)。
|
`atang_injury`;B-VAL-B 线阿棠的轻微崴脚在本支线场景内
|
||||||
|
自含前置动作)。
|
||||||
|
|||||||
@@ -0,0 +1,216 @@
|
|||||||
|
# 《苍梧疑冢》修订后独立评审
|
||||||
|
|
||||||
|
日期:2026-10-03
|
||||||
|
|
||||||
|
## 范围与口径
|
||||||
|
|
||||||
|
完整审读 `story-framework.md`、`act1.md`、`adaptive-branches.md`,并确认三份全文与 `review-bundle.md` 一致。另查 ADR-0001、ADR-0004 和 `design/adaptive-insertion.md`,仅用于识别规则冲突。不改剧本;不把未上线支线当作已运行功能;未做引擎、数据存储或实际 UI 验收。
|
||||||
|
|
||||||
|
“未闭环”指本次要求检查的目标在现稿仍未成立;“新发现”指额外找到的具体矛盾,不声称知道它首次出现于哪轮修订。P1 为制作前必须解决的承诺、合规边界、不可执行路径及核心测量问题;P2 为明确连续性或规格缺口。合规判断限于内容风险,不替代法律意见;“无危险动作示范”按本次评审明确提出的红线验收。
|
||||||
|
|
||||||
|
## 结论总览
|
||||||
|
|
||||||
|
| 用户检查项 | 结论 |
|
||||||
|
|---|---|
|
||||||
|
| 1. 沉默合法 | 开门后果已闭环;整套台词、变体与跨场景承诺未完全闭环 |
|
||||||
|
| 2. 记忆私密 | NPC 不看见、不复述幻境内容已闭环;“不登记入档”与测量用途边界未闭环 |
|
||||||
|
| 3. 老人四档等价 | 三项关键情报已闭环;耗时、劳动和带路服务等价未闭环 |
|
||||||
|
| 4. 温简伤病贯穿 | 留营、通讯、篝火姿态局部已闭环;退烧时间、归队、塌方弱态及后续肩伤未闭环 |
|
||||||
|
| 5. 支线时序 | B-VAL-B 的异地收束、B-CON-B 的未来回写已闭环;B-VAL-A、B-CON-B 开场及 B-CUL-A 仍有不可能顺序 |
|
||||||
|
| 6. 社会赞许性 | 未闭环;事后写出代价,不等于作答时无显著正确方向 |
|
||||||
|
| 7. 合规表述 | 记录/保护的主线立场成立;危险动作示范红线未闭环,数据告知另有缺口 |
|
||||||
|
| 8. 死路、变量、测量 | 有新发现:沙暴组合路径无锚点、账目不守恒、互斥支线互相引用、知识谜题依据不足等 |
|
||||||
|
|
||||||
|
## P1:必须先解决
|
||||||
|
|
||||||
|
### R01[对应 7,未闭环]危险动作仍具体可复现,自查结论与正文相反
|
||||||
|
|
||||||
|
证据:
|
||||||
|
|
||||||
|
- [支线册:1527](./adaptive-branches.md#L1527) 自查称:“动作设计无危险示范(拓纹人不进缝、风暴探身必系绳、排险走规程)。”
|
||||||
|
- [支线册:722](./adaptive-branches.md#L722),B-EMO-A-02-A:“你冲出去三十步,风把你摁着摩擦。”该选项没有系绳,也不是只有环境危险而无主角动作。
|
||||||
|
- [支线册:807](./adaptive-branches.md#L807),B-EMO-B-01-B:“谁体重跟她最接近?平移换脚,再慢慢卸。”后续又写“用石块把板缘楔死”。C 则写“把她原地固定住,我们拆这块板”,并展开撬杆、绳套、楔石和掀板流程。
|
||||||
|
- [支线册:852](./adaptive-branches.md#L852),B-EMO-B-02-A:“探臂抢半方纹出来”;C:“撬杆别住缝口!别住了再拓”。人不进缝不等于手臂没有夹压危险,且主角主动干预仍在位移的遗址结构。
|
||||||
|
- [主线:734](./act1.md#L734),C2-03-C:“我体重最轻,走前面探”,塌梁后获得“探法本身没错,下次用绳”的专业角色背书。[主线:1296](./act1.md#L1296) 又以身体抵住滑动器座、扑身抱鼎完成抢救。
|
||||||
|
|
||||||
|
影响:这些不是单纯描绘险境,而是选项指令、操作顺序与成功回报相连。把结局写成“无伤”“机关锈死”不能消除示范属性。
|
||||||
|
|
||||||
|
最小闭环:保留风险判断、撤离与记录抉择,但去掉换脚、楔板、拆板、探臂、身体顶构件等具体操作和“规范/正确”背书。记录可由安全距离影像、位置标注、是否等待专业排险承载。
|
||||||
|
|
||||||
|
### R02[对应 2、7,新发现问题]“不登记入档”与画像用途没有明确边界
|
||||||
|
|
||||||
|
已闭环部分:[主线:1161](./act1.md#L1161) 明确“幻境只在玩家一个人的眼里”,[主线:1231](./act1.md#L1231) 禁止 NPC “看见、复述或评论幻境内容”。队友关心脸色、手抖不构成内容泄漏。没有证据再判定旧版“队友旁观私密记忆”问题仍存在。
|
||||||
|
|
||||||
|
未闭环证据:[主线:1224](./act1.md#L1224) 馆长说“这段不登记入档。问心之室里的事,归你”;但 [主线:1256](./act1.md#L1256) 仍将本段韵律和告别行为用于情绪稳定性、依恋采样,[主线:1266](./act1.md#L1266) 仍规定保存完成方式和密度状态。
|
||||||
|
|
||||||
|
影响:“不入记忆展厅”“不保留讲述文本”“不计入人格测量”是三种不同承诺。现稿没有把“不登记”限于哪一种,玩家可合理理解为本段不被系统采集。这里是剧本承诺与处理规格不一致,不能据此直接断言实际服务已经违规存储。
|
||||||
|
|
||||||
|
最小闭环:明确本段是否保存文本、是否仅不公开展示、是否参与测量;让玩家听到的承诺与真实产品行为一致。不能用馆长后来被揭露隐瞒任务,替代现实产品的隐私告知。
|
||||||
|
|
||||||
|
### R03[对应 7,新发现问题]唯一告知落点没有覆盖语音分析
|
||||||
|
|
||||||
|
[主线:69](./act1.md#L69) 唯一确认页只写:“你已知晓:剧情选择将用于为你推荐伴侣。按本心选,没有对错。”同时声明“此后全剧不再出现同类提示”。而 [ADR-0004:44](../adr/0004-data-rules-consent-immutability-replay.md#L44) 明确要求注册告知覆盖“提取语音特征用于性格分析”。
|
||||||
|
|
||||||
|
影响:如果制作只照剧本唯一告知页落地,语音讲述、文本和韵律分析的告知范围不足。若已有独立注册同意页完整覆盖,此项可由实际页面验收关闭;本剧本包本身没有给出该证据。也不能仅凭“已知晓”文案推定所有必要同意都已取得。
|
||||||
|
|
||||||
|
### R04[对应 5、6,未闭环]B-VAL-A 的 D 尚未获得可执行的可靠替代路径
|
||||||
|
|
||||||
|
[支线册:958](./adaptive-branches.md#L958) D 指向城门施粥处;[支线册:985](./adaptive-branches.md#L985) 老鬼随后说“上月听说断了米”。[支线册:990](./adaptive-branches.md#L990) 汇合写母子“刚走出十几步”,紧接着 D 追加孩子举饼,旁白确认“母子真的喝到了粥”。但 [支线册:1046](./adaptive-branches.md#L1046) 论证明确说里坊到粥棚要“一个时辰”。
|
||||||
|
|
||||||
|
影响:孩子尚未到粥棚,不能已经从该次指路取得食物并证实成功。若把饼解释为出场前已有,又不能证明本次指路可靠,还削弱母子缺水的前提。更重要的是,“可靠”的证据在玩家选择之后才出现,无法消除选择当时的真假信息差。
|
||||||
|
|
||||||
|
最小闭环:选择前给出当日仍开放、母子可安全到达的可见证据;到达确认移至确实经过一时辰后的场景,不再夹在“十几步”之后。老鬼“上月听说”的来源也需说明:小队此次入城尚不足一月。
|
||||||
|
|
||||||
|
### R05[对应 5,未闭环]B-CON-B 回写方向修好了,挂点位置仍错
|
||||||
|
|
||||||
|
已闭环:[支线册:619](./adaptive-branches.md#L619) 的 `flag_palace_crates` 已明确在未来 §3-2 台基特写兑现,不再修改已播放的 §3-1。
|
||||||
|
|
||||||
|
未闭环:[支线册:13](./adaptive-branches.md#L13) 挂点 2 固定在“§3-1 之后、§3-2 之前(安门内下沉庭院)”。[主线:1047](./act1.md#L1047) §3-1 已“穿过守墓老人指点的暗门”。但 [支线册:491](./adaptive-branches.md#L491) B-CON-B 开场仍是“宫墟暗门外。你们正准备进安门”;收束再次“进暗门,穿过庭院”。
|
||||||
|
|
||||||
|
影响:未交代返回就把队伍从门内重置到门外,再次进入。最小修复应把牙人相遇放在门内允许到达的位置,或单独重定本支线挂点并同步路由,不可只改兑现句。
|
||||||
|
|
||||||
|
### R06[对应 5、8,新发现问题]B-CUL-A 使用尚未发生的 C2-04 解谜记忆
|
||||||
|
|
||||||
|
[支线册:1224](./adaptive-branches.md#L1224) 固定挂点在 §2-4 后,收束才去 §2-5。可是 [支线册:1303](./adaptive-branches.md#L1303) 提示问:“想想你在星图盘上,刚把什么补回苍龙的心口。”[支线册:1316](./adaptive-branches.md#L1316) 温简又说“这和星图盘是一套思路”,引用“火中,而后城安”。上述星图在 [主线:783](./act1.md#L783) §2-5 才初见。
|
||||||
|
|
||||||
|
影响:正常选“下去看看”就产生先知式回忆。最小闭环是将本支线信息写成星图谜题的先行线索,不能以已解过星图的口吻说话。
|
||||||
|
|
||||||
|
### R07[对应 8,新发现问题]B-EMO-A 存在无法按文本执行的组合路径
|
||||||
|
|
||||||
|
路径:01 选 C,就地用物资箱围圈;02 选 C,要求“一头拴石柱(或:门框)”。
|
||||||
|
|
||||||
|
证据:[支线册:675](./adaptive-branches.md#L675) 与 [支线册:716](./adaptive-branches.md#L716)。石柱属于 01-B 的石坊,门框属于 01-A 的民居,01-C 从未抵达任一建筑,没有既定锚点。另外 [支线册:691](./adaptive-branches.md#L691) 01-C 已写风缓、脱出并清点物资,随后公共段又切回“风最盛的时候”。
|
||||||
|
|
||||||
|
影响:这是组合路径的叙事死路与时序倒退,不是已证实的引擎卡死。需为不同掩体配置真实可行的后续行动,或替换整段危险探查;风缓、清点应移到第二次抉择之后。
|
||||||
|
|
||||||
|
### R08[对应 6,未闭环]C3-03 仍存在明确的救人优先方向
|
||||||
|
|
||||||
|
[主线:1295](./act1.md#L1295) 已明示沈铎“一个人的力气撼不动石构件,他吼:来人”;但 B 选项仍以“阿棠那边,有沈铎”合理化先救鼎簋。玩家选择时面对的是明确求援的受困伤员与铭文载体,不是两个安全上可比的任务。[主线:1321](./act1.md#L1321) 阿棠说“选得对,那句话比我金贵”,[主线:1327](./act1.md#L1327) 沈铎称 C“处置正确”,直接提供评价标签。
|
||||||
|
|
||||||
|
影响:把文物改称“几十万人的最后一句话”增加情感重量,但没有消除紧急救人的职业伦理优先级。C 在按钮文案中兼顾两边,其温简肩伤代价仅事后揭晓;A 的文物损失、B 的骨裂、C 的肩伤也不能因作者声明就视为“损失等量”。
|
||||||
|
|
||||||
|
最小闭环:先确认受困者已经由足够救援力量稳定保护,再让玩家在两个同样正当但不可兼顾的任务间选择;删除“选得对/处置正确”式裁判语言。否则这个节点更适合测危险认知和救援判断,不宜声称已消除社会赞许性。
|
||||||
|
|
||||||
|
### R09[对应 6、8,未闭环并有新问题]C2-01 的四项不在同一个清晰的资源问题上
|
||||||
|
|
||||||
|
[主线:529](./act1.md#L529) 有“两人份”药,A 只确定“先给温简”,没有决定第二份给谁;[主线:582](./act1.md#L582) 论证却说 A“让两个健康人裸奔在瘴区”。如果温简消耗两份,应事先明示治疗剂量;否则少记了一份药。
|
||||||
|
|
||||||
|
C 按钮注释称“药剂常识上可行”,[主线:585](./act1.md#L585) 论证又称“医学上是最劣解”。D 的可见承诺是共同议事、制定长期规则,误药和老鬼抢先分配都在事后才揭晓;“无章可依,拦不住”也与已经存在的队长和采集员裁断权限不协调。
|
||||||
|
|
||||||
|
影响:A 的救病者方向仍占显著道德优势,D 的成熟程序形象也未在选择前给出具体时间风险。选项实际混入治疗与预防知识、对议事速度的猜测、对老鬼行为的预测,而不只是价值优先级。
|
||||||
|
|
||||||
|
最小闭环:先定义药的治疗/预防用途、各人所需剂量、第二份去向和延迟风险,再写选择;不要靠未提前提供的信息制造“配平”。
|
||||||
|
|
||||||
|
## P2:局部闭环与连续性问题
|
||||||
|
|
||||||
|
### R10[对应 1,部分已闭环、部分未闭环]C1-02 开门已修复,承诺仍未完全统一
|
||||||
|
|
||||||
|
已闭环:[主线:140](./act1.md#L140) 台词明确“不讲,门也开”;[主线:146](./act1.md#L146) 引导语“沉默也是回答”;[主线:163](./act1.md#L163) 沉默后确实“门开了”;分支固定进入 §1-4。不能再报告“沉默不开门”。
|
||||||
|
|
||||||
|
未闭环:上游 [主线:89](./act1.md#L89) 仍称燃料“只有一种,你的真实人生”;本点 [主线:136](./act1.md#L136) 仍要“祭品:一段真实的离别”,[主线:161](./act1.md#L161) 又称门看“故事真不真”。这些可作为隐喻,但与无须披露即可启动的机制尚缺一致解释。沉默后还无条件说“你的记忆已经收进档案馆展厅”,而 [主线:175](./act1.md#L175) 本点场景表只有四种讲述签名,没有 `silent` 的即时演出。
|
||||||
|
|
||||||
|
最小闭环:明确不提交记忆也可启动;沉默时只生成空档/留白记录,不能称收到实际记忆;补齐 silent 场景。故事框架 [框架:9](../story-framework.md#L9) 的“人生故事兑换下一段旅程”也需同步。
|
||||||
|
|
||||||
|
### R11[对应 1、2,新发现问题]C1-02 沉默后,C3-02 开场仍播放具体记忆描述
|
||||||
|
|
||||||
|
[主线:1172](./act1.md#L1172) 无条件说“那是你的记忆。C1-02 里你讲给离别之门的那一段”,“那天的光线,那个人的背影”。但 [主线:1236](./act1.md#L1236) 又规定沉默玩家仅有模糊雾景。
|
||||||
|
|
||||||
|
影响:沉默者会先被告知系统掌握了其从未讲述的私人经历,再进入通用雾景;即使 NPC 没看见,也破坏隐私可信度。应将开场也按记忆是否存在分支,而不是只改生成规则。
|
||||||
|
|
||||||
|
### R12[对应 3,信息已闭环,严格等价未闭环]C2-02 还存在时间与服务的差异
|
||||||
|
|
||||||
|
已闭环:[主线:661](./act1.md#L661) 及 [主线:696](./act1.md#L696) 明确四档都有暗门方位、开启时辰、完整谶语,不再用情报完整度奖励自责或深暴露。
|
||||||
|
|
||||||
|
未闭环:[主线:641](./act1.md#L641) 只要求沉默者“陪他编一下午草绳”,讲述者没有同等劳动与等待的演出;[主线:669](./act1.md#L669) 却声明“耗时相同”。[主线:674](./act1.md#L674) 又把亲自送达暗门与只在界碑指方向区分开,在未知陵区,带路本身就是减少寻路与风险的服务。现稿没有明确说明路程、到达时刻和安全结果如何保持相等。
|
||||||
|
|
||||||
|
最小闭环:写四档共同到达同一地点、共同等到同一时辰且无额外劳动要求;只变老人说话方式。若必须保留不同带路方式,至少明确导航保障和同点同刻汇合,否则只能声称“关键情报等量”。框架 [框架:109](../story-framework.md#L109) 仍是“情报详细度三档”,也未同步。
|
||||||
|
|
||||||
|
### R13[对应 4,未闭环]温简的健康规则没有贯穿可执行正文
|
||||||
|
|
||||||
|
已闭环:§2-3 留营、§2-4 通讯、§2-6 卧姿已有条件文本;支线册总则也提供了通讯与替代体力动作的处理规则。
|
||||||
|
|
||||||
|
未闭环证据:
|
||||||
|
|
||||||
|
- [主线:564](./act1.md#L564) B“烧到第三日才退”,紧接的 §2-3 [主线:616](./act1.md#L616) 已“服过药,烧退了”,没有交代跨到第三日或药从何来。A“次日能下床”,同日墓道默认在场也需交代。
|
||||||
|
- §2-4 仍留营,挂点 1 全程留营,§2-5 [主线:789](./act1.md#L789) 却说“出发前自己背着记录板跟来了”。这把同一次出发同时写成留营和跟队,缺少赶来汇合的事件。
|
||||||
|
- [主线:605](./act1.md#L605) 约定“§3-4 塌方戏体力描写降档”,实际 [主线:1296](./act1.md#L1296) 的扑向器座、以身体抵住,以及 C 的扑身抱鼎均无 weak 条件分支。
|
||||||
|
- C3-03-C 已肩膀“抬不起来”,后续只记变量,§3-5 至终幕没有兑现肩伤的动作限制。应至少在记录、交板或撤离分工中落一处实际影响。
|
||||||
|
- [支线册:31](./adaptive-branches.md#L31) 将 weak 括注为“C2-01 选 B/C”,主线却定义 B/C/D。以变量判断能覆盖 D,但人工制作或按选项抄写会漏 D,不能保留两套口径。
|
||||||
|
|
||||||
|
### R14[对应 5,旧顺序问题已闭环,但仍有局部残留]B-VAL-B 的 B 已不要求送医者返回带路
|
||||||
|
|
||||||
|
[支线册:1130](./adaptive-branches.md#L1130) 老人与少年去药铺前喊出背阴路;[支线册:1157](./adaptive-branches.md#L1157) 由阿棠依喊话找到入口。该路径不再依赖未来返程,挂在 §3-2 前成立。
|
||||||
|
|
||||||
|
残留:[支线册:1107](./adaptive-branches.md#L1107) 上午选择仍说“连夜送去”,后续则半个时辰路程、天黑前能回;应统一当即送医。阿棠“白日里采集时崴过一下”与 C3-03 后来的伤不同,不能判为偷读未来 `atang_injury`,但应补上这次轻微崴伤的前置动作,避免像伤情凭空出现。
|
||||||
|
|
||||||
|
### R15[对应 6,新发现问题]其他选择仍有道德标签或显著有利的后续
|
||||||
|
|
||||||
|
- B-VAL-A-02:[支线册:1010](./adaptive-branches.md#L1010) 是“照实记 / 记路上损耗 / 你定我签”。A 获队长最高规格信任并 `+2`,其余 `+1` 且被反问良心。A 没有明确代价,诚实与做假账在采集员职业里不具社会赞许性等值。
|
||||||
|
- B-ATT-A-01:C 获 [支线册:146](./adaptive-branches.md#L146)“流程正确”,成本主要藏在作者论证“把活人拆成搜索坐标”,不是实际可见的代价。不能靠这种解释否认组织型答案的正确性优势。
|
||||||
|
- B-EMO-A-02-C:[支线册:735](./adaptive-branches.md#L735) 同时获“处置规范”和“这事就过去了”;A 白受险、B 承担“万一”。“都安心一半”与演出不符。
|
||||||
|
- B-VAL-B:D [支线册:1141](./adaptive-branches.md#L1141) 治伤、退回半药、获情感肯定;A 用尽战备。未发生的“老人也可能全用”不能当作当前已呈现的代价。C 从“一瓶药”突然拆出清创和止痛两种药,又声称“感染这一关过了”,药品前提不清,混入了医学知识猜测。
|
||||||
|
- C3-05-C:[主线:1477](./act1.md#L1477) 将“不接托付”捆绑为“各走各的,谁都别为谁停下”,后续让有伤阿棠自己背绳。拒绝私人关系义务与拒绝团队救援不是同一构念,不能作为纯粹“独立型”的等体面选项。
|
||||||
|
|
||||||
|
### R16[对应 8,新发现问题]B-VAL-A 的物资和核账不能对上
|
||||||
|
|
||||||
|
[支线册:972](./adaptive-branches.md#L972) B 给了一杯水,随后却说“队里的水一两没少”。01-C 付出两囊,只有 01-A 设置 `flag_thirst_days`,未说明两囊支出为何反而不影响饮水。[支线册:1010](./adaptive-branches.md#L1010) “照实记”只给一囊、两囊、零三个模板,漏掉一杯;02-C [支线册:1026](./adaptive-branches.md#L1026) 不论前选什么都记“施水一囊”。01-D 零水支出时,02-B 还要求虚构“囊漏了”。
|
||||||
|
|
||||||
|
最小闭环:四条线分别定义实际水量、是否换得物品与登记性质,所有二次核账文案由该结果派生。D 的银镯是答谢赠与,不应与 C 一起一概登记“有偿征集”。入册本身也不自动解决是否有权代机构受领的问题。
|
||||||
|
|
||||||
|
### R17[对应 8,新发现问题]B-CON-B 的关闭渠道代价被下一选项抵销
|
||||||
|
|
||||||
|
[支线册:538](./adaptive-branches.md#L538) 01-A 对方说以后消息“一条也买不着”,[支线册:603](./adaptive-branches.md#L603) 更断言 A“永远关上了情报的门”。但 [支线册:560](./adaptive-branches.md#L560) 所有路径统一递上“城西脚店”的渠道,02-A 一律设置 `flag_fish_channel = true`。
|
||||||
|
|
||||||
|
最小闭环:要么 01-A 确实关闭并调整 02,要么允许鱼牙人反悔重开,但删去“永久损失”作为配平依据,并给出前后转变。
|
||||||
|
|
||||||
|
### R18[对应 8,新发现问题]B-CUL-B 引用同挂点互斥支线
|
||||||
|
|
||||||
|
[支线册:55](./adaptive-branches.md#L55) 每章至多一条,变体 B 都固定第 3 章挂点。可是 [支线册:1364](./adaptive-branches.md#L1364) B-CUL-B 写“守陵人的窄阶(若 B-VAL-B 已触发)”。在现有规则下,二者不能同章先后发生,此条件不可达。
|
||||||
|
|
||||||
|
另:[支线册:479](./adaptive-branches.md#L479) `flag_water_intel` 的消费方是 §3-8,但 [主线:1644](./act1.md#L1644) 终幕规定“对所有玩家一致,不做任何分支”。应补实际消费句或改索引,不能同时声称主线零改动与消费该变量。
|
||||||
|
|
||||||
|
### R19[对应 8,新发现问题]“文化底蕴”谜题的标准答案存在证据不足
|
||||||
|
|
||||||
|
- C1-05,[主线:361](./act1.md#L361):由碑额单字“安”断言“不是墓碑,而是镇城碑/安民碑”。辨字本身不充分决定碑的用途。若这是苍梧独有的制度,必须给出相应规则,否则测的是猜编剧的设定。
|
||||||
|
- C2-04,[主线:804](./act1.md#L804):外圈“完整刻着”包含心宿的七宿,随后又称心宿被磨去,提示问“缺的是哪一宿”。必须让美术和文字对齐到底是轮廓、文字标注还是星位完整。
|
||||||
|
- B-CUL-B,[支线册:1395](./adaptive-branches.md#L1395):第一问把九字全判为谥号,第二问仅凭“昭、穆”两字并见就断言实际按宗庙昭穆排位。人名/谥号用字与昭穆制度不能仅靠同字互证;目前没有左右位置、代际关系的独立证据。应补空间与世系线索,或改第二问。
|
||||||
|
|
||||||
|
这些是谜面推理充分性问题;本次没有做完整古文字、礼制或天文史专家审校,不把所有历史解释都当作已证伪。
|
||||||
|
|
||||||
|
### R20[对应 8,新发现问题]测量弱证据声明已改进,但具体记录仍会混淆不同原因
|
||||||
|
|
||||||
|
已闭环:[主线:20](./act1.md#L20) 已限定单次弱证据;C3-05/06 已明确择偶偏好仅为弱代理;支线规则也允许证据不足而非硬出结论。应保留这些收窄。
|
||||||
|
|
||||||
|
未闭环:
|
||||||
|
|
||||||
|
- [支线册:1442](./adaptive-branches.md#L1442) B-CUL-B 可随时离开,却把谜面按“提示用尽处理”。主动拒答与用三档提示仍不能解答必须区分,否则兴趣/赶路倾向会伪装成知识能力不足。
|
||||||
|
- [支线册:640](./adaptive-branches.md#L640) 将 8/10 秒决策、是否读完、超时用于情绪稳定性;未规定计时起点、背景播放、阅读速度或设备打断处理。只能先记录反应行为,不能将未读完或超时直接视作情绪不稳定。
|
||||||
|
- [支线册:924](./adaptive-branches.md#L924) 价值观支线宣称对方“无任何关系、无任何回报能力”,实际 B 用的是已经见过的守墓老人,且两变体都提供信息/道路/赠物回报。其目标应收窄为资源分配,不能当作已隔离互惠预期的陌生人利他测量。
|
||||||
|
- [主线:1266](./act1.md#L1266) `farewell_partial` 没有完整的判定与回报规格。C3-01、B-CUL-A/B 含两个作答子题,却各只给一个 `solved`,需定义一对一错、分题提示的聚合,避免数据不同而标签相同。
|
||||||
|
|
||||||
|
### R21[对应 1、8,新发现问题]自我暴露奖励仍有方向性,不能仅靠“总点数相同”证明无诱导
|
||||||
|
|
||||||
|
[主线:918](./act1.md#L918) C2-05 深暴露独得提前秘密,浅暴露没有,沉默的温简亲密线延至 Act 2。[主线:935](./act1.md#L935) 深暴露温简 `+2`,浅暴露 `+1`,沉默温简 `+1` 加沈铎 `+1`。这不是沉默不能通关,也不是沉默零奖励,不能夸大为机械处罚;但对想靠近温简或优先了解核心悬念的玩家,深暴露明确更有利,测到的深度会混入内容获取动机。
|
||||||
|
|
||||||
|
C2-02 对自责者给“被信任的厚重”,对愤怒指向他人者“没再多看你一眼”,也仍有情绪表达的社会奖惩。情报等量解决了物质诱导的一部分,并不等于测量已无赞许性偏差。
|
||||||
|
|
||||||
|
### R22[对应 8,新发现问题]世界时间、终幕安排及状态回收仍需统一
|
||||||
|
|
||||||
|
- [支线册:794](./adaptive-branches.md#L794) 与 [支线册:857](./adaptive-branches.md#L857) 在已穿越的古代现场说器物“死了上千年”“在这儿一千年了”;[支线册:1354](./adaptive-branches.md#L1354) 又称眼前铭文“千年前的字”。如真是苍梧当时已有的更古老遗存,应给设定依据,不能自动套用现代考古视角的器龄。
|
||||||
|
- [主线:1585](./act1.md#L1585) C3-06 向沈铎倾诉后得到“撤离提前”,没有日期变量,终幕仍对所有人固定“回程,定在八天后”。若前者只指撤离宫墟、后者指返现实,需要明确区分;否则个别选项对时间的承诺未兑现。
|
||||||
|
- §2-2 次晨、§2-4 次日上午、当夜篝火和玉佩之后,[主线:1024](./act1.md#L1024) 突然“第五个夜晚”,缺少跳日衔接。该缺口与温简第三日退烧应在一张绝对日历上统一,不宜各自以一句“次日”修补。
|
||||||
|
- [主线:453](./act1.md#L453) C1-06 写固定跳转 §2-1,但下文还有 §1-8 章末演出和路由;[主线:1624](./act1.md#L1624) C3-06 写直接进入报告,后面还有 §3-8 终幕。需说明这些是章末自动插播,还是应改跳转目标,避免照字面制作漏掉演出。这里不是实际引擎已跳过的断言。
|
||||||
|
|
||||||
|
### R23[包外交叉检查,新发现问题]工程设计与剧本的选皮规则冲突
|
||||||
|
|
||||||
|
[工程设计:39](../design/adaptive-insertion.md#L39) 规定“A 未用过则 A,用过则 B”,且说“挂点被占则顺延到该章另一挂点”。剧本却是 A/B 按章固定、每章只有一个挂点。
|
||||||
|
|
||||||
|
可复现条件:第 2 章插 ATT-A,第 3 章首次选中低置信价值观。按工程设计会选未用过的 VAL-A,按剧本必须 VAL-B。该矛盾会把本次所有地点与状态修订再次破坏。此项来自包外关联设计,单独列出,不当成剧本正文已执行的行为。
|
||||||
|
|
||||||
|
## 修订验收建议
|
||||||
|
|
||||||
|
1. 先关闭 R01-R09:安全红线、隐私告知、三个先后次序错误、组合死路,以及核心选择点的前置信息。
|
||||||
|
2. 建一张逐日行程表和温简/阿棠状态表,把所有分支放回唯一时间轴;不要只在总则写“需换色”。
|
||||||
|
3. 为 B-VAL-A 做 4×3 的核账文本矩阵,为 B-EMO-A 做 4×3 的掩体/探查矩阵(含超时),逐格验证动作所需道具与物资守恒。
|
||||||
|
4. 把“已闭环”的四处保留下来:C1-02 沉默开门、C3-02 NPC 内容隔离、C2-02 三项情报全给、B-VAL-B 离场前指路;B-CON-B 的未来回写也不应退回旧挂法。
|
||||||
|
5. 最后统一故事框架、正式剧本、支线册与工程设计,再生成评审包。当前评审包忠实包含母文档,问题不在合成陈旧,而在母文档彼此尚未统一。
|
||||||
+16
-12
@@ -7,7 +7,8 @@
|
|||||||
|
|
||||||
近未来,"时空档案馆"致力于采集即将湮灭的历史现场。档案馆选中玩家成为
|
近未来,"时空档案馆"致力于采集即将湮灭的历史现场。档案馆选中玩家成为
|
||||||
**口述历史采集员**:你讲述的真实人生是穿越旅程的燃料。每次任务归来,
|
**口述历史采集员**:你讲述的真实人生是穿越旅程的燃料。每次任务归来,
|
||||||
档案馆"采集你的记忆";你也可以用自己的人生故事兑换下一段旅程。
|
档案馆"采集你的记忆";你也可以用自己的人生故事兑换下一段旅程——
|
||||||
|
讲述是燃料,讲述点门前的沉默与留白同样被收下,同样是燃料(沉默合法铁律)。
|
||||||
|
|
||||||
**设定红线**:主角方永远是**记录与保护者**,不是掠夺者(合规口径,见 ADR-0001)。
|
**设定红线**:主角方永远是**记录与保护者**,不是掠夺者(合规口径,见 ADR-0001)。
|
||||||
|
|
||||||
@@ -60,15 +61,17 @@
|
|||||||
|
|
||||||
## 4. Act 1 选择点地图
|
## 4. Act 1 选择点地图
|
||||||
|
|
||||||
> 选择点 ID 规则:`C{章}-{序}`。类型:按钮式(B)/ 语音讲述点(V)。
|
> 选择点 ID 规则:`C{章}-{序}`。类型:按钮式(B)/ 语音讲述点(V)/
|
||||||
> 每章 B 类 5-7 个,V 类 1-2 个。讲述点 30 秒内必须给即时回报。
|
> 私密路径(P;C3-02 不录音、不计画像)。
|
||||||
|
> 第 1/2 章有 V 类讲述点,第 3 章以 P 类私密路径代替新增讲述;
|
||||||
|
> 讲述点与私密路径选择均在 30 秒内给即时回报。
|
||||||
|
|
||||||
### 第 1 章「登记」(现实 → 档案馆 → 启程)
|
### 第 1 章「登记」(现实 → 档案馆 → 启程)
|
||||||
|
|
||||||
| ID | 类型 | 内容(一行) | 承载维度 |
|
| ID | 类型 | 内容(一行) | 承载维度 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| C1-01 | B | 穿越者身份登记:城市/年龄/性别/职业/学历(同时是匹配硬过滤字段,见 ADR-0003) | —(字段采集) |
|
| C1-01 | B | 穿越者身份登记:城市/年龄/性别/职业/学历(同时是匹配硬过滤字段,见 ADR-0003) | —(字段采集) |
|
||||||
| C1-02 | V | **记忆钥匙·离别之门**:"以一段真实的离别祭之" | 依恋类型 #1、自我暴露深度 |
|
| C1-02 | V | **记忆钥匙·离别之门**:邀请讲述一段真实的离别(沉默亦通行,门照常开,展厅记空档页) | 依恋类型 #1、自我暴露深度 |
|
||||||
| C1-03 | B | 馆长问任务动机,四选项无对错(求知/冒险/逃避现实/有人托付) | 价值观 #1 |
|
| C1-03 | B | 馆长问任务动机,四选项无对错(求知/冒险/逃避现实/有人托付) | 价值观 #1 |
|
||||||
| C1-04 | B | 初见小队:老鬼提议"带点纪念品回来",无人表态,你接不接话 | 价值观 #2(金钱观)、冲突风格 #1 |
|
| C1-04 | B | 初见小队:老鬼提议"带点纪念品回来",无人表态,你接不接话 | 价值观 #2(金钱观)、冲突风格 #1 |
|
||||||
| C1-05 | B | 解谜线入门:残碑文字辨认(真实古文字知识,三档难度自适应) | 文化底蕴 #1 |
|
| C1-05 | B | 解谜线入门:残碑文字辨认(真实古文字知识,三档难度自适应) | 文化底蕴 #1 |
|
||||||
@@ -78,7 +81,7 @@
|
|||||||
|
|
||||||
| ID | 类型 | 内容 | 承载维度 |
|
| ID | 类型 | 内容 | 承载维度 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| C2-01 | B | 物资分配:药粉只够两人份,三人需要(老鬼主张按"贡献",阿棠主张给最弱的) | 价值观 #3、冲突风格 #2 |
|
| C2-01 | B | 物资分配:防瘴药两人份(一份=一人七日预防量;病人足量治疗需两份连服——剂量在选择前摊明),三人需要 | 价值观 #3、冲突风格 #2 |
|
||||||
| C2-02 | V | **故事换情报·守墓老人**:"讲一个你这辈子最后悔的决定" | 价值观(悔的模式)、情绪稳定性 #1 |
|
| C2-02 | V | **故事换情报·守墓老人**:"讲一个你这辈子最后悔的决定" | 价值观(悔的模式)、情绪稳定性 #1 |
|
||||||
| C2-03 | B | 路线分歧:沈铎的稳妥路线 vs 阿棠的直觉捷径,队里目光都落在你身上 | 风险偏好 #2、冲突风格 #3 |
|
| C2-03 | B | 路线分歧:沈铎的稳妥路线 vs 阿棠的直觉捷径,队里目光都落在你身上 | 风险偏好 #2、冲突风格 #3 |
|
||||||
| C2-04 | B | 解谜线:星象图复原(真实天文知识) | 文化底蕴 #2 |
|
| C2-04 | B | 解谜线:星象图复原(真实天文知识) | 文化底蕴 #2 |
|
||||||
@@ -90,8 +93,8 @@
|
|||||||
| ID | 类型 | 内容 | 承载维度 |
|
| ID | 类型 | 内容 | 承载维度 |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| C3-01 | B | 解谜线:礼制还原(以出土器物推断仪式流程) | 文化底蕴 #3 |
|
| C3-01 | B | 解谜线:礼制还原(以出土器物推断仪式流程) | 文化底蕴 #3 |
|
||||||
| C3-02 | V | **问心幻境**(三通道之通道 2):墓室机关"照见"玩家 C1-02 讲述的记忆意象,通关条件是在幻境中完成告别 | 情绪稳定性 #2、依恋类型 #3 |
|
| C3-02 | P(私密路径) | **问心幻境**:由 C1-02 已授权的记忆生成玩家私有幻境;玩家可进入、折返或直接退出,石门均开启;本点不保存新增讲述,也不作画像输入 | 叙事事件(不计入人格测量) |
|
||||||
| C3-03 | B | 幻境外突发塌方征兆:先救队友 vs 先保住刚出土的核心文物 | 价值观 #5、情绪稳定性 #3 |
|
| C3-03 | B | 塌方后伤员已由足够人员稳定救护,玩家只在安全线内选择跟进伤情记录、远距铭文记录或两边交接;不进入活动结构 | 价值观 #5(单次弱证据;反应时不用于情绪评分) |
|
||||||
| C3-04 | B | 老鬼私藏事发,沈铎要按规程上报档案馆:你作证 / 求情 / 沉默 | 冲突风格 #5、价值观 #6 |
|
| C3-04 | B | 老鬼私藏事发,沈铎要按规程上报档案馆:你作证 / 求情 / 沉默 | 冲突风格 #5、价值观 #6 |
|
||||||
| C3-05 | B | 阿棠对你交付后背:把唯一的逃生绳交给你保管 | 择偶偏好 #1(信任行为) |
|
| C3-05 | B | 阿棠对你交付后背:把唯一的逃生绳交给你保管 | 择偶偏好 #1(信任行为) |
|
||||||
| C3-06 | B | 章末钩子:你无意听到馆长与未知方通话——档案馆隐瞒了什么。追问馆长 / 按下不表 / 告诉某个队友(选谁) | 依恋类型 #4(求助行为)、择偶偏好 #2 |
|
| C3-06 | B | 章末钩子:你无意听到馆长与未知方通话——档案馆隐瞒了什么。追问馆长 / 按下不表 / 告诉某个队友(选谁) | 依恋类型 #4(求助行为)、择偶偏好 #2 |
|
||||||
@@ -101,14 +104,15 @@
|
|||||||
|
|
||||||
## 5. 记忆钥匙点与预写变体(通道 1)
|
## 5. 记忆钥匙点与预写变体(通道 1)
|
||||||
|
|
||||||
每个 V 类讲述点配 2-4 个**预写场景变体**,由 Jev 提取的情绪签名选择:
|
V 类讲述点配预写场景变体;C3-02 复用 C1-02 变体,
|
||||||
|
不在私密房间另行采集或评分:
|
||||||
|
|
||||||
| 讲述点 | 情绪签名维度 | 预写变体(基调) |
|
| 讲述点 | 情绪签名维度 | 预写变体(基调) |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| C1-02 离别之门 | 温暖×永失 / 悲恸 / 释然 / 怨怼 | "爱被封存"墓室 / "战乱乱冢" / "远行者的空驿" / "被凿毁的合葬龛" |
|
| C1-02 离别之门 | 温暖×永失 / 悲恸 / 释然 / 怨怼 / 沉默 | "爱被封存"墓室 / "战乱乱冢" / "远行者的空驿" / "被凿毁的合葬龛" / "留白的长廊" |
|
||||||
| C2-02 最后悔的决定 | 自责 / 遗憾但坦然 / 愤怒指向他人 | 老人回赠的情报详细度三档 + 对应夜游场景变体 |
|
| C2-02 最后悔的决定 | 自责 / 遗憾但坦然 / 愤怒指向他人 / 沉默 | 四档情报等量(方位、时辰、完整谶语全给,老人同点同刻送到暗门口);仅老人态度与夜游氛围不同 |
|
||||||
| C2-05 篝火 | 深暴露 / 浅暴露 / 沉默 | 温简后续亲密度分支(他分享档案馆秘密的早晚) |
|
| C2-05 篝火 | 深暴露 / 浅暴露 / 沉默 | 亲密、智识线索、队长背书三种不可替代回报;档案馆疑云由浅暴露线提前听到 |
|
||||||
| C3-02 问心幻境 | 由 C1-02 记忆内容驱动 | 幻境即 BOSS 战:记忆越具体,幻境越丰满(ADR-0004:记不清=幻境模糊,设定自洽) |
|
| C3-02 问心幻境 | 复用 C1-02 情绪变体,沉默玩家为通用雾景 | 私密路径三态均开门;本期不新增语音、不保存室内讲述、不作人格评分 |
|
||||||
|
|
||||||
## 6. 自适应路由规则(Jev 置信度驱动)
|
## 6. 自适应路由规则(Jev 置信度驱动)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,185 @@
|
|||||||
|
/** 将已定稿支线册的正文/选项/后续转换为静态 JSON。运行:pnpm exec tsx scripts/build-adaptive-branches.ts */
|
||||||
|
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
||||||
|
import type { BranchPack, ChoiceNode, DialogueNode, StoryNode } from '../src/engine/types';
|
||||||
|
|
||||||
|
const source = readFileSync(new URL('../docs/script/adaptive-branches.md', import.meta.url), 'utf8');
|
||||||
|
const sections = [...source.matchAll(/^## B-(ATT|CON|EMO|VAL|CUL) 变体 ([AB]).*\n([\s\S]*?)(?=^## B-|^# 支线|^# 附录)/gm)];
|
||||||
|
const dimensions = { ATT: 'attachment', CON: 'conflict_style', EMO: 'emotional_stability', VAL: 'values', CUL: 'culture' } as const;
|
||||||
|
const sprites: Record<string, string> = { 沈铎: 'shenduo', 阿棠: 'atang', 温简: 'wenjian', 老鬼: 'laogui' };
|
||||||
|
const clean = (s: string) => s.replace(/^> ?/gm, '').replace(/\*\*/g, '').replace(/\n[ \t]*/g, '').trim();
|
||||||
|
const conditional = (normal: string, weak: string) => `【若 wenjian_health != weak:${normal}】【若 wenjian_health = weak:${weak}】`;
|
||||||
|
const waterLedger = [
|
||||||
|
['a_give', '施水一囊,未收物。'],
|
||||||
|
['b_cup', '施水一杯,未收物。'],
|
||||||
|
['c_trade', '售水两囊,得镯一支(CW-0322,有偿征集)。'],
|
||||||
|
['d_redirect', '水支出零,受赠镯一支(CW-0322,赠与代收)。'],
|
||||||
|
] as const;
|
||||||
|
const ledgerDetails = waterLedger.map(([value, text]) => `【若 cb_val_a_water = ${value}:${text}】`).join('');
|
||||||
|
|
||||||
|
function adapt(text: string, code: string, skin: string): string {
|
||||||
|
let t = text.replace(/【工程注:[\s\S]*?】/g, '').replace(/(`[^)]*)/g, '')
|
||||||
|
.replace(/(主线等值情报[^)]*)/g, '').replace(/(Act 2 伏笔[^)]*)/g, '')
|
||||||
|
.replace(/(氛围换色[^)]*)/g, '').replace(/(剧本明示[^)]*)/g, '')
|
||||||
|
.replace('(C2-05 的他,懂的人自然懂。)', '')
|
||||||
|
.replace('(轻伤演出:肩头淤青)', ',肩头淤青')
|
||||||
|
.replace('(背景演出)', '')
|
||||||
|
.replace('(或摊上最年长的)', '')
|
||||||
|
.replace('这地方刚塌过(或:这地方还没探完)', '这地方还没探完')
|
||||||
|
.replace('石柱(或:门框)', '牢固的支点');
|
||||||
|
if (skin === 'A') {
|
||||||
|
const replacements: [string, string][] = [
|
||||||
|
['温简一遍遍校准呼叫频率', conditional('温简一遍遍校准呼叫频率', '通讯频道里,温简在营地一遍遍校准呼叫频率')],
|
||||||
|
['(轻声)', conditional('(轻声)', '(营地通讯频道,声音虚弱)')],
|
||||||
|
['我和温简上塬,带足信标,沿路插', conditional('我和温简上塬,带足信标,沿路插', '我带老鬼上塬,温简在频道里报坐标,带足信标,沿路插')],
|
||||||
|
['队长和老鬼留守坐标点做呼叫中继,', conditional('队长和老鬼留守坐标点做呼叫中继,', '队长留守坐标点做呼叫中继,')],
|
||||||
|
['又看看你和温简', conditional('又看看你和温简', '又看看你和老鬼')],
|
||||||
|
['温简与你并肩,用只有两个人听见的音量说', conditional('温简与你并肩,用只有两个人听见的音量说', '温简切到与你单独通话的频道,虚弱地说')],
|
||||||
|
['说完他加快半步去追阿棠,留你一个人在后面走。', conditional('说完他加快半步去追阿棠,留你一个人在后面走。', '说完频道安静下去,留你一个人在后面走。')],
|
||||||
|
['温简在旁边低头走路,睫毛动了一下,什么都没说。', conditional('温简在旁边低头走路,睫毛动了一下,什么都没说。', '频道里温简的呼吸停了一拍,什么都没说。')],
|
||||||
|
['温简在记录板的边角,无意识地画着什么。', conditional('温简在记录板的边角,无意识地画着什么。', '通讯频道里,传来温简翻页的声音。')],
|
||||||
|
['(什么都没说,看你。所有人都在看你。)', conditional('(什么都没说,看你。所有人都在看你。)', '(频道里只有他虚弱的呼吸声。所有人都在等你。)')],
|
||||||
|
['(贴着你的耳朵喊)', conditional('(贴着你的耳朵喊)', '(在通讯频道里喊,电流声盖掉半句)')],
|
||||||
|
['五个人叠着喘', conditional('五个人叠着喘', '四个人叠着喘')],
|
||||||
|
['五个人的脸', conditional('五个人的脸', '四个人的脸')],
|
||||||
|
['温简最后开口', conditional('温简最后开口', '温简最后在频道里虚弱地开口')],
|
||||||
|
['(把铭文描进记录板,越描越慢)', conditional('(把铭文描进记录板,越描越慢)', '(营地频道,他对着传回的铭文照片描进记录板,声音虚弱)')],
|
||||||
|
];
|
||||||
|
for (const [from, to] of replacements) t = t.replaceAll(from, to);
|
||||||
|
}
|
||||||
|
// 支线册以代码字体标注变量名;游戏条件解析只接受裸变量名。
|
||||||
|
return t.replace(/【若\s*`?([a-z0-9_]+)`?\s*(!=|=)\s*`?([a-z0-9_]+)`?\s*:/g, '【若 $1 $2 $3:');
|
||||||
|
}
|
||||||
|
|
||||||
|
function polish(text: string): string {
|
||||||
|
return text.replace('(若 C2-05玩家与他有同行约定,追加一句他的低语:「正好,我步子慢,站得稳。」)', '【若 c2_05_depth = deep:他低语:「正好,我步子慢,站得稳。」】')
|
||||||
|
.replace('(C2-03 已埋的约束)', '')
|
||||||
|
.replace('(C2-04)', '')
|
||||||
|
.replace('星图盘上复原的苍龙心宿', '苍龙心宿')
|
||||||
|
.replace('(呼应 C2-01 的规程设定)', '')
|
||||||
|
.replace('说了全支线最重要的一句台词', '低声说')
|
||||||
|
.replace('冲你们喊了全支线唯一一句话', '冲你们喊')
|
||||||
|
.replace('——但这半方纹样,是后来§3-2 解谜时她第一个认出来的。', '')
|
||||||
|
.replace('——这句"账先欠着",是 Act 2 重回此地的钩子。', '')
|
||||||
|
.replace('(Act 2 沈铎线的一粒沙)', '')
|
||||||
|
.replace('(各线统一,他溜之前)', '')
|
||||||
|
.replace('你关掉的这条线,Act 2 永远不会再开——', '你关掉了这条线——')
|
||||||
|
.replace('(§3-1「有人修过路」的直接延续)', '')
|
||||||
|
.replace('(§3-1)', '')
|
||||||
|
.replace('守陵人的窄阶(若 B-VAL-B 已触发)或你们的探查路线', '你们的探查路线')
|
||||||
|
.replace('后来 §3-2 解谜', '待会儿上台基辨纹时')
|
||||||
|
.replace(/八月大火昏见——此铭是[\s\S]*?解题后台词指向未来。/, '')
|
||||||
|
.replace('与解谜线同一时刻、同一片黄昏。进 §2-5。', '正是黄昏。')
|
||||||
|
.replace('(沈铎的规程与主角方的红线一致:记录者,不取不扰)', '')
|
||||||
|
.replace('想想你在星图盘上,刚把什么补回苍龙的心口。', '大火是星名,苍龙的心宿。')
|
||||||
|
.replace('这和星图盘是一套思路。「火中,而后城安」,等的是星;这段铭文,记的也是星。', '这和老人说的「火中,而后城安」是一套思路。等的是星,记的也是星。')
|
||||||
|
.replace('【场景】', '').replace('【场景(坑道内)】', '')
|
||||||
|
.replace('——这句问话没有选项,悬在你头上,带回队里', '——这句问话悬在你头上,带回队里');
|
||||||
|
}
|
||||||
|
|
||||||
|
const exits: Record<string, string> = {
|
||||||
|
'ATT-A': '阿棠带路,全队沿观星台的侧阶向上走。黄昏落在阶石上。',
|
||||||
|
'ATT-B': '阿棠在记录板上写下「第 27 号坑·一家人俑」。采集收尾,沈铎招手,全队向台基合拢。',
|
||||||
|
'CON-A': '纠纷了结,日头西斜。从草市抄近路抵达宫墟外围,正是黄昏。',
|
||||||
|
'CON-B': '鱼牙人退走了。你们从庭院向台基合拢。',
|
||||||
|
'EMO-A': '风停,浮沙被刮走整整一层。主街中段露出一截石阶和圆形基座的边缘。阿棠用袖子擦着刻纹:「哎……这纹路,观星台。它这是——给咱们开门呢?」黄昏前,你们登台。',
|
||||||
|
'EMO-B': '采集收尾,全队向台基合拢。陶范的纹样与「铸完那一批,再没有下一批」的话,跟着你们一起上去。',
|
||||||
|
'VAL-A': '账毕,日头西斜。黄昏时,你们登上观星台。',
|
||||||
|
'VAL-B': '【若 cb_val_b_medicine != b_refer:少年拄着木枝,一瘸一拐把你们引到台基背阴面,亲手拨开荒草:「这边。我阿爷说,给神走的路修得高,给人走的路修得低。走低的,不摔跤。」】【若 cb_val_b_medicine = b_refer:老人和少年已往城里去。阿棠照着少年的喊话,在排水沟边找到荒草,拨开它。她学着少年的口气说:「给神走的路修得高,给人走的路修得低。走低的,不摔跤。」】',
|
||||||
|
};
|
||||||
|
|
||||||
|
mkdirSync(new URL('../content/branches/', import.meta.url), { recursive: true });
|
||||||
|
for (const match of sections) {
|
||||||
|
const code = match[1] as keyof typeof dimensions;
|
||||||
|
const skin = match[2];
|
||||||
|
const body = match[3].replace(/^> ?/gm, '');
|
||||||
|
const id = `b-${code.toLowerCase()}-${skin.toLowerCase()}`;
|
||||||
|
const nodes: StoryNode[] = [];
|
||||||
|
let counter = 0;
|
||||||
|
function dialogue(text: string, next = ''): DialogueNode[] {
|
||||||
|
const out: DialogueNode[] = [];
|
||||||
|
for (let paragraph of text.split(/\n\s*\n/)) {
|
||||||
|
if (!paragraph.trim() || /^\s*(?:#{1,3} |【正文】|---|【工程注)/.test(paragraph)) continue;
|
||||||
|
const raw = clean(paragraph);
|
||||||
|
if (!paragraph) continue;
|
||||||
|
const speech = raw.match(/^(沈铎|阿棠|温简|老鬼|马四|鱼牙人|妇人|守墓老人|守陵少年)(([^)]*))?:([\s\S]*)$/);
|
||||||
|
const speaker = speech ? speech[1] : '旁白';
|
||||||
|
let spoken = polish(adapt(speech ? (speech[2] ?? '') + speech[3] : raw, code, skin));
|
||||||
|
if (spoken.startsWith('【') && !spoken.startsWith('【若') && spoken.endsWith('】')) spoken = spoken.slice(1, -1);
|
||||||
|
out.push({ id: `${id}-d${++counter}`, type: 'dialogue', speaker, text: spoken, sprite: sprites[speaker] ?? 'scene', next: '' });
|
||||||
|
}
|
||||||
|
out.forEach((n, i) => { n.next = out[i + 1]?.id ?? next; });
|
||||||
|
nodes.push(...out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
if (code !== 'CUL') {
|
||||||
|
const choices = [...body.matchAll(/【选项((CB-[A-Z]+-[AB]-\d+))[^]*?】([\s\S]*?)(?=\n###)/g)];
|
||||||
|
let previousResults: DialogueNode[] = [];
|
||||||
|
let previousEnd = 0;
|
||||||
|
for (const choice of choices) {
|
||||||
|
const point = choice[1];
|
||||||
|
const intro = body.slice(previousEnd, choice.index);
|
||||||
|
const opening = dialogue(intro, point.toLowerCase());
|
||||||
|
previousResults.forEach((n) => { n.next = opening[0]?.id ?? point.toLowerCase(); });
|
||||||
|
// 每个选项以空行分隔;只取玩家台词,括号中的测量解释不入游戏。
|
||||||
|
const optionBlocks = choice[2].split(/\n\s*\n/).filter((s) => /^\*\*[A-D]\./.test(s.trim()));
|
||||||
|
const resultStart = body.indexOf(`### 各选项后续(${point})`, choice.index!);
|
||||||
|
const resultEnd = body.indexOf('\n### ', resultStart + 4);
|
||||||
|
const resultText = body.slice(resultStart, resultEnd < 0 ? body.length : resultEnd);
|
||||||
|
const results = [...resultText.matchAll(/^- \*\*([A-D])\*\*:([\s\S]*?)(?=^- \*\*[A-D]\*\*:|$(?![\s\S]))/gm)];
|
||||||
|
const choiceNode: ChoiceNode = { id: point.toLowerCase(), type: 'choice', pointId: point, prompt: '你怎么做?', dimensions: [dimensions[code]], options: [] };
|
||||||
|
previousResults = [];
|
||||||
|
for (const block of optionBlocks) {
|
||||||
|
const letter = block.trim()[2];
|
||||||
|
const label = adapt(clean(block.replace(/^\*\*[A-D]\./, '').split(/\n(/)[0]), code, skin);
|
||||||
|
const result = results.find((r) => r[1] === letter);
|
||||||
|
if (!result) throw new Error(`缺少后续 ${point}-${letter}`);
|
||||||
|
const after = dialogue(result[2]);
|
||||||
|
if (point === 'CB-VAL-A-02' && letter === 'B') after[0].text = after[0].text.replace('每项实数,', `每项实数:${ledgerDetails}`);
|
||||||
|
if (point === 'CB-VAL-A-02' && letter === 'C') after[0].text = after[0].text.replace('按实数记了,', `按实数记了:${ledgerDetails}`);
|
||||||
|
choiceNode.options.push({ id: `${point}-${letter}`, label, next: after[0].id });
|
||||||
|
previousResults.push(after.at(-1)!);
|
||||||
|
}
|
||||||
|
if (code === 'EMO' && point.endsWith('01')) {
|
||||||
|
const timeout = dialogue(skin === 'A' ? '你继续听沈铎指挥点人。队员在警戒线内一一应答;风声盖住阿棠的声音,她举手示意自己还在。' : '沈铎按既定预案接管排险,其他队员退出阵列。阿棠在受训人员协助下安全离开。');
|
||||||
|
choiceNode.options.push({ id: `${point}-TIMEOUT`, label: skin === 'A' ? '听沈铎指挥点人' : '由沈铎接管排险', next: timeout[0].id });
|
||||||
|
previousResults.push(timeout.at(-1)!);
|
||||||
|
choiceNode.timer = { seconds: skin === 'A' ? 10 : 8, optionId: `${point}-TIMEOUT` };
|
||||||
|
}
|
||||||
|
nodes.push(choiceNode);
|
||||||
|
previousEnd = resultEnd;
|
||||||
|
if (choiceNode.options.length < 3) throw new Error(`选项不完整 ${point}`);
|
||||||
|
}
|
||||||
|
const lastSection = body.slice(previousEnd).split('### 选项设计说明')[0];
|
||||||
|
const closing = code === 'VAL' && skin === 'B' ? [] : dialogue(lastSection);
|
||||||
|
const exit = dialogue(exits[`${code}-${skin}`], '$resume');
|
||||||
|
if (closing.length) closing.at(-1)!.next = exit[0].id;
|
||||||
|
previousResults.forEach((n) => { n.next = closing[0]?.id ?? exit[0].id; });
|
||||||
|
} else {
|
||||||
|
const point = `CB-CUL-${skin}-01`;
|
||||||
|
const beforePuzzle = body.slice(0, body.indexOf('### 谜面'));
|
||||||
|
const opening = dialogue(beforePuzzle.split('【前置选择')[0], skin === 'A' ? 'cb-cul-a-00' : point.toLowerCase());
|
||||||
|
void opening;
|
||||||
|
if (skin === 'A') {
|
||||||
|
const skipped = dialogue(body.slice(body.indexOf('### skip 线正文'), body.indexOf('【场景(坑道内)】')).replace(/###.*\n/, ''), '$resume');
|
||||||
|
const interior = dialogue(body.slice(body.indexOf('【场景(坑道内)】'), body.indexOf('### 谜面')), point.toLowerCase());
|
||||||
|
nodes.push({ id: 'cb-cul-a-00', type: 'choice', pointId: 'CB-CUL-A-00', prompt: '下去看看,还是赶路?', dimensions: ['culture'], untimed: true,
|
||||||
|
options: [{ id: 'CB-CUL-A-00-A', label: '下去看看', next: interior[0].id }, { id: 'CB-CUL-A-00-B', label: '赶路要紧', next: skipped[0].id }] });
|
||||||
|
}
|
||||||
|
const puzzleText = body.slice(body.indexOf('### 谜面'), body.indexOf('**谜底**'));
|
||||||
|
const questionBlocks = [...puzzleText.matchAll(/- \*\*(第[一二]问)\*\*:([^\n]+)\n([\s\S]*?)(?=\n- \*\*第|$(?![\s\S]))/gm)];
|
||||||
|
const hintsText = body.slice(body.indexOf('### 三档难度提示'), body.indexOf('### 解题后正文'));
|
||||||
|
const hints = [...hintsText.matchAll(/^\d\. ([\s\S]*?)(?=^\d\. |$(?![\s\S]))/gm)].map((h) => polish(clean(h[1])));
|
||||||
|
const after = dialogue(body.slice(body.indexOf('### 解题后正文'), body.indexOf('### 选项设计说明')));
|
||||||
|
const explanation = dialogue(body.slice(body.indexOf('**谜底**'), body.indexOf('### 三档难度提示')).replace('**谜底**:', '温简:'), after[0].id);
|
||||||
|
const exit = dialogue(skin === 'A' ? '半小时到,沈铎封道拍照。爬出断口时,日头贴着观星台的圆盘往下坠。阿棠眯着眼:「该去台上,看那颗星了。」' : '俑道尽头有侧阶直通台基顶。沈铎说:「记录。上去。」全队拾级登台。', '$resume');
|
||||||
|
after.at(-1)!.next = exit[0].id;
|
||||||
|
nodes.push({ id: point.toLowerCase(), type: 'choice', pointId: point, dimensions: ['culture'],
|
||||||
|
prompt: skin === 'A' ? '主壁铭文:「隹八月壬辰,大火見,王命作冊:城南垣,徙。」旁刻一人在高台举火,台下众人负土而行。残刻干支表起甲子、止癸酉,十枚围成一旬。' : '前排九俑胸口刻字:文、武、成、康、昭、穆、共、懿、孝。单数位微偏左,双数位微偏右,交错成两列。',
|
||||||
|
puzzle: { questions: questionBlocks.map((q, i) => ({ prompt: clean(q[2]), options: [...q[3].matchAll(/^ - [A-D]\. (.+)$/gm)].map((o) => clean(o[1])), answer: skin === 'A' && i === 1 ? 2 : 1 })), hints, exitLabel: skin === 'B' ? '上台基' : '请温简释读' },
|
||||||
|
options: [{ id: `${point}-ANSWER`, label: '确认释读', next: explanation[0].id }, { id: `${point}-EXIT`, label: skin === 'B' ? '上台基' : '请温简释读', next: explanation[0].id }] });
|
||||||
|
}
|
||||||
|
const pack: BranchPack = { id, dimension: dimensions[code], anchor: { chapter: skin === 'A' ? 'chapter2' : 'chapter3', afterNode: skin === 'A' ? 'c2-branch-anchor' : 'c3-d1b' }, estimatedMinutes: 8, startNodeId: nodes[0].id, nodes };
|
||||||
|
writeFileSync(new URL(`../content/branches/${id}.json`, import.meta.url), JSON.stringify(pack, null, 2) + '\n');
|
||||||
|
console.log(`${id}: ${nodes.length} nodes`);
|
||||||
|
}
|
||||||
|
if (sections.length !== 10) throw new Error(`预期十条支线,实际 ${sections.length}`);
|
||||||
@@ -0,0 +1,279 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { readFileSync } from 'node:fs';
|
||||||
|
import { BRANCHES } from '../src/engine/branches';
|
||||||
|
import { decideInsertion } from '../src/engine/branching';
|
||||||
|
import { StoryEngine } from '../src/engine/engine';
|
||||||
|
import { makeEvent } from '../src/engine/eventStore';
|
||||||
|
import type { StoryEvent, EventStore } from '../src/engine/eventStore';
|
||||||
|
import { choiceFlags, deriveFlags, resolveConditionalText } from '../src/engine/flags';
|
||||||
|
import { MemoryProgressStore } from '../src/engine/progress';
|
||||||
|
import { buildState, scoreProfile, PROFILE_DIMENSIONS } from '../src/engine/scoring';
|
||||||
|
import type { Confidences, ProfileDimension } from '../src/engine/scoring';
|
||||||
|
import type { Chapter, StoryNode, ChoiceBehavior } from '../src/engine/types';
|
||||||
|
import { MockJevClient } from '../src/services/jev';
|
||||||
|
import type { JevQuestion } from '../src/services/jev';
|
||||||
|
|
||||||
|
const chapters: Chapter[] = [1, 2, 3].map((i) => JSON.parse(readFileSync(new URL(`../content/chapters/chapter${i}.json`, import.meta.url), 'utf8')));
|
||||||
|
const confidences = (low: Confidences = {}): Confidences => Object.assign(Object.fromEntries(PROFILE_DIMENSIONS.map((d) => [d.id, 0.9])), low);
|
||||||
|
const inserted = (id: string) => {
|
||||||
|
const b = BRANCHES.find((b) => b.id === id)!;
|
||||||
|
return makeEvent({ sessionId: 'test', chapterId: b.anchor.chapter, pointId: id, kind: 'branch_inserted', branchId: id, dimensions: [b.dimension], confidence: 0.3 });
|
||||||
|
};
|
||||||
|
const sample = (point: string, dimension: ProfileDimension) => makeEvent({ sessionId: 'test', chapterId: 'chapter1', pointId: point, kind: 'choice', dimensions: [dimension], optionId: 'A', optionLabel: 'A' });
|
||||||
|
const choose = (low: Confidences, events: StoryEvent[] = [], target = chapters[1], completedChapter?: Chapter) => decideInsertion({ confidences: confidences(low), events, branches: BRANCHES, target, completedChapter });
|
||||||
|
|
||||||
|
for (const [label, low, events, target, expected] of [
|
||||||
|
['单维低置信', { attachment: 0.5 }, [], chapters[1], 'b-att-a'],
|
||||||
|
['多维权重', { attachment: 0.4, values: 0.4, culture: 0.4 }, [], chapters[1], 'b-att-a'],
|
||||||
|
['缺口优先', { attachment: 0.59, values: 0.1 }, [], chapters[1], 'b-val-a'],
|
||||||
|
['稀缺优先', { attachment: 0.4, values: 0.4 }, [sample('one', 'attachment'), sample('two', 'attachment')], chapters[1], 'b-val-a'],
|
||||||
|
['重复换皮', { attachment: 0.2 }, [inserted('b-att-a')], chapters[2], 'b-att-b'],
|
||||||
|
['第3章初测不可搬A', { values: 0.2 }, [], chapters[2], 'b-val-b'],
|
||||||
|
['两皮耗尽', { attachment: 0.2 }, [inserted('b-att-a'), inserted('b-att-b')], chapters[2], undefined],
|
||||||
|
['占用不叠加', { values: 0.2 }, [inserted('b-att-a')], chapters[1], undefined],
|
||||||
|
['占用顺延下一章', { values: 0.2 }, [inserted('b-att-a')], chapters[2], 'b-val-b'],
|
||||||
|
['无定义维度', { risk: 0.1, mate_preference: 0.1 }, [], chapters[1], undefined],
|
||||||
|
['阈值边界', { attachment: 0.6 }, [], chapters[1], undefined],
|
||||||
|
['无效置信度', { attachment: NaN, values: -1 }, [], chapters[1], undefined],
|
||||||
|
['超时长', { attachment: 0.1 }, [], { ...chapters[1], estimatedMinutes: 38 }, undefined],
|
||||||
|
['时长等于45', { attachment: 0.1 }, [], { ...chapters[1], estimatedMinutes: 37 }, 'b-att-a'],
|
||||||
|
] as [string, Confidences, StoryEvent[], Chapter, string | undefined][]) {
|
||||||
|
assert.equal(choose(low, events, target).branch?.id, expected, label);
|
||||||
|
}
|
||||||
|
assert.deepEqual(choose({ risk: 0.1, mate_preference: 0.1 }).evidenceInsufficient, ['risk', 'mate_preference']);
|
||||||
|
assert.deepEqual(choose({ attachment: 0.1 }, [inserted('b-att-a'), inserted('b-att-b')], chapters[2]).evidenceInsufficient, ['attachment']);
|
||||||
|
assert.equal(choose({ culture: 0.2 }, [], chapters[1], chapters[0]).branch, undefined);
|
||||||
|
assert.equal(choose({ culture: 0.2 }, [sample('C1-05', 'culture')], chapters[1], chapters[0]).branch?.id, 'b-cul-a');
|
||||||
|
console.log('✓ 自适应决策矩阵:权重、缺口、稀缺、A/B、占用、证据不足、主线优先、时长/阈值边界');
|
||||||
|
|
||||||
|
class MemoryStore implements EventStore {
|
||||||
|
events: StoryEvent[] = [];
|
||||||
|
async list(sessionId?: string) { return this.events.filter((e) => !sessionId || e.sessionId === sessionId); }
|
||||||
|
async append(e: StoryEvent) { this.events.push(e); }
|
||||||
|
}
|
||||||
|
class FixedJev extends MockJevClient {
|
||||||
|
dimension: ProfileDimension;
|
||||||
|
calls: unknown[] = [];
|
||||||
|
constructor(dimension: ProfileDimension = 'attachment') { super(); this.dimension = dimension; }
|
||||||
|
override async ask(state: unknown, questions: Record<string, JevQuestion>) {
|
||||||
|
const result = await super.ask(state, questions);
|
||||||
|
if (Object.keys(questions).length === 7) {
|
||||||
|
this.calls.push(state);
|
||||||
|
for (const d of PROFILE_DIMENSIONS) if (result[d.id]?.type === 'score') result[d.id].confidence = d.id === this.dimension ? 0.2 : 0.9;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function step(e: StoryEngine, picks: Record<string, string> = {}, behavior?: ChoiceBehavior) {
|
||||||
|
const n = e.currentNode();
|
||||||
|
if (n.type === 'choice') {
|
||||||
|
await e.choose(picks[n.pointId] ?? n.options[0].id, behavior ?? (n.puzzle ? { answers: n.puzzle.questions.map((q) => q.answer), hintsUsed: 0 } : { decisionMs: 320 }));
|
||||||
|
} else if (n.type === 'narration') await e.skipNarration();
|
||||||
|
else if (n.type === 'identity') await e.confirmIdentity({ name: '测试', age: '', city: '', gender: '', occupation: '', education: '' });
|
||||||
|
else await e.advance();
|
||||||
|
}
|
||||||
|
async function walkTo(e: StoryEngine, id: string) {
|
||||||
|
for (let i = 0; i < 400 && !e.state.finished; i++) {
|
||||||
|
if (e.state.nodeId === id) return;
|
||||||
|
await step(e);
|
||||||
|
}
|
||||||
|
assert.fail(`未到达 ${id},当前位置 ${e.state.nodeId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 所有节点均可达,所有出口返回主线,不共享/改写主线 JSON。
|
||||||
|
const original = JSON.stringify(chapters);
|
||||||
|
const pointIds = new Set<string>();
|
||||||
|
for (const branch of BRANCHES) {
|
||||||
|
const index = new Map(branch.nodes.map((n) => [n.id, n]));
|
||||||
|
const reached = new Set<string>();
|
||||||
|
function visit(id: string, path = new Set<string>()) {
|
||||||
|
if (id === '$resume') return;
|
||||||
|
assert(!path.has(id), `支线循环 ${id}`);
|
||||||
|
const n = index.get(id);
|
||||||
|
assert(n, `无效跳转 ${id}`);
|
||||||
|
reached.add(id);
|
||||||
|
const nexts = n.type === 'choice' ? n.options.map((o) => o.next) : 'next' in n ? [n.next] : [];
|
||||||
|
assert(nexts.length, `没有出口 ${id}`);
|
||||||
|
for (const next of nexts) visit(next, new Set([...path, id]));
|
||||||
|
}
|
||||||
|
visit(branch.startNodeId);
|
||||||
|
assert.equal(reached.size, branch.nodes.length, branch.id);
|
||||||
|
for (const node of branch.nodes) {
|
||||||
|
if (node.type !== 'choice') continue;
|
||||||
|
assert(!pointIds.has(node.pointId));
|
||||||
|
pointIds.add(node.pointId);
|
||||||
|
assert.deepEqual(node.dimensions, [branch.dimension]);
|
||||||
|
if (node.puzzle) {
|
||||||
|
assert.equal(node.puzzle.questions.length, 2);
|
||||||
|
assert(node.puzzle.questions.every((q) => q.options.length === 4));
|
||||||
|
assert.equal(node.puzzle.hints.length, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const health of ['weak', 'recovering']) {
|
||||||
|
for (const choiceNode of branch.nodes.filter((n) => n.type === 'choice')) {
|
||||||
|
for (const option of choiceNode.options) {
|
||||||
|
const store = new MemoryStore();
|
||||||
|
const progress = new MemoryProgressStore();
|
||||||
|
const engine = new StoryEngine(chapters, store, new FixedJev(branch.dimension), undefined, progress, BRANCHES);
|
||||||
|
await engine.restore({ sessionId: 'test', chapterId: branch.anchor.chapter, nodeId: branch.anchor.afterNode, flags: { wenjian_health: health }, insertions: [branch.id] });
|
||||||
|
await engine.advance();
|
||||||
|
assert.equal(engine.state.nodeId, branch.startNodeId);
|
||||||
|
for (let i = 0; engine.state.activeBranchId && i < 100; i++) {
|
||||||
|
const node = engine.currentNode();
|
||||||
|
const texts = node.type === 'choice' ? [node.prompt, ...node.options.map((o) => o.label)] : node.type === 'dialogue' ? [node.text] : [];
|
||||||
|
for (const text of texts) {
|
||||||
|
const rendered = engine.renderText(text);
|
||||||
|
assert(!/【若|工程注|§\d|Act 2|线追加|若 C2-05/.test(rendered), `${node.id} 泄漏: ${rendered}`);
|
||||||
|
}
|
||||||
|
const snap = progress.load()!;
|
||||||
|
const restored = new StoryEngine(chapters, store, new FixedJev(), undefined, progress, BRANCHES);
|
||||||
|
await restored.restore(snap);
|
||||||
|
assert.equal(restored.state.nodeId, engine.state.nodeId);
|
||||||
|
assert.equal(restored.state.activeBranchId, branch.id);
|
||||||
|
const behavior = node.id === choiceNode.id && node.type === 'choice' ? node.puzzle ? { answers: node.puzzle.questions.map((q) => q.answer), hintsUsed: 2, decisionMs: 400 } : { timedOut: option.id.endsWith('TIMEOUT'), decisionMs: 400 } : undefined;
|
||||||
|
await step(engine, { [choiceNode.pointId]: option.id }, behavior);
|
||||||
|
}
|
||||||
|
assert.equal(engine.state.activeBranchId, undefined);
|
||||||
|
const anchor = chapters.find((c) => c.id === branch.anchor.chapter)!.nodes.find((n) => n.id === branch.anchor.afterNode)!;
|
||||||
|
assert.equal(engine.state.nodeId, 'next' in anchor ? anchor.next : '');
|
||||||
|
const event = store.events.find((e) => e.pointId === choiceNode.pointId);
|
||||||
|
// CUL-A skip 不经过谜面;默认进入路径则每个目标点都应落事件。
|
||||||
|
assert(event, `${choiceNode.pointId} 缺事件`);
|
||||||
|
assert.equal(event.optionId, option.id);
|
||||||
|
assert.equal(event.branchId, branch.id);
|
||||||
|
const derived = deriveFlags(store.events);
|
||||||
|
for (const [flag, value] of Object.entries(derived)) assert.equal(engine.state.flags[flag], value, flag);
|
||||||
|
if (nodeIsPuzzle(choiceNode)) assert.equal(event.behavior?.solved, !option.id.endsWith('EXIT'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function nodeIsPuzzle(n: StoryNode) { return n.type === 'choice' && !!n.puzzle; }
|
||||||
|
|
||||||
|
// 01 的实际支出与受领性质必须在 02 的全部三个入口一致,D 的粥棚只在路程之后可见。
|
||||||
|
const waterBranch = BRANCHES.find((b) => b.id === 'b-val-a')!;
|
||||||
|
const waterChoices = waterBranch.nodes.filter((n) => n.type === 'choice');
|
||||||
|
assert.equal(waterChoices.length, 2);
|
||||||
|
const waterRecords = ['施水一囊', '施水一杯', '售水两囊,得镯一支(CW-0322,有偿征集)', '水支出零,受赠镯一支(CW-0322,赠与代收)'];
|
||||||
|
for (let water = 0; water < 4; water++) {
|
||||||
|
const flags = choiceFlags('CB-VAL-A-01', waterChoices[0].options[water].id);
|
||||||
|
const opening = waterBranch.nodes.find((n) => n.id === waterBranch.startNodeId)!;
|
||||||
|
assert(opening.type === 'dialogue' && opening.text.includes('施粥棚开着'), '选前必须提供当日粥棚证据');
|
||||||
|
const crossroads = waterBranch.nodes.find((n) => n.type === 'dialogue' && n.text.includes('盛热粥的碗'))!;
|
||||||
|
const elapsed = waterBranch.nodes.find((n) => n.type === 'dialogue' && n.text.includes('一个时辰的路走完了'))!;
|
||||||
|
const nexts = new Map(waterBranch.nodes.map((n) => [n.id, n]));
|
||||||
|
let cursor = waterChoices[0].options[water].next;
|
||||||
|
let sawElapsed = false;
|
||||||
|
while (cursor !== waterChoices[1].id) {
|
||||||
|
if (cursor === elapsed.id) sawElapsed = true;
|
||||||
|
if (cursor === crossroads.id) assert(sawElapsed, '粥棚确认早于一个时辰路程');
|
||||||
|
const node = nexts.get(cursor)!;
|
||||||
|
assert(node && 'next' in node, `核账前路径中断 ${cursor}`);
|
||||||
|
cursor = node.next;
|
||||||
|
}
|
||||||
|
assert.equal(resolveConditionalText((crossroads as { text: string }).text, flags)?.includes('热粥'), water === 3);
|
||||||
|
for (const [ledger, option] of waterChoices[1].options.entries()) {
|
||||||
|
const node = nexts.get(option.next)!;
|
||||||
|
assert(node.type === 'dialogue');
|
||||||
|
const rendered = resolveConditionalText(node.text, { ...flags, wenjian_health: 'recovering' })!;
|
||||||
|
assert(rendered.includes(waterRecords[water]), `核账 ${water}/${ledger} 不守恒: ${rendered}`);
|
||||||
|
for (const [other, record] of waterRecords.entries()) if (other !== water) assert(!rendered.includes(record), `核账 ${water}/${ledger} 串账`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const conB = BRANCHES.find((b) => b.id === 'b-con-b')!;
|
||||||
|
assert(conB.nodes[0].type === 'dialogue' && conB.nodes[0].text.includes('安门内'));
|
||||||
|
assert(conB.nodes.every((n) => n.type !== 'dialogue' || !n.text.includes('准备进安门')));
|
||||||
|
for (const [letter, value] of [['A', 'a_alert'], ['B', 'b_relay'], ['C', 'c_record'], ['TIMEOUT', 'self_rescue']] as const)
|
||||||
|
assert.equal(choiceFlags('CB-EMO-B-01', `CB-EMO-B-01-${letter}`).cb_emo_b_plate, value);
|
||||||
|
for (const [letter, value] of [['A', 'a_record'], ['B', 'b_mark'], ['C', 'c_leave']] as const)
|
||||||
|
assert.equal(choiceFlags('CB-EMO-B-02', `CB-EMO-B-02-${letter}`).cb_emo_b_vent, value);
|
||||||
|
assert.equal(choiceFlags('CB-VAL-A-01', 'CB-VAL-A-01-C').flag_thirst_days, 'true');
|
||||||
|
console.log('✓ 四路水量×三种核账、粥棚时间及可见性、门内入口、压板枚举');
|
||||||
|
assert.equal(JSON.stringify(chapters), original);
|
||||||
|
assert.equal(pointIds.size, 17);
|
||||||
|
console.log('✓ 十条支线全部选项/超时/跳过、温简双状态、逐节点恢复、事件与 flags、出口汇合');
|
||||||
|
|
||||||
|
for (const dimension of ['attachment', 'values', 'conflict_style', 'emotional_stability', 'culture'] as const) {
|
||||||
|
const store = new MemoryStore();
|
||||||
|
const jev = new FixedJev(dimension);
|
||||||
|
const e = new StoryEngine(chapters, store, jev, undefined, undefined, BRANCHES);
|
||||||
|
for (let i = 0; !e.state.finished && i < 400; i++) await step(e);
|
||||||
|
assert(e.state.finished);
|
||||||
|
const plans = store.events.filter((e) => e.kind === 'branch_inserted');
|
||||||
|
assert.equal(plans.length, 2);
|
||||||
|
assert(plans[0].branchId?.endsWith('-a') && plans[1].branchId?.endsWith('-b'));
|
||||||
|
assert.equal(jev.calls.length, 3);
|
||||||
|
assert(store.events.some((e) => e.kind === 'evidence_insufficient' && e.dimensions.includes(dimension)));
|
||||||
|
assert(String(jev.calls[2]).includes('CB-'));
|
||||||
|
const count = store.events.length;
|
||||||
|
e.replay('chapter1');
|
||||||
|
assert.equal(e.insertions.length, 0);
|
||||||
|
assert.equal(e.state.activeBranchId, undefined);
|
||||||
|
assert.equal(store.events.length, count);
|
||||||
|
await walkTo(e, 'c1-end');
|
||||||
|
await e.advance();
|
||||||
|
assert.equal(e.insertions[0].id, plans[0].branchId);
|
||||||
|
assert(!String(jev.calls.at(-1)).includes(plans[0].sessionId));
|
||||||
|
}
|
||||||
|
console.log('✓ 五维各跑通三章、A→B换皮、末章证据不足、本局隔离、replay 清计划');
|
||||||
|
|
||||||
|
{
|
||||||
|
const store = new MemoryStore();
|
||||||
|
const progress = new MemoryProgressStore();
|
||||||
|
const jev = new FixedJev();
|
||||||
|
const e = new StoryEngine(chapters, store, jev, undefined, progress, BRANCHES);
|
||||||
|
await walkTo(e, 'c1-end');
|
||||||
|
const oldSnapshot = progress.load()!;
|
||||||
|
await Promise.all([e.advance(), e.advance(), e.advance()]);
|
||||||
|
assert.equal(jev.calls.length, 1);
|
||||||
|
assert.equal(e.state.chapterId, 'chapter2');
|
||||||
|
assert.equal(store.events.filter((ev) => ev.kind === 'branch_inserted').length, 1);
|
||||||
|
const restored = new StoryEngine(chapters, store, jev, undefined, progress, BRANCHES);
|
||||||
|
await restored.restore(oldSnapshot);
|
||||||
|
await restored.advance();
|
||||||
|
assert.equal(jev.calls.length, 1);
|
||||||
|
assert.equal(restored.insertions.length, 1);
|
||||||
|
assert.equal(store.events.filter((ev) => ev.kind === 'branch_inserted').length, 1);
|
||||||
|
}
|
||||||
|
for (const jev of [
|
||||||
|
{ ask: async () => { throw new Error('test offline'); } },
|
||||||
|
{ ask: async () => ({}) },
|
||||||
|
{ adaptiveScoring: false, ask: async () => { assert.fail('缺 key 不应打分'); } },
|
||||||
|
]) {
|
||||||
|
const store = new MemoryStore();
|
||||||
|
const e = new StoryEngine(chapters, store, jev, undefined, undefined, BRANCHES);
|
||||||
|
await e.restore({ sessionId: 'test', chapterId: 'chapter1', nodeId: 'c1-end', flags: {} });
|
||||||
|
await e.advance();
|
||||||
|
assert.equal(e.state.chapterId, 'chapter2');
|
||||||
|
assert.equal(e.insertions.length, 0);
|
||||||
|
assert.equal(store.events.length, 0);
|
||||||
|
}
|
||||||
|
console.log('✓ 重复点击与刷新幂等、Jev故障/无效响应/无key降级');
|
||||||
|
|
||||||
|
for (const branchId of ['b-cul-a', 'b-cul-b']) {
|
||||||
|
const branch = BRANCHES.find((b) => b.id === branchId)!;
|
||||||
|
const puzzle = branch.nodes.find((n) => n.type === 'choice' && n.puzzle)!;
|
||||||
|
assert(puzzle.type === 'choice' && puzzle.puzzle);
|
||||||
|
for (const [correct, expected] of [[2, 'true'], [1, 'partial'], [0, 'false']] as const) {
|
||||||
|
const store = new MemoryStore();
|
||||||
|
const e = new StoryEngine(chapters, store, new MockJevClient(), undefined, undefined, BRANCHES);
|
||||||
|
await e.restore({ sessionId: 'test', chapterId: branch.anchor.chapter, nodeId: puzzle.id, activeBranchId: branch.id, flags: {}, insertions: [branch.id] });
|
||||||
|
const answers = puzzle.puzzle.questions.map((q, i) => i < correct ? q.answer : (q.answer + 1) % q.options.length);
|
||||||
|
await e.choose(puzzle.options[0].id, { answers, hintsUsed: 1 });
|
||||||
|
assert.equal(e.state.flags[`c${branchId.replaceAll('-', '_')}_solved`], expected);
|
||||||
|
assert.deepEqual(store.events.at(-1)?.behavior?.answerCorrect, [correct > 0, correct > 1]);
|
||||||
|
}
|
||||||
|
const store = new MemoryStore();
|
||||||
|
const e = new StoryEngine(chapters, store, new MockJevClient(), undefined, undefined, BRANCHES);
|
||||||
|
await e.restore({ sessionId: 'test', chapterId: branch.anchor.chapter, nodeId: puzzle.id, activeBranchId: branch.id, flags: {}, insertions: [branch.id] });
|
||||||
|
await e.choose(puzzle.options[1].id);
|
||||||
|
assert.equal(e.state.flags[`c${branchId.replaceAll('-', '_')}_solved`], 'not_attempted');
|
||||||
|
assert.equal(e.state.flags[`c${branchId.replaceAll('-', '_')}_left`], 'true');
|
||||||
|
assert.equal(store.events.at(-1)?.behavior?.answers, undefined);
|
||||||
|
}
|
||||||
|
console.log('✓ 文化双问题分题记录 partial,主动离场独立于提示用尽/答错');
|
||||||
|
|
||||||
|
assert(!buildState([makeEvent({ sessionId: 'private', chapterId: 'chapter1', kind: 'profile', pointId: 'C1-01', dimensions: [], profile: { name: 'SECRET-NAME', city: '', age: '', gender: '', occupation: '', education: '' } })]).includes('SECRET-NAME'));
|
||||||
|
await assert.rejects(() => scoreProfile([], { ask: async () => ({}) }));
|
||||||
|
assert.equal(resolveConditionalText('【若 cb_val_a_water = d_redirect:粥棚】', { cb_val_a_water: 'a_give' }), '');
|
||||||
|
console.log('✓ 共享评分排除身份数据、无效响应不冒充低置信、条件文案不串线');
|
||||||
@@ -51,7 +51,7 @@ const chapters = [1, 2, 3].map(
|
|||||||
// --- 1. C1-01 身份抽取(走引擎真实代码路径) ---
|
// --- 1. C1-01 身份抽取(走引擎真实代码路径) ---
|
||||||
const store = new MemoryStore();
|
const store = new MemoryStore();
|
||||||
const engine = new StoryEngine(chapters, store, jev);
|
const engine = new StoryEngine(chapters, store, jev);
|
||||||
while (engine.currentNode().type !== 'identity') engine.advance();
|
while (engine.currentNode().type !== 'identity') await engine.advance();
|
||||||
const intro = '我叫林晚,今年29岁,在杭州一家互联网公司做产品经理,硕士毕业五年了。';
|
const intro = '我叫林晚,今年29岁,在杭州一家互联网公司做产品经理,硕士毕业五年了。';
|
||||||
const profile = await engine.extractIdentity(intro);
|
const profile = await engine.extractIdentity(intro);
|
||||||
console.log('\n[1] C1-01 身份抽取');
|
console.log('\n[1] C1-01 身份抽取');
|
||||||
@@ -60,7 +60,7 @@ console.log(' 抽取:', JSON.stringify(profile));
|
|||||||
|
|
||||||
// --- 2. C1-02 情绪签名 → 场景变体(choice 格式实测) ---
|
// --- 2. C1-02 情绪签名 → 场景变体(choice 格式实测) ---
|
||||||
await engine.confirmIdentity(profile);
|
await engine.confirmIdentity(profile);
|
||||||
while (engine.currentNode().type !== 'narration') engine.advance();
|
while (engine.currentNode().type !== 'narration') await engine.advance();
|
||||||
const farewell = '外婆走的那天我在外地考试,没赶上最后一面。后来我每年清明都回去,院子里的桂花树还在。';
|
const farewell = '外婆走的那天我在外地考试,没赶上最后一面。后来我每年清明都回去,院子里的桂花树还在。';
|
||||||
let result = await engine.submitNarration(farewell);
|
let result = await engine.submitNarration(farewell);
|
||||||
console.log('\n[2] C1-02 情绪签名 → 场景变体');
|
console.log('\n[2] C1-02 情绪签名 → 场景变体');
|
||||||
|
|||||||
+58
-17
@@ -14,7 +14,8 @@ import { readFileSync } from 'node:fs';
|
|||||||
import { CONSENT_VERSION, StoryEngine } from '../src/engine/engine';
|
import { CONSENT_VERSION, StoryEngine } from '../src/engine/engine';
|
||||||
import { LocalStorageEventStore } from '../src/engine/eventStore';
|
import { LocalStorageEventStore } from '../src/engine/eventStore';
|
||||||
import type { EventStore, StoryEvent } from '../src/engine/eventStore';
|
import type { EventStore, StoryEvent } from '../src/engine/eventStore';
|
||||||
import { choiceFlags, narrationFlags } from '../src/engine/flags';
|
import { choiceFlags, deriveFlags, narrationFlags } from '../src/engine/flags';
|
||||||
|
import { buildState } from '../src/engine/scoring';
|
||||||
import { MemoryProgressStore } from '../src/engine/progress';
|
import { MemoryProgressStore } from '../src/engine/progress';
|
||||||
import { SqliteEventStore } from '../src/engine/sqliteEventStore';
|
import { SqliteEventStore } from '../src/engine/sqliteEventStore';
|
||||||
import type { Chapter, ChoiceNode, IdentityProfile, NarrationNode } from '../src/engine/types';
|
import type { Chapter, ChoiceNode, IdentityProfile, NarrationNode } from '../src/engine/types';
|
||||||
@@ -37,8 +38,8 @@ const EXPECTED: Record<string, string[]> = {
|
|||||||
'C2-05': ['attachment', 'reciprocity'],
|
'C2-05': ['attachment', 'reciprocity'],
|
||||||
'C2-06': ['conflict_style', 'values'],
|
'C2-06': ['conflict_style', 'values'],
|
||||||
'C3-01': ['culture'],
|
'C3-01': ['culture'],
|
||||||
'C3-02': ['emotional_stability', 'attachment'],
|
'C3-02': [],
|
||||||
'C3-03': ['values', 'emotional_stability'],
|
'C3-03': ['values'],
|
||||||
'C3-04': ['conflict_style', 'values'],
|
'C3-04': ['conflict_style', 'values'],
|
||||||
'C3-05': ['mate_preference'],
|
'C3-05': ['mate_preference'],
|
||||||
'C3-06': ['attachment', 'mate_preference'],
|
'C3-06': ['attachment', 'mate_preference'],
|
||||||
@@ -115,7 +116,7 @@ for (const [id, dims] of Object.entries(EXPECTED)) {
|
|||||||
|
|
||||||
// --- 3. V 类讲述点变体结构 ---
|
// --- 3. V 类讲述点变体结构 ---
|
||||||
const narrationNodes = chapters.flatMap((c) => c.nodes.filter((n) => n.type === 'narration')) as NarrationNode[];
|
const narrationNodes = chapters.flatMap((c) => c.nodes.filter((n) => n.type === 'narration')) as NarrationNode[];
|
||||||
check(narrationNodes.map((n) => n.pointId).sort().join(',') === 'C1-02,C2-02,C2-05,C3-02', 'V 类讲述点为 C1-02/C2-02/C2-05/C3-02');
|
check(narrationNodes.map((n) => n.pointId).sort().join(',') === 'C1-02,C2-02,C2-05,C3-02', '三个语音讲述点 + C3-02 私密路径节点均存在');
|
||||||
for (const n of narrationNodes) {
|
for (const n of narrationNodes) {
|
||||||
check(n.variants.length >= 2, `${n.pointId}: 有 ${n.variants.length} 个预写场景变体`);
|
check(n.variants.length >= 2, `${n.pointId}: 有 ${n.variants.length} 个预写场景变体`);
|
||||||
check(
|
check(
|
||||||
@@ -126,11 +127,16 @@ for (const n of narrationNodes) {
|
|||||||
check(narrationNodes.find((n) => n.pointId === 'C3-02')?.variantFrom === 'C1-02', 'C3-02 变体由 C1-02 驱动(variantFrom)');
|
check(narrationNodes.find((n) => n.pointId === 'C3-02')?.variantFrom === 'C1-02', 'C3-02 变体由 C1-02 驱动(variantFrom)');
|
||||||
for (const n of narrationNodes) {
|
for (const n of narrationNodes) {
|
||||||
check(
|
check(
|
||||||
FOLLOW_UP_DIMENSIONS.every((d) => typeof n.followUps?.[d] === 'string' && n.followUps[d]!.length > 0),
|
n.pointId === 'C3-02' ? n.followUp.maxQuestions === 0 && Object.keys(n.followUps).length === 0 : FOLLOW_UP_DIMENSIONS.every((d) => typeof n.followUps?.[d] === 'string' && n.followUps[d]!.length > 0),
|
||||||
`${n.pointId}: 六维 followUps 追问模板齐全`,
|
`${n.pointId}: 六维 followUps 追问模板齐全`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 自动遍历解谜点时提交真实两题答案,其他选择点无需行为参数。 */
|
||||||
|
function puzzleBehavior(node: ChoiceNode) {
|
||||||
|
return node.puzzle ? { answers: node.puzzle.questions.map((q) => q.answer), hintsUsed: 0 } : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
// --- 4. Mock 模式端到端跑通三章 ---
|
// --- 4. Mock 模式端到端跑通三章 ---
|
||||||
const store = new MemoryStore();
|
const store = new MemoryStore();
|
||||||
const jev = new MockJevClient();
|
const jev = new MockJevClient();
|
||||||
@@ -139,19 +145,20 @@ let guard = 0;
|
|||||||
while (!engine.state.finished && guard++ < 200) {
|
while (!engine.state.finished && guard++ < 200) {
|
||||||
const node = engine.currentNode();
|
const node = engine.currentNode();
|
||||||
if (node.type === 'choice') {
|
if (node.type === 'choice') {
|
||||||
await engine.choose((node as ChoiceNode).options[0].id);
|
await engine.choose(node.options[0].id, puzzleBehavior(node));
|
||||||
} else if (node.type === 'identity') {
|
} else if (node.type === 'identity') {
|
||||||
const profile = await engine.extractIdentity('我叫阿黎,29岁,在杭州做互联网产品经理,硕士学历');
|
const profile = await engine.extractIdentity('我叫阿黎,29岁,在杭州做互联网产品经理,硕士学历');
|
||||||
await engine.confirmIdentity(profile);
|
await engine.confirmIdentity(profile);
|
||||||
} else if (node.type === 'narration') {
|
} else if (node.type === 'narration') {
|
||||||
// C2-05 走"沉默"路径验证合法选项,其余讲述;Mock 检测到缺失维度则回答追问
|
// C2-05 走"沉默"路径验证合法选项,其余讲述;Mock 检测到缺失维度则回答追问
|
||||||
if ((node as NarrationNode).pointId === 'C2-05') await engine.skipNarration();
|
if ((node as NarrationNode).pointId === 'C2-05') await engine.skipNarration();
|
||||||
|
else if ((node as NarrationNode).pointId === 'C3-02') await engine.resolvePrivateIllusion('farewell_done');
|
||||||
else {
|
else {
|
||||||
await engine.submitNarration('(验证用占位讲述)');
|
await engine.submitNarration('(验证用占位讲述)');
|
||||||
if (engine.state.pendingFollowUp) await engine.answerFollowUp('(验证用追问回答)');
|
if (engine.state.pendingFollowUp) await engine.answerFollowUp('(验证用追问回答)');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
engine.advance();
|
await engine.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
check(engine.state.finished, '三章流程跑通(Mock 模式)');
|
check(engine.state.finished, '三章流程跑通(Mock 模式)');
|
||||||
@@ -167,6 +174,23 @@ const c305 = store.events.find((e) => e.pointId === 'C3-02' && e.kind === 'narra
|
|||||||
const c102 = store.events.find((e) => e.pointId === 'C1-02' && e.kind === 'narration');
|
const c102 = store.events.find((e) => e.pointId === 'C1-02' && e.kind === 'narration');
|
||||||
check(!!c305?.variantId && c305.variantId === c102?.variantId, 'C3-02 幻境变体复用 C1-02 情绪解析结果');
|
check(!!c305?.variantId && c305.variantId === c102?.variantId, 'C3-02 幻境变体复用 C1-02 情绪解析结果');
|
||||||
|
|
||||||
|
// C3-01 分题作答:事件、增量 flags 与事件重放结果必须一致。
|
||||||
|
{
|
||||||
|
const puzzle = chapters[2].nodes.find((n) => n.id === 'c3-01') as ChoiceNode;
|
||||||
|
check(!!puzzle.puzzle && puzzle.puzzle.questions.length === 2 && puzzle.puzzle.hints.length === 3, 'C3-01:两道独立选择题与三档提示');
|
||||||
|
for (const [correctCount, expected] of [[1, 'partial'], [2, 'true'], [0, 'false']] as const) {
|
||||||
|
const s = new MemoryStore();
|
||||||
|
const e = new StoryEngine(chapters, s, new MockJevClient());
|
||||||
|
await e.restore({ sessionId: `c3-01-${correctCount}`, chapterId: 'chapter3', nodeId: puzzle.id, flags: {} });
|
||||||
|
const answers = puzzle.puzzle!.questions.map((q, i) => i < correctCount ? q.answer : (q.answer + 1) % q.options.length);
|
||||||
|
await e.choose(puzzle.options[0].id, { answers, hintsUsed: 2 });
|
||||||
|
const event = s.events.at(-1);
|
||||||
|
check(event?.behavior?.answers?.join(',') === answers.join(',') && event.behavior.answerCorrect?.join(',') === [correctCount > 0, correctCount > 1].join(',') && event.behavior.hintsUsed === 2, `C3-01 ${expected}:分题答案、正误与提示数落事件`);
|
||||||
|
check(e.state.flags.c3_01_solved === expected && e.state.flags.c3_01_hints_used === '2' && deriveFlags(s.events).c3_01_solved === expected && deriveFlags(s.events).c3_01_hints_used === '2', `C3-01 ${expected}:即时与重放 flags 一致`);
|
||||||
|
check(e.state.nodeId === 'c3-01-ok', `C3-01 ${expected}:提交后进入原有温简复原节点`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- 5. 画像报告:7 维 score 问题 ---
|
// --- 5. 画像报告:7 维 score 问题 ---
|
||||||
const criteria = Array.from({ length: 10 }, (_, i) => `${i + 1} 级`);
|
const criteria = Array.from({ length: 10 }, (_, i) => `${i + 1} 级`);
|
||||||
const dims = ['attachment', 'conflict_style', 'values', 'emotional_stability', 'risk', 'culture', 'mate_preference'];
|
const dims = ['attachment', 'conflict_style', 'values', 'emotional_stability', 'risk', 'culture', 'mate_preference'];
|
||||||
@@ -189,10 +213,11 @@ while (engine.currentNode().type !== 'choice') {
|
|||||||
if (n.type === 'identity') {
|
if (n.type === 'identity') {
|
||||||
await engine.confirmIdentity(await engine.extractIdentity('占位自我介绍'));
|
await engine.confirmIdentity(await engine.extractIdentity('占位自我介绍'));
|
||||||
} else if (n.type === 'narration') {
|
} else if (n.type === 'narration') {
|
||||||
|
if (n.pointId === 'C3-02') { await engine.resolvePrivateIllusion('farewell_done'); continue; }
|
||||||
await engine.submitNarration('(验证用占位讲述)');
|
await engine.submitNarration('(验证用占位讲述)');
|
||||||
if (engine.state.pendingFollowUp) await engine.answerFollowUp('(验证用追问回答)');
|
if (engine.state.pendingFollowUp) await engine.answerFollowUp('(验证用追问回答)');
|
||||||
} else {
|
} else {
|
||||||
engine.advance();
|
await engine.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const firstChoice = engine.currentNode() as ChoiceNode;
|
const firstChoice = engine.currentNode() as ChoiceNode;
|
||||||
@@ -226,12 +251,12 @@ class FixedNoulJev extends MockJevClient {
|
|||||||
/** 推进到指定节点(途中选择点取第一项、身份点直接确认、讲述点沉默略过) */
|
/** 推进到指定节点(途中选择点取第一项、身份点直接确认、讲述点沉默略过) */
|
||||||
async function walkTo(eng: StoryEngine, nodeId: string) {
|
async function walkTo(eng: StoryEngine, nodeId: string) {
|
||||||
let g = 0;
|
let g = 0;
|
||||||
while (eng.currentNode().id !== nodeId && g++ < 60) {
|
while (eng.currentNode().id !== nodeId && g++ < 250) {
|
||||||
const n = eng.currentNode();
|
const n = eng.currentNode();
|
||||||
if (n.type === 'choice') await eng.choose((n as ChoiceNode).options[0].id);
|
if (n.type === 'choice') await eng.choose(n.options[0].id, puzzleBehavior(n));
|
||||||
else if (n.type === 'identity') await eng.confirmIdentity(await eng.extractIdentity('占位自我介绍'));
|
else if (n.type === 'identity') await eng.confirmIdentity(await eng.extractIdentity('占位自我介绍'));
|
||||||
else if (n.type === 'narration') await eng.skipNarration();
|
else if (n.type === 'narration') await eng.skipNarration();
|
||||||
else eng.advance();
|
else await eng.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +347,7 @@ check(choiceFlags('C2-01', 'b_exposure').wenjian_health === 'weak', 'flag 推导
|
|||||||
check(choiceFlags('C2-01', 'a_weakest').wenjian_health === 'recovering', 'flag 推导:C2-01 a_weakest → wenjian_health=recovering');
|
check(choiceFlags('C2-01', 'a_weakest').wenjian_health === 'recovering', 'flag 推导:C2-01 a_weakest → wenjian_health=recovering');
|
||||||
check(choiceFlags('C2-01', 'd_process').wenjian_health === 'weak', 'flag 推导(S4 修订):C2-01 d_process → wenjian_health=weak(议事真空期老鬼先行分药)');
|
check(choiceFlags('C2-01', 'd_process').wenjian_health === 'weak', 'flag 推导(S4 修订):C2-01 d_process → wenjian_health=weak(议事真空期老鬼先行分药)');
|
||||||
check(!('flag_prophecy_heard' in narrationFlags('C2-02', 'intel_full', '(讲述)')), 'flag 推导(S3 修订):flag_prophecy_heard 已废除,C2-02 四档等价');
|
check(!('flag_prophecy_heard' in narrationFlags('C2-02', 'intel_full', '(讲述)')), 'flag 推导(S3 修订):flag_prophecy_heard 已废除,C2-02 四档等价');
|
||||||
check(narrationFlags('C3-02', undefined, null).c3_02_mode === 'exit_room', 'flag 推导:C3-02 沉默退出 → c3_02_mode=exit_room');
|
check(narrationFlags('C3-02', undefined, null).c3_02_mode === undefined, 'C3-02 不从文本推导模式');
|
||||||
{
|
{
|
||||||
const f = choiceFlags('C3-06', 'c_confide_wenjian');
|
const f = choiceFlags('C3-06', 'c_confide_wenjian');
|
||||||
check(
|
check(
|
||||||
@@ -330,7 +355,7 @@ check(narrationFlags('C3-02', undefined, null).c3_02_mode === 'exit_room', 'flag
|
|||||||
'flag 推导:C3-06 队友子分支 → c_confide / confidant=wenjian / flag_arch_doubt',
|
'flag 推导:C3-06 队友子分支 → c_confide / confidant=wenjian / flag_arch_doubt',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
check(narrationFlags('C2-05', 'wenjian_close_early', '(讲述)').flag_wenjian_secret === 'true', 'flag 推导:C2-05 深暴露 → flag_wenjian_secret');
|
check(narrationFlags('C2-05', 'wenjian_close_late', '(讲述)').flag_wenjian_secret === 'true', 'flag 推导:C2-05 浅暴露 → flag_wenjian_secret');
|
||||||
|
|
||||||
/** 走查三章并收集途经节点与渲染文本;picker 可指定选择点的选项 */
|
/** 走查三章并收集途经节点与渲染文本;picker 可指定选择点的选项 */
|
||||||
async function walkAll(eng: StoryEngine, picker: Record<string, string> = {}) {
|
async function walkAll(eng: StoryEngine, picker: Record<string, string> = {}) {
|
||||||
@@ -345,17 +370,17 @@ async function walkAll(eng: StoryEngine, picker: Record<string, string> = {}) {
|
|||||||
else if (node.type === 'choice') rendered.push(eng.renderText(node.prompt), ...node.options.map((o) => eng.renderText(o.label)));
|
else if (node.type === 'choice') rendered.push(eng.renderText(node.prompt), ...node.options.map((o) => eng.renderText(o.label)));
|
||||||
else if (node.type === 'narration' || node.type === 'identity') rendered.push(eng.renderText(node.prompt));
|
else if (node.type === 'narration' || node.type === 'identity') rendered.push(eng.renderText(node.prompt));
|
||||||
if (node.type === 'choice') {
|
if (node.type === 'choice') {
|
||||||
await eng.choose(picker[node.pointId] ?? node.options[0].id);
|
await eng.choose(picker[node.pointId] ?? node.options[0].id, puzzleBehavior(node));
|
||||||
} else if (node.type === 'identity') {
|
} else if (node.type === 'identity') {
|
||||||
await eng.confirmIdentity(await eng.extractIdentity('占位自我介绍'));
|
await eng.confirmIdentity(await eng.extractIdentity('占位自我介绍'));
|
||||||
} else if (node.type === 'narration') {
|
} else if (node.type === 'narration') {
|
||||||
if (node.pointId === 'C3-02' && picker.C3_02_SILENT) await eng.skipNarration();
|
if (node.pointId === 'C3-02') await eng.resolvePrivateIllusion(picker.C3_02_SILENT ? 'exit_room' : 'farewell_done');
|
||||||
else {
|
else {
|
||||||
await eng.submitNarration('(验证用占位讲述)');
|
await eng.submitNarration('(验证用占位讲述)');
|
||||||
if (eng.state.pendingFollowUp) await eng.answerFollowUp('(验证用追问回答)');
|
if (eng.state.pendingFollowUp) await eng.answerFollowUp('(验证用追问回答)');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eng.advance();
|
await eng.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return { seen, rendered, finished: eng.state.finished };
|
return { seen, rendered, finished: eng.state.finished };
|
||||||
@@ -511,8 +536,24 @@ class FailingJev extends MockJevClient {
|
|||||||
check(eng3.state.playerName === '林晚', '断点恢复后从 profile 事件取回称呼');
|
check(eng3.state.playerName === '林晚', '断点恢复后从 profile 事件取回称呼');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// C3-02 的三种路径只落叙事模式,不采集或评分室内讲述。
|
||||||
|
for (const mode of ['farewell_done', 'farewell_partial', 'exit_room'] as const) {
|
||||||
|
const s = new MemoryStore();
|
||||||
|
const e = new StoryEngine(chapters, s, new MockJevClient());
|
||||||
|
await walkTo(e, 'c3-02');
|
||||||
|
const r = await e.resolvePrivateIllusion(mode);
|
||||||
|
const recorded = s.events.find((ev) => ev.pointId === 'C3-02');
|
||||||
|
check(recorded?.kind === 'narration' && recorded.narrationMode === mode && recorded.transcript === null && recorded.dimensions.length === 0, `问心之室 ${mode}:不留文本或维度标签`);
|
||||||
|
check(e.state.flags.c3_02_mode === mode && !buildState(s.events).includes('C3-02'), `问心之室 ${mode}:叙事变量生效且画像隔离`);
|
||||||
|
check(r.event.id === recorded?.id && e.state.nodeId === 'c3-cutscene', `问心之室 ${mode}:石门照常开启`);
|
||||||
|
const restored = deriveFlags(s.events);
|
||||||
|
check(restored.c3_02_mode === mode && restored.c3_02_fantasy_density === e.state.flags.c3_02_fantasy_density, `问心之室 ${mode}:恢复后叙事状态一致`);
|
||||||
|
}
|
||||||
|
|
||||||
if (failures > 0) {
|
if (failures > 0) {
|
||||||
|
// 主线失败先退出,避免掩盖原有回归。
|
||||||
console.error(`\n${failures} 项校验失败`);
|
console.error(`\n${failures} 项校验失败`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
console.log('\n全部校验通过');
|
console.log('\n全部校验通过');
|
||||||
|
await import('./verify-adaptive');
|
||||||
|
|||||||
+32
@@ -91,6 +91,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.choice-options {
|
.choice-options {
|
||||||
|
min-width: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
@@ -98,6 +99,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.choice-btn {
|
.choice-btn {
|
||||||
|
overflow-wrap: anywhere;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -115,6 +117,36 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 讲述点 ---- */
|
/* ---- 讲述点 ---- */
|
||||||
|
.choice-box fieldset {
|
||||||
|
min-width: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 12px 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.puzzle-option {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.choice-box progress {
|
||||||
|
width: 100%;
|
||||||
|
height: 10px;
|
||||||
|
accent-color: #d4a96a;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled { opacity: 0.6; cursor: default; }
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.scene { padding: 64px 12px 16px; }
|
||||||
|
.dimension-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
|
||||||
|
.dimension-value { grid-column: 1 / -1; }
|
||||||
|
.dialogue-box > div:last-child { min-width: 0; overflow-wrap: anywhere; }
|
||||||
|
}
|
||||||
|
|
||||||
.mechanism-tag {
|
.mechanism-tag {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
import { StoryEngine } from './engine/engine';
|
import { StoryEngine } from './engine/engine';
|
||||||
|
import { BRANCHES } from './engine/branches';
|
||||||
import { LocalStorageEventStore } from './engine/eventStore';
|
import { LocalStorageEventStore } from './engine/eventStore';
|
||||||
import { LocalStorageProgressStore } from './engine/progress';
|
import { LocalStorageProgressStore } from './engine/progress';
|
||||||
import { SqliteEventStore, isTauriRuntime } from './engine/sqliteEventStore';
|
import { SqliteEventStore, isTauriRuntime } from './engine/sqliteEventStore';
|
||||||
@@ -25,7 +26,7 @@ export default function App() {
|
|||||||
const store = isTauriRuntime() ? new SqliteEventStore() : new LocalStorageEventStore();
|
const store = isTauriRuntime() ? new SqliteEventStore() : new LocalStorageEventStore();
|
||||||
const jev = getJevClient();
|
const jev = getJevClient();
|
||||||
const progress = new LocalStorageProgressStore();
|
const progress = new LocalStorageProgressStore();
|
||||||
return { engine: new StoryEngine(chapters, store, jev, undefined, progress), store, jev, progress };
|
return { engine: new StoryEngine(chapters, store, jev, undefined, progress, BRANCHES), store, jev, progress };
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import type { BranchPack } from './types';
|
||||||
|
import attA from '../../content/branches/b-att-a.json';
|
||||||
|
import attB from '../../content/branches/b-att-b.json';
|
||||||
|
import conA from '../../content/branches/b-con-a.json';
|
||||||
|
import conB from '../../content/branches/b-con-b.json';
|
||||||
|
import emoA from '../../content/branches/b-emo-a.json';
|
||||||
|
import emoB from '../../content/branches/b-emo-b.json';
|
||||||
|
import valA from '../../content/branches/b-val-a.json';
|
||||||
|
import valB from '../../content/branches/b-val-b.json';
|
||||||
|
import culA from '../../content/branches/b-cul-a.json';
|
||||||
|
import culB from '../../content/branches/b-cul-b.json';
|
||||||
|
|
||||||
|
export const BRANCHES = [attA, attB, conA, conB, emoA, emoB, valA, valB, culA, culB] as BranchPack[];
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import type { BranchPack, Chapter } from './types';
|
||||||
|
import type { StoryEvent } from './eventStore';
|
||||||
|
import type { Confidences, ProfileDimension } from './scoring';
|
||||||
|
|
||||||
|
export const CONFIDENCE_THRESHOLD = 0.6;
|
||||||
|
const WEIGHTS = { attachment: 5, values: 4, conflict_style: 3, emotional_stability: 2, culture: 1 };
|
||||||
|
|
||||||
|
export interface BranchDecision {
|
||||||
|
branch?: BranchPack;
|
||||||
|
evidenceInsufficient: ProfileDimension[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 皮肤绑定章节,不跨章搬运;稀缺度按不同选择点计数,追问不重复计票。 */
|
||||||
|
export function decideInsertion({ confidences, events, branches, target, completedChapter }: {
|
||||||
|
confidences: Confidences;
|
||||||
|
completedChapter?: Chapter;
|
||||||
|
events: StoryEvent[];
|
||||||
|
branches: BranchPack[];
|
||||||
|
target?: Chapter;
|
||||||
|
}): BranchDecision {
|
||||||
|
const used = new Set(events.filter((e) => e.kind === 'branch_inserted').map((e) => e.branchId));
|
||||||
|
const low = (dimension: ProfileDimension) => {
|
||||||
|
const c = confidences[dimension];
|
||||||
|
return c !== undefined && Number.isFinite(c) && c >= 0 && c < CONFIDENCE_THRESHOLD;
|
||||||
|
};
|
||||||
|
const evidenceInsufficient = (Object.keys(confidences) as ProfileDimension[]).filter((d) =>
|
||||||
|
low(d) && (!branches.some((b) => b.dimension === d) || branches.filter((b) => b.dimension === d).every((b) => used.has(b.id))),
|
||||||
|
);
|
||||||
|
if (!target || events.some((e) => e.kind === 'branch_inserted' && e.chapterId === target.id)) return { evidenceInsufficient };
|
||||||
|
const sampled = events.filter((e) => e.kind === 'choice' || e.kind === 'narration');
|
||||||
|
const unplayedPuzzle = completedChapter?.nodes.some((n) => n.type === 'choice' && n.dimensions.includes('culture') &&
|
||||||
|
!sampled.some((e) => e.chapterId === completedChapter.id && e.pointId === n.pointId));
|
||||||
|
const candidates = branches.filter((b) => b.anchor.chapter === target.id && low(b.dimension) && !used.has(b.id) &&
|
||||||
|
target.nodes.some((n) => n.id === b.anchor.afterNode && 'next' in n) &&
|
||||||
|
(b.dimension !== 'culture' || !unplayedPuzzle) &&
|
||||||
|
(target.estimatedMinutes ?? 35) + b.estimatedMinutes <= 45);
|
||||||
|
const priority = (b: BranchPack) => WEIGHTS[b.dimension] * (CONFIDENCE_THRESHOLD - confidences[b.dimension]!) /
|
||||||
|
(1 + new Set(sampled.filter((e) => e.dimensions.includes(b.dimension)).map((e) => e.pointId)).size);
|
||||||
|
candidates.sort((a, b) => priority(b) - priority(a) || a.id.localeCompare(b.id));
|
||||||
|
return { branch: candidates[0], evidenceInsufficient };
|
||||||
|
}
|
||||||
+163
-20
@@ -1,4 +1,6 @@
|
|||||||
import type { Chapter, FollowUpDimension, IdentityProfile, NarrationNode, SceneVariant, StoryNode } from './types';
|
import type { BranchPack, Chapter, ChoiceBehavior, FollowUpDimension, IdentityProfile, NarrationNode, SceneVariant, StoryNode } from './types';
|
||||||
|
import { decideInsertion } from './branching';
|
||||||
|
import { extractConfidences, scoreProfile } from './scoring';
|
||||||
import { FOLLOW_UP_DIMENSIONS, FOLLOW_UP_PRIORITY } from './types';
|
import { FOLLOW_UP_DIMENSIONS, FOLLOW_UP_PRIORITY } from './types';
|
||||||
import type { EventStore, StoryEvent } from './eventStore';
|
import type { EventStore, StoryEvent } from './eventStore';
|
||||||
import { makeEvent } from './eventStore';
|
import { makeEvent } from './eventStore';
|
||||||
@@ -24,6 +26,7 @@ export interface EngineState {
|
|||||||
playerName: string;
|
playerName: string;
|
||||||
/** 全部三章完成后为 true → 进入画像报告 */
|
/** 全部三章完成后为 true → 进入画像报告 */
|
||||||
finished: boolean;
|
finished: boolean;
|
||||||
|
activeBranchId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NarrationResult {
|
export interface NarrationResult {
|
||||||
@@ -82,9 +85,13 @@ export class StoryEngine {
|
|||||||
private jev: JevClient;
|
private jev: JevClient;
|
||||||
private progress?: ProgressStore;
|
private progress?: ProgressStore;
|
||||||
private nodeIndex: Map<string, StoryNode> = new Map();
|
private nodeIndex: Map<string, StoryNode> = new Map();
|
||||||
|
readonly insertions: BranchPack[] = [];
|
||||||
|
private branches: BranchPack[];
|
||||||
|
private chapterTransition: Promise<void> | null = null;
|
||||||
|
|
||||||
constructor(chapters: Chapter[], store: EventStore, jev: JevClient, startChapterId?: string, progress?: ProgressStore) {
|
constructor(chapters: Chapter[], store: EventStore, jev: JevClient, startChapterId?: string, progress?: ProgressStore, branches: BranchPack[] = []) {
|
||||||
if (chapters.length === 0) throw new Error('StoryEngine: no chapters');
|
if (chapters.length === 0) throw new Error('StoryEngine: no chapters');
|
||||||
|
this.branches = branches;
|
||||||
this.chapters = chapters;
|
this.chapters = chapters;
|
||||||
this.store = store;
|
this.store = store;
|
||||||
this.jev = jev;
|
this.jev = jev;
|
||||||
@@ -119,18 +126,15 @@ export class StoryEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** dialogue / cutscene / chapterEnd 前进一步 */
|
/** dialogue / cutscene / chapterEnd 前进一步 */
|
||||||
advance(): void {
|
async advance(): Promise<void> {
|
||||||
|
if (this.chapterTransition) return this.chapterTransition;
|
||||||
const node = this.currentNode();
|
const node = this.currentNode();
|
||||||
if (node.type === 'dialogue' || node.type === 'cutscene') {
|
if (node.type === 'dialogue' || node.type === 'cutscene') {
|
||||||
this.state.nodeId = node.next;
|
this.moveTo(node.next);
|
||||||
} else if (node.type === 'chapterEnd') {
|
} else if (node.type === 'chapterEnd') {
|
||||||
if (node.nextChapter) {
|
this.chapterTransition = this.finishChapter(node.nextChapter);
|
||||||
this.state.chapterId = node.nextChapter;
|
try { await this.chapterTransition; } finally { this.chapterTransition = null; }
|
||||||
this.state.nodeId = this.currentChapter().startNodeId;
|
return;
|
||||||
this.state.activeVariant = null;
|
|
||||||
} else {
|
|
||||||
this.state.finished = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error(`advance() 不适用于 ${node.type} 节点`);
|
throw new Error(`advance() 不适用于 ${node.type} 节点`);
|
||||||
}
|
}
|
||||||
@@ -138,13 +142,99 @@ export class StoryEngine {
|
|||||||
this.persist();
|
this.persist();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 进度持久化:每次状态迁移后落盘(真机刷新/热更新后从断点恢复) */
|
/** 章末只用本 session 的累计证据决策,故障不阻断主线。 */
|
||||||
|
private async finishChapter(nextChapter?: string): Promise<void> {
|
||||||
|
const sessionId = this.state.sessionId;
|
||||||
|
const chapterId = this.state.chapterId;
|
||||||
|
if (this.branches.length && this.jev.adaptiveScoring !== false) {
|
||||||
|
try {
|
||||||
|
const events = await this.store.list(sessionId);
|
||||||
|
const target = this.chapters.find((c) => c.id === nextChapter);
|
||||||
|
// 事件先于快照落盘;中途刷新时复用原计划。
|
||||||
|
const existing = events.find((e) => e.kind === 'branch_inserted' && e.chapterId === nextChapter);
|
||||||
|
if (existing) {
|
||||||
|
const pack = this.branches.find((b) => b.id === existing.branchId);
|
||||||
|
if (pack) this.loadInsertion(pack);
|
||||||
|
} else {
|
||||||
|
const confidences = extractConfidences(await scoreProfile(events, this.jev));
|
||||||
|
if (sessionId !== this.state.sessionId) return;
|
||||||
|
const decision = decideInsertion({ confidences, events, branches: this.branches, target, completedChapter: this.currentChapter() });
|
||||||
|
for (const dimension of decision.evidenceInsufficient) {
|
||||||
|
if (events.some((e) => e.kind === 'evidence_insufficient' && e.dimensions.includes(dimension))) continue;
|
||||||
|
await this.store.append(makeEvent({ sessionId, chapterId, kind: 'evidence_insufficient',
|
||||||
|
pointId: `EVIDENCE-${dimension}`, dimensions: [dimension], confidence: confidences[dimension] }));
|
||||||
|
}
|
||||||
|
const branch = decision.branch;
|
||||||
|
if (branch) {
|
||||||
|
this.validateInsertion(branch);
|
||||||
|
await this.store.append(makeEvent({ sessionId, chapterId: branch.anchor.chapter, kind: 'branch_inserted',
|
||||||
|
pointId: branch.id, branchId: branch.id, dimensions: [branch.dimension], confidence: confidences[branch.dimension] }));
|
||||||
|
if (sessionId !== this.state.sessionId) return;
|
||||||
|
this.loadInsertion(branch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[engine] 章末评分/支线计划失败,本章不插入,继续主线', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sessionId !== this.state.sessionId) return;
|
||||||
|
if (nextChapter) {
|
||||||
|
this.state.chapterId = nextChapter;
|
||||||
|
this.state.nodeId = this.currentChapter().startNodeId;
|
||||||
|
this.state.activeVariant = null;
|
||||||
|
} else this.state.finished = true;
|
||||||
|
this.state.activeBranchId = undefined;
|
||||||
|
this.skipHiddenNodes();
|
||||||
|
this.persist();
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateInsertion(branch: BranchPack): void {
|
||||||
|
const anchor = this.nodeIndex.get(`${branch.anchor.chapter}/${branch.anchor.afterNode}`);
|
||||||
|
const ids = new Set(branch.nodes.map((n) => n.id));
|
||||||
|
if (!anchor || !('next' in anchor) || !ids.has(branch.startNodeId) || ids.size !== branch.nodes.length) throw new Error(`无效支线 ${branch.id}`);
|
||||||
|
for (const node of branch.nodes) {
|
||||||
|
if (node.type === 'chapterEnd' || this.chapters.find((c) => c.id === branch.anchor.chapter)?.nodes.some((n) => n.id === node.id)) throw new Error(`无效支线节点 ${node.id}`);
|
||||||
|
const nexts = node.type === 'choice' ? node.options.map((o) => o.next) : [node.next];
|
||||||
|
if (nexts.some((next) => next !== '$resume' && !ids.has(next))) throw new Error(`无效支线出口 ${node.id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private loadInsertion(branch: BranchPack): void {
|
||||||
|
if (this.insertions.some((b) => b.id === branch.id)) return;
|
||||||
|
if (this.insertions.some((b) => b.anchor.chapter === branch.anchor.chapter)) throw new Error('每章至多一条支线');
|
||||||
|
this.validateInsertion(branch);
|
||||||
|
this.insertions.push(branch);
|
||||||
|
for (const node of branch.nodes) this.nodeIndex.set(`${branch.anchor.chapter}/${node.id}`, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearInsertions(): void {
|
||||||
|
for (const b of this.insertions) for (const n of b.nodes) this.nodeIndex.delete(`${b.anchor.chapter}/${n.id}`);
|
||||||
|
this.insertions.length = 0;
|
||||||
|
this.state.activeBranchId = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private moveTo(next: string): void {
|
||||||
|
if (next === '$resume') {
|
||||||
|
const branch = this.insertions.find((b) => b.id === this.state.activeBranchId);
|
||||||
|
const anchor = branch && this.nodeIndex.get(`${branch.anchor.chapter}/${branch.anchor.afterNode}`);
|
||||||
|
if (!anchor || !('next' in anchor)) throw new Error('支线缺少返回挂点');
|
||||||
|
this.state.nodeId = anchor.next;
|
||||||
|
this.state.activeBranchId = undefined;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const insertion = this.insertions.find((b) => b.anchor.chapter === this.state.chapterId && b.anchor.afterNode === this.state.nodeId);
|
||||||
|
this.state.nodeId = insertion ? insertion.startNodeId : next;
|
||||||
|
if (insertion) this.state.activeBranchId = insertion.id;
|
||||||
|
}
|
||||||
|
|
||||||
private persist(): void {
|
private persist(): void {
|
||||||
this.progress?.save({
|
this.progress?.save({
|
||||||
sessionId: this.state.sessionId,
|
sessionId: this.state.sessionId,
|
||||||
chapterId: this.state.chapterId,
|
chapterId: this.state.chapterId,
|
||||||
nodeId: this.state.nodeId,
|
nodeId: this.state.nodeId,
|
||||||
flags: { ...this.state.flags },
|
flags: { ...this.state.flags },
|
||||||
|
activeBranchId: this.state.activeBranchId,
|
||||||
|
insertions: this.insertions.map((b) => b.id),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,6 +247,15 @@ export class StoryEngine {
|
|||||||
if (!snapshot) return false;
|
if (!snapshot) return false;
|
||||||
const chapter = this.chapters.find((c) => c.id === snapshot.chapterId);
|
const chapter = this.chapters.find((c) => c.id === snapshot.chapterId);
|
||||||
if (!chapter) return false;
|
if (!chapter) return false;
|
||||||
|
const events = await this.store.list(snapshot.sessionId);
|
||||||
|
this.clearInsertions();
|
||||||
|
const ids = new Set([...(snapshot.insertions ?? []), ...events.filter((e) => e.kind === 'branch_inserted').map((e) => e.branchId)]);
|
||||||
|
for (const id of ids) {
|
||||||
|
const branch = this.branches.find((b) => b.id === id);
|
||||||
|
if (branch) {
|
||||||
|
try { this.loadInsertion(branch); } catch (e) { console.warn('[engine] 无法恢复支线', e); }
|
||||||
|
}
|
||||||
|
}
|
||||||
const nodeExists = this.nodeIndex.has(`${snapshot.chapterId}/${snapshot.nodeId}`);
|
const nodeExists = this.nodeIndex.has(`${snapshot.chapterId}/${snapshot.nodeId}`);
|
||||||
this.state.sessionId = snapshot.sessionId;
|
this.state.sessionId = snapshot.sessionId;
|
||||||
this.state.chapterId = snapshot.chapterId;
|
this.state.chapterId = snapshot.chapterId;
|
||||||
@@ -165,8 +264,8 @@ export class StoryEngine {
|
|||||||
this.state.activeVariant = null;
|
this.state.activeVariant = null;
|
||||||
this.state.pendingFollowUp = null;
|
this.state.pendingFollowUp = null;
|
||||||
this.state.finished = false;
|
this.state.finished = false;
|
||||||
const events = await this.store.list(snapshot.sessionId);
|
|
||||||
const profile = [...events].reverse().find((e) => e.kind === 'profile')?.profile;
|
const profile = [...events].reverse().find((e) => e.kind === 'profile')?.profile;
|
||||||
|
this.state.activeBranchId = this.insertions.find((b) => b.anchor.chapter === snapshot.chapterId && b.nodes.some((n) => n.id === this.state.nodeId))?.id;
|
||||||
this.state.playerName = profile?.name ?? '';
|
this.state.playerName = profile?.name ?? '';
|
||||||
this.skipHiddenNodes();
|
this.skipHiddenNodes();
|
||||||
this.persist();
|
this.persist();
|
||||||
@@ -183,7 +282,7 @@ export class StoryEngine {
|
|||||||
if (node.type !== 'dialogue' && node.type !== 'cutscene') return;
|
if (node.type !== 'dialogue' && node.type !== 'cutscene') return;
|
||||||
const text = node.type === 'dialogue' ? node.text : node.caption;
|
const text = node.type === 'dialogue' ? node.text : node.caption;
|
||||||
if (resolveConditionalText(text, this.state.flags) !== null) return;
|
if (resolveConditionalText(text, this.state.flags) !== null) return;
|
||||||
this.state.nodeId = node.next;
|
this.moveTo(node.next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,9 +297,23 @@ export class StoryEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** B 类选择:落事件并按选项跳转 */
|
/** B 类选择:落事件并按选项跳转 */
|
||||||
async choose(optionId: string): Promise<void> {
|
async choose(optionId: string, behavior?: ChoiceBehavior): Promise<void> {
|
||||||
const node = this.currentNode();
|
const node = this.currentNode();
|
||||||
if (node.type !== 'choice') throw new Error('当前节点不是选择点');
|
if (node.type !== 'choice') throw new Error('当前节点不是选择点');
|
||||||
|
if (behavior?.decisionMs !== undefined && (!Number.isFinite(behavior.decisionMs) || behavior.decisionMs < 0)) throw new Error('无效决策耗时');
|
||||||
|
if (behavior?.interrupted) behavior = { ...behavior, decisionMs: undefined };
|
||||||
|
if (behavior?.timedOut && node.timer?.optionId !== optionId) throw new Error('无效超时选项');
|
||||||
|
if (node.puzzle) {
|
||||||
|
const puzzle = node.puzzle;
|
||||||
|
if (optionId.endsWith('-EXIT')) behavior = { hintsUsed: behavior?.hintsUsed ?? 0, solved: false };
|
||||||
|
else {
|
||||||
|
if (!behavior?.answers || behavior.answers.length !== puzzle.questions.length || behavior.answers.some((a, i) => !Number.isInteger(a) || a < 0 || a >= puzzle.questions[i].options.length)) throw new Error('请完成谜面两问');
|
||||||
|
const hints = behavior.hintsUsed ?? 0;
|
||||||
|
if (!Number.isInteger(hints) || hints < 0 || hints > puzzle.hints.length) throw new Error('无效提示档数');
|
||||||
|
const answerCorrect = behavior.answers.map((a, i) => a === puzzle.questions[i].answer);
|
||||||
|
behavior = { ...behavior, hintsUsed: hints, answerCorrect, solved: answerCorrect.every(Boolean) };
|
||||||
|
}
|
||||||
|
}
|
||||||
const option = node.options.find((o) => o.id === optionId);
|
const option = node.options.find((o) => o.id === optionId);
|
||||||
if (!option) throw new Error(`未知选项 ${optionId}`);
|
if (!option) throw new Error(`未知选项 ${optionId}`);
|
||||||
await this.store.append(
|
await this.store.append(
|
||||||
@@ -211,11 +324,13 @@ export class StoryEngine {
|
|||||||
pointId: node.pointId,
|
pointId: node.pointId,
|
||||||
dimensions: node.dimensions,
|
dimensions: node.dimensions,
|
||||||
optionId: option.id,
|
optionId: option.id,
|
||||||
optionLabel: option.label,
|
optionLabel: this.renderText(node.puzzle ? `${node.prompt} ${option.label};${node.puzzle.questions.map((q, i) => `${q.prompt} ${q.options[behavior?.answers?.[i] ?? -1] ?? '未作答'}`).join(';')}` : option.label),
|
||||||
|
behavior,
|
||||||
|
branchId: this.state.activeBranchId,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
Object.assign(this.state.flags, choiceFlags(node.pointId, option.id));
|
Object.assign(this.state.flags, choiceFlags(node.pointId, option.id, behavior));
|
||||||
this.state.nodeId = option.next;
|
this.moveTo(option.next);
|
||||||
this.skipHiddenNodes();
|
this.skipHiddenNodes();
|
||||||
this.persist();
|
this.persist();
|
||||||
}
|
}
|
||||||
@@ -229,6 +344,7 @@ export class StoryEngine {
|
|||||||
async submitNarration(transcript: string | null): Promise<NarrationResult | null> {
|
async submitNarration(transcript: string | null): Promise<NarrationResult | null> {
|
||||||
const node = this.currentNode();
|
const node = this.currentNode();
|
||||||
if (node.type !== 'narration') throw new Error('当前节点不是讲述点');
|
if (node.type !== 'narration') throw new Error('当前节点不是讲述点');
|
||||||
|
if (node.pointId === 'C3-02') throw new Error('问心之室仅记录路径,不接收讲述文本');
|
||||||
if (this.state.pendingFollowUp) throw new Error('有未处理的追问,请先 answerFollowUp / skipFollowUp');
|
if (this.state.pendingFollowUp) throw new Error('有未处理的追问,请先 answerFollowUp / skipFollowUp');
|
||||||
if (transcript === null) return this.finalizeNarration(node, null);
|
if (transcript === null) return this.finalizeNarration(node, null);
|
||||||
// Jev 故障(CORS / 网络)时降级:跳过追问环节,剧情继续,不卡死
|
// Jev 故障(CORS / 网络)时降级:跳过追问环节,剧情继续,不卡死
|
||||||
@@ -331,7 +447,31 @@ export class StoryEngine {
|
|||||||
await this.store.append(event);
|
await this.store.append(event);
|
||||||
Object.assign(this.state.flags, narrationFlags(node.pointId, variant.variantId, transcript));
|
Object.assign(this.state.flags, narrationFlags(node.pointId, variant.variantId, transcript));
|
||||||
this.state.activeVariant = variant;
|
this.state.activeVariant = variant;
|
||||||
this.state.nodeId = node.next;
|
this.moveTo(node.next);
|
||||||
|
this.skipHiddenNodes();
|
||||||
|
this.persist();
|
||||||
|
return { variant, event };
|
||||||
|
}
|
||||||
|
|
||||||
|
async resolvePrivateIllusion(mode: 'farewell_done' | 'farewell_partial' | 'exit_room'): Promise<NarrationResult> {
|
||||||
|
const node = this.currentNode();
|
||||||
|
if (node.type !== 'narration' || node.pointId !== 'C3-02') throw new Error('当前节点不是问心之室');
|
||||||
|
const variant = await this.resolveVariant(node, null);
|
||||||
|
const event = makeEvent({
|
||||||
|
sessionId: this.state.sessionId,
|
||||||
|
chapterId: this.state.chapterId,
|
||||||
|
kind: 'narration',
|
||||||
|
pointId: node.pointId,
|
||||||
|
dimensions: [],
|
||||||
|
variantId: variant.variantId,
|
||||||
|
narrationMode: mode,
|
||||||
|
transcript: null,
|
||||||
|
});
|
||||||
|
await this.store.append(event);
|
||||||
|
this.state.flags.c3_02_mode = mode;
|
||||||
|
this.state.flags.c3_02_fantasy_density = variant.variantId === 'generic_fog' ? 'fog' : 'partial';
|
||||||
|
this.state.activeVariant = variant;
|
||||||
|
this.moveTo(node.next);
|
||||||
this.skipHiddenNodes();
|
this.skipHiddenNodes();
|
||||||
this.persist();
|
this.persist();
|
||||||
return { variant, event };
|
return { variant, event };
|
||||||
@@ -339,6 +479,8 @@ export class StoryEngine {
|
|||||||
|
|
||||||
/** 沉默永远是合法选项(CONTEXT:在什么面前沉默本身也是数据);沉默路径不触发追问 */
|
/** 沉默永远是合法选项(CONTEXT:在什么面前沉默本身也是数据);沉默路径不触发追问 */
|
||||||
async skipNarration(): Promise<NarrationResult> {
|
async skipNarration(): Promise<NarrationResult> {
|
||||||
|
const node = this.currentNode();
|
||||||
|
if (node.type === 'narration' && node.pointId === 'C3-02') return this.resolvePrivateIllusion('exit_room');
|
||||||
const r = await this.submitNarration(null);
|
const r = await this.submitNarration(null);
|
||||||
if (!r) throw new Error('unreachable: 沉默路径不触发追问');
|
if (!r) throw new Error('unreachable: 沉默路径不触发追问');
|
||||||
return r;
|
return r;
|
||||||
@@ -485,8 +627,8 @@ export class StoryEngine {
|
|||||||
profile,
|
profile,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
this.state.nodeId = node.next;
|
|
||||||
this.state.playerName = profile.name.trim();
|
this.state.playerName = profile.name.trim();
|
||||||
|
this.moveTo(node.next);
|
||||||
this.skipHiddenNodes();
|
this.skipHiddenNodes();
|
||||||
this.persist();
|
this.persist();
|
||||||
}
|
}
|
||||||
@@ -522,6 +664,7 @@ export class StoryEngine {
|
|||||||
const chapter = this.chapters.find((c) => c.id === (chapterId ?? this.state.chapterId));
|
const chapter = this.chapters.find((c) => c.id === (chapterId ?? this.state.chapterId));
|
||||||
if (!chapter) throw new Error(`unknown chapter ${chapterId}`);
|
if (!chapter) throw new Error(`unknown chapter ${chapterId}`);
|
||||||
this.state.sessionId = newSessionId();
|
this.state.sessionId = newSessionId();
|
||||||
|
this.clearInsertions();
|
||||||
this.state.chapterId = chapter.id;
|
this.state.chapterId = chapter.id;
|
||||||
this.state.nodeId = chapter.startNodeId;
|
this.state.nodeId = chapter.startNodeId;
|
||||||
this.state.activeVariant = null;
|
this.state.activeVariant = null;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { DimensionTag, FollowUpDimension, IdentityProfile } from './types';
|
import type { ChoiceBehavior, DimensionTag, FollowUpDimension, IdentityProfile } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 选择/讲述事件存储(ADR-0004:历史不可涂改)。
|
* 选择/讲述事件存储(ADR-0004:历史不可涂改)。
|
||||||
@@ -17,7 +17,10 @@ export interface StoryEvent {
|
|||||||
sessionId: string;
|
sessionId: string;
|
||||||
ts: number;
|
ts: number;
|
||||||
chapterId: string;
|
chapterId: string;
|
||||||
kind: 'choice' | 'narration' | 'profile' | 'followup' | 'followup_skipped' | 'consent';
|
kind: 'choice' | 'narration' | 'profile' | 'followup' | 'followup_skipped' | 'consent' | 'branch_inserted' | 'evidence_insufficient';
|
||||||
|
branchId?: string;
|
||||||
|
confidence?: number;
|
||||||
|
behavior?: ChoiceBehavior;
|
||||||
/** 选择点 ID,如 C1-03;consent 事件固定为 'CONSENT' */
|
/** 选择点 ID,如 C1-03;consent 事件固定为 'CONSENT' */
|
||||||
pointId: string;
|
pointId: string;
|
||||||
dimensions: DimensionTag[];
|
dimensions: DimensionTag[];
|
||||||
@@ -29,6 +32,8 @@ export interface StoryEvent {
|
|||||||
/** narration 事件:选中的场景变体(情绪签名 → variantId) */
|
/** narration 事件:选中的场景变体(情绪签名 → variantId) */
|
||||||
variantId?: string;
|
variantId?: string;
|
||||||
emotionSignature?: string;
|
emotionSignature?: string;
|
||||||
|
/** C3-02 幻境:只记录路径,不保留在室内说的话 */
|
||||||
|
narrationMode?: 'farewell_done' | 'farewell_partial' | 'exit_room';
|
||||||
/** followup / followup_skipped 事件:追问针对的缺失维度 */
|
/** followup / followup_skipped 事件:追问针对的缺失维度 */
|
||||||
followUpDimension?: FollowUpDimension;
|
followUpDimension?: FollowUpDimension;
|
||||||
/** 讲述/追问的转写文本(Web Speech API 只给文本,不留音频,符合 ADR-0004 即焚原则;沉默为 null) */
|
/** 讲述/追问的转写文本(Web Speech API 只给文本,不留音频,符合 ADR-0004 即焚原则;沉默为 null) */
|
||||||
|
|||||||
+92
-14
@@ -1,4 +1,5 @@
|
|||||||
import type { StoryEvent } from './eventStore';
|
import type { StoryEvent } from './eventStore';
|
||||||
|
import type { ChoiceBehavior } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分支变量(flag)系统 —— 依据 docs/script/act1.md 附录 A。
|
* 分支变量(flag)系统 —— 依据 docs/script/act1.md 附录 A。
|
||||||
@@ -17,15 +18,15 @@ import type { StoryEvent } from './eventStore';
|
|||||||
*/
|
*/
|
||||||
export type Flags = Record<string, string>;
|
export type Flags = Record<string, string>;
|
||||||
|
|
||||||
/** 解谜点正确答案(optionId),用于 *_solved 变量 */
|
/** 单选解谜点正确答案(optionId);C3-01 按两题行为记录单独聚合。 */
|
||||||
const PUZZLE_ANSWERS: Record<string, string> = {
|
const PUZZLE_ANSWERS: Record<string, string> = {
|
||||||
'C1-05': 'stele_guardian',
|
'C1-05': 'stele_guardian',
|
||||||
'C2-04': 'heart_xin',
|
'C2-04': 'heart_xin',
|
||||||
'C3-01': 'rite_correct',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 选择事件 → 变量赋值(变量名与取值对齐附录 A;optionId 已与枚举值对齐) */
|
/** 选择事件 → 变量赋值(变量名与取值对齐附录 A;optionId 已与枚举值对齐) */
|
||||||
export function choiceFlags(pointId: string, optionId: string): Flags {
|
export function choiceFlags(pointId: string, optionId: string, behavior?: ChoiceBehavior): Flags {
|
||||||
|
if (pointId.startsWith('CB-')) return branchChoiceFlags(pointId, optionId, behavior);
|
||||||
const f: Flags = {};
|
const f: Flags = {};
|
||||||
switch (pointId) {
|
switch (pointId) {
|
||||||
case 'C1-03':
|
case 'C1-03':
|
||||||
@@ -36,12 +37,17 @@ export function choiceFlags(pointId: string, optionId: string): Flags {
|
|||||||
if (optionId === 'd_play_along') f.flag_laogui_thread = 'true';
|
if (optionId === 'd_play_along') f.flag_laogui_thread = 'true';
|
||||||
break;
|
break;
|
||||||
case 'C1-05':
|
case 'C1-05':
|
||||||
case 'C2-04':
|
case 'C2-04': {
|
||||||
case 'C3-01': {
|
|
||||||
const prefix = pointId.toLowerCase().replace('-', '_');
|
const prefix = pointId.toLowerCase().replace('-', '_');
|
||||||
f[`${prefix}_solved`] = String(optionId === PUZZLE_ANSWERS[pointId]);
|
f[`${prefix}_solved`] = String(optionId === PUZZLE_ANSWERS[pointId]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'C3-01': {
|
||||||
|
f.c3_01_hints_used = String(behavior?.hintsUsed ?? 0);
|
||||||
|
const correct = behavior?.answerCorrect?.filter(Boolean).length ?? 0;
|
||||||
|
f.c3_01_solved = correct === 2 ? 'true' : correct === 1 ? 'partial' : 'false';
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 'C1-06':
|
case 'C1-06':
|
||||||
f.c1_06_landing = optionId; // a_safe / b_precise
|
f.c1_06_landing = optionId; // a_safe / b_precise
|
||||||
break;
|
break;
|
||||||
@@ -61,9 +67,9 @@ export function choiceFlags(pointId: string, optionId: string): Flags {
|
|||||||
break;
|
break;
|
||||||
case 'C3-03':
|
case 'C3-03':
|
||||||
f.c3_03_choice = optionId;
|
f.c3_03_choice = optionId;
|
||||||
f.atang_injury = optionId === 'a_person' ? 'bruise' : optionId === 'b_relic' ? 'fracture' : 'sprain';
|
f.atang_injury = 'fracture';
|
||||||
f.wenjian_injury = optionId === 'c_command' ? 'shoulder' : 'none';
|
f.relic_loss = optionId === 'c_command' ? 'outer_three' : 'one_ding';
|
||||||
f.relic_loss = optionId === 'a_person' ? 'one_ding' : 'none';
|
f.relic_record = optionId === 'b_relic' ? 'full' : 'partial';
|
||||||
break;
|
break;
|
||||||
case 'C3-04':
|
case 'C3-04':
|
||||||
f.c3_04_stance = optionId;
|
f.c3_04_stance = optionId;
|
||||||
@@ -72,7 +78,7 @@ export function choiceFlags(pointId: string, optionId: string): Flags {
|
|||||||
break;
|
break;
|
||||||
case 'C3-05':
|
case 'C3-05':
|
||||||
f.c3_05_rope = optionId;
|
f.c3_05_rope = optionId;
|
||||||
f.flag_rope_holder = optionId === 'a_accept' ? 'you' : optionId === 'b_protocol' ? 'shenduo' : 'atang';
|
f.flag_rope_holder = optionId === 'a_accept' ? 'you' : 'shenduo';
|
||||||
break;
|
break;
|
||||||
case 'C3-06':
|
case 'C3-06':
|
||||||
f.flag_arch_doubt = 'true';
|
f.flag_arch_doubt = 'true';
|
||||||
@@ -104,7 +110,9 @@ export function narrationFlags(pointId: string, variantId: string | undefined, t
|
|||||||
const f: Flags = {};
|
const f: Flags = {};
|
||||||
switch (pointId) {
|
switch (pointId) {
|
||||||
case 'C1-02':
|
case 'C1-02':
|
||||||
if (transcript === null) f.c1_02_signature = 'silent';
|
f.c1_02_signature = transcript === null ? 'silent' : ({
|
||||||
|
sealed_love_tomb: 'warm_loss', war_graves: 'grief', empty_post_station: 'release', desecrated_niche: 'grievance',
|
||||||
|
} as Record<string, string>)[variantId ?? ''] ?? 'silent';
|
||||||
break;
|
break;
|
||||||
case 'C2-02': {
|
case 'C2-02': {
|
||||||
// S3(评审修订):四档物质所得严格相等(方位+时辰+完整谶语全给),flag_prophecy_heard 已废除
|
// S3(评审修订):四档物质所得严格相等(方位+时辰+完整谶语全给),flag_prophecy_heard 已废除
|
||||||
@@ -115,11 +123,10 @@ export function narrationFlags(pointId: string, variantId: string | undefined, t
|
|||||||
case 'C2-05': {
|
case 'C2-05': {
|
||||||
const depth = transcript === null ? 'silent' : variantId ? VARIANT_SIGNATURE[variantId] : undefined;
|
const depth = transcript === null ? 'silent' : variantId ? VARIANT_SIGNATURE[variantId] : undefined;
|
||||||
if (depth) f.c2_05_depth = depth;
|
if (depth) f.c2_05_depth = depth;
|
||||||
if (variantId === 'wenjian_close_early') f.flag_wenjian_secret = 'true';
|
if (variantId === 'wenjian_close_late') f.flag_wenjian_secret = 'true';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'C3-02':
|
case 'C3-02':
|
||||||
f.c3_02_mode = transcript === null ? 'exit_room' : 'farewell_done';
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return f;
|
return f;
|
||||||
@@ -129,14 +136,85 @@ export function narrationFlags(pointId: string, variantId: string | undefined, t
|
|||||||
export function deriveFlags(events: StoryEvent[]): Flags {
|
export function deriveFlags(events: StoryEvent[]): Flags {
|
||||||
const flags: Flags = {};
|
const flags: Flags = {};
|
||||||
for (const e of events) {
|
for (const e of events) {
|
||||||
if (e.kind === 'choice' && e.optionId) Object.assign(flags, choiceFlags(e.pointId, e.optionId));
|
if (e.kind === 'choice' && e.optionId) Object.assign(flags, choiceFlags(e.pointId, e.optionId, e.behavior));
|
||||||
else if (e.kind === 'narration') Object.assign(flags, narrationFlags(e.pointId, e.variantId, e.transcript ?? null));
|
else if (e.kind === 'narration') {
|
||||||
|
Object.assign(flags, narrationFlags(e.pointId, e.variantId, e.transcript ?? null));
|
||||||
|
if (e.pointId === 'C3-02' && e.narrationMode) {
|
||||||
|
flags.c3_02_mode = e.narrationMode;
|
||||||
|
flags.c3_02_fantasy_density = e.variantId === 'generic_fog' ? 'fog' : 'partial';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 条件文本解析 ----------
|
// ---------- 条件文本解析 ----------
|
||||||
|
|
||||||
|
const BRANCH_CHOICES: Record<string, [string, string[]]> = {
|
||||||
|
'CB-ATT-A-01': ['cb_att_a_search', ['a_rush', 'b_wait', 'c_organize']],
|
||||||
|
'CB-ATT-A-02': ['cb_att_a_reunion', ['a_scold', 'b_swallow', 'c_joke', 'd_care']],
|
||||||
|
'CB-ATT-B-01': ['cb_att_b_boundary', ['a_go', 'b_wait', 'c_proxy']],
|
||||||
|
'CB-CON-A-01': ['cb_con_a_ruling', ['a_settle', 'b_principle', 'c_invest', 'd_document']],
|
||||||
|
'CB-CON-A-02': ['cb_con_a_aftermath', ['a_comfort', 'b_mirror', 'c_leave']],
|
||||||
|
'CB-CON-B-01': ['cb_con_b_ruling', ['a_void', 'b_defer', 'c_reprice', 'd_document']],
|
||||||
|
'CB-CON-B-02': ['cb_con_b_channel', ['a_keep', 'b_report', 'c_cut']],
|
||||||
|
'CB-EMO-A-01': ['cb_emo_a_shelter', ['a_count', 'b_map', 'c_shelter']],
|
||||||
|
'CB-EMO-A-02': ['cb_emo_a_cry', ['a_look', 'b_hold', 'c_call']],
|
||||||
|
'CB-EMO-B-01': ['cb_emo_b_plate', ['a_alert', 'b_relay', 'c_record']],
|
||||||
|
'CB-EMO-B-02': ['cb_emo_b_vent', ['a_record', 'b_mark', 'c_leave']],
|
||||||
|
'CB-VAL-A-01': ['cb_val_a_water', ['a_give', 'b_cup', 'c_trade', 'd_redirect']],
|
||||||
|
'CB-VAL-A-02': ['cb_val_a_ledger', ['a_honest', 'b_smooth', 'c_defer']],
|
||||||
|
'CB-VAL-B-01': ['cb_val_b_medicine', ['a_use', 'b_refer', 'c_split', 'd_delegate']],
|
||||||
|
'CB-CUL-A-00': ['cb_cul_a_entry', ['enter', 'skip']],
|
||||||
|
};
|
||||||
|
|
||||||
|
function branchChoiceFlags(point: string, option: string, behavior?: ChoiceBehavior): Flags {
|
||||||
|
const f: Flags = {};
|
||||||
|
const letter = option.slice(point.length + 1);
|
||||||
|
const index = 'ABCD'.indexOf(letter);
|
||||||
|
const mapping = BRANCH_CHOICES[point];
|
||||||
|
if (mapping && index >= 0) f[mapping[0]] = mapping[1][index];
|
||||||
|
if (point.startsWith('CB-EMO-')) {
|
||||||
|
const prefix = point.slice(0, 8).toLowerCase().replaceAll('-', '_');
|
||||||
|
if (behavior?.decisionMs !== undefined) f[`${prefix}_rt${Number(point.slice(-2))}`] = String(behavior.decisionMs);
|
||||||
|
if (letter === 'TIMEOUT') f[mapping[0]] = point.includes('-A-') ? 'follow' : 'self_rescue';
|
||||||
|
}
|
||||||
|
if (point === 'CB-ATT-A-02') {
|
||||||
|
f.flag_side_stairs = 'true';
|
||||||
|
if (letter === 'A') f.att_debt = 'true';
|
||||||
|
}
|
||||||
|
if (point === 'CB-ATT-B-01') {
|
||||||
|
f.flag_pit27_figurines = 'true';
|
||||||
|
f[['att_seen', 'att_trusted', 'att_owed'][index]] = 'true';
|
||||||
|
}
|
||||||
|
if (point === 'CB-CON-A-01') {
|
||||||
|
f.market_warmth = letter === 'B' ? 'cold' : 'normal';
|
||||||
|
if (letter === 'C') f.flag_water_intel = 'true';
|
||||||
|
if (letter === 'D') f.flag_shiling_noticed = 'true';
|
||||||
|
}
|
||||||
|
if (point === 'CB-CON-B-01' && ['C', 'D'].includes(letter)) f.flag_palace_crates = 'true';
|
||||||
|
if (point === 'CB-CON-B-02') f.flag_fish_channel = String(letter === 'A');
|
||||||
|
if (point === 'CB-EMO-B-02') f[letter === 'A' ? 'flag_mould_rubbing' : 'flag_sealed_vault'] = 'true';
|
||||||
|
if (point === 'CB-VAL-A-01') {
|
||||||
|
if (['C', 'D'].includes(letter)) f.flag_silver_bracelet = 'true';
|
||||||
|
if (['A', 'C'].includes(letter)) f.flag_thirst_days = 'true';
|
||||||
|
}
|
||||||
|
if (point === 'CB-VAL-B-01') {
|
||||||
|
f.flag_hidden_stairs = 'true';
|
||||||
|
f['rapport.shouling'] = 'true';
|
||||||
|
if (letter === 'B') f.flag_herb_poultice = 'true';
|
||||||
|
}
|
||||||
|
if (point === 'CB-CUL-A-01' || point === 'CB-CUL-B-01') {
|
||||||
|
const prefix = point.slice(0, 8).toLowerCase().replaceAll('-', '_');
|
||||||
|
f[`${prefix}_hints_used`] = String(behavior?.hintsUsed ?? 0);
|
||||||
|
const correct = behavior?.answerCorrect?.filter(Boolean).length ?? (behavior?.solved ? 2 : 0);
|
||||||
|
f[`${prefix}_solved`] = option.endsWith('-EXIT') ? 'not_attempted' : correct === 2 ? 'true' : correct === 1 ? 'partial' : 'false';
|
||||||
|
if (option.endsWith('-EXIT')) f[`${prefix}_left`] = 'true';
|
||||||
|
f[point.includes('-A-') ? 'flag_wall_relocation' : 'flag_tenth_king'] = 'true';
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
const CONDITION_RE = /【若([^】]+)】/g;
|
const CONDITION_RE = /【若([^】]+)】/g;
|
||||||
|
|
||||||
/** 求值单个条件表达式,如 `c2_06_jade = a_public`、`wenjian_health != weak` */
|
/** 求值单个条件表达式,如 `c2_06_jade = a_public`、`wenjian_health != weak` */
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ export interface ProgressSnapshot {
|
|||||||
nodeId: string;
|
nodeId: string;
|
||||||
/** 分支变量随位置一起持久化,恢复后条件文本渲染不断档 */
|
/** 分支变量随位置一起持久化,恢复后条件文本渲染不断档 */
|
||||||
flags: Flags;
|
flags: Flags;
|
||||||
|
activeBranchId?: string;
|
||||||
|
insertions?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProgressStore {
|
export interface ProgressStore {
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import type { StoryEvent } from './eventStore';
|
||||||
|
import type { JevClient } from '../services/jev';
|
||||||
|
|
||||||
|
export const PROFILE_DIMENSIONS = [
|
||||||
|
{ id: 'attachment', label: '依恋类型', description: '你在亲密与分离面前的姿态' },
|
||||||
|
{ id: 'conflict_style', label: '冲突处理风格', description: '分歧当前,你先做什么' },
|
||||||
|
{ id: 'values', label: '价值观与人生优先级', description: '两体面或两难堪之间,你让什么先走' },
|
||||||
|
{ id: 'emotional_stability', label: '情绪稳定性', description: '突发与悔恨面前的波形' },
|
||||||
|
{ id: 'risk', label: '风险偏好', description: '稳妥落点与精确落点之间的你' },
|
||||||
|
{ id: 'culture', label: '文化底蕴', description: '碑文、星象与礼制,实测而非自报' },
|
||||||
|
{ id: 'mate_preference', label: '择偶偏好', description: '你信任谁、把后背交给谁' },
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
export type ProfileDimension = (typeof PROFILE_DIMENSIONS)[number]['id'];
|
||||||
|
export interface DimensionScore {
|
||||||
|
id: ProfileDimension;
|
||||||
|
label: string;
|
||||||
|
score: number;
|
||||||
|
max: number;
|
||||||
|
confidence: number;
|
||||||
|
}
|
||||||
|
export type Confidences = Partial<Record<ProfileDimension, number>>;
|
||||||
|
|
||||||
|
/** 身份与同意不是人格证据;路由标记只说明采样路径,不作为性格结论。 */
|
||||||
|
export function buildState(events: StoryEvent[]): string {
|
||||||
|
const lines = events.flatMap((e) => {
|
||||||
|
if (e.pointId === 'C3-02') return [];
|
||||||
|
const prefix = `[轨迹 ${e.sessionId} / ${e.pointId}]`;
|
||||||
|
switch (e.kind) {
|
||||||
|
case 'choice':
|
||||||
|
return [`${prefix} 选择「${e.optionLabel}」(维度: ${e.dimensions.join('/')})` +
|
||||||
|
(e.behavior ? ` 行为: ${JSON.stringify(e.behavior)}` : '')];
|
||||||
|
case 'narration':
|
||||||
|
return [`${prefix} ${e.transcript === null ? '沉默' : `讲述: ${e.transcript}`} 情绪签名: ${e.emotionSignature ?? '未知'}`];
|
||||||
|
case 'followup': return [`${prefix} 追问(${e.followUpDimension})回答: ${e.transcript}`];
|
||||||
|
case 'followup_skipped': return [`${prefix} 追问(${e.followUpDimension})回答: 记不清了(跳过)`];
|
||||||
|
case 'branch_inserted': return [`${prefix} 采样路径: ${e.branchId},补测 ${e.dimensions.join('/')},触发置信度 ${e.confidence}(不是行为证据)`];
|
||||||
|
case 'evidence_insufficient': return [`${prefix} 路由标记: ${e.dimensions.join('/')} 证据不足(不是性格结论)`];
|
||||||
|
default: return [];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return `用户在互动剧情《苍梧疑冢》第一幕中的行为轨迹(按时间排序,轨迹分开比较)。支线与主线同等采样;结论不一致也是信号,不回滚。沉默、跳过和使用提示不等于低人格水平。路径标记不可当作行为证据:\n${lines.join('\n')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function scoreProfile(events: StoryEvent[], jev: JevClient): Promise<DimensionScore[]> {
|
||||||
|
const criteria = Array.from({ length: 10 }, (_, i) => `${i + 1} 级`);
|
||||||
|
const answers = await jev.ask(buildState(events), Object.fromEntries(PROFILE_DIMENSIONS.map((d) => [d.id, {
|
||||||
|
type: 'score' as const,
|
||||||
|
instructions: `根据用户行为轨迹,评估其「${d.label}」维度水平:${d.description}。仅按已观察到的行为判断,证据少或矛盾时降低置信度。`,
|
||||||
|
criteria,
|
||||||
|
}])));
|
||||||
|
return PROFILE_DIMENSIONS.map((d) => {
|
||||||
|
const a = answers?.[d.id];
|
||||||
|
if (a?.type !== 'score' || !Number.isFinite(a.score) || a.score < 0 || a.score > 9 ||
|
||||||
|
!Number.isFinite(a.confidence) || a.confidence < 0 || a.confidence > 1) {
|
||||||
|
throw new Error(`Jev 返回无效的 ${d.id} 评分`);
|
||||||
|
}
|
||||||
|
return { id: d.id, label: d.label, score: Math.round((a.score + 1) * 10) / 10, max: 10, confidence: a.confidence };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractConfidences(scores: DimensionScore[]): Confidences {
|
||||||
|
return Object.fromEntries(scores.map((s) => [s.id, s.confidence]));
|
||||||
|
}
|
||||||
@@ -42,6 +42,9 @@ export interface ChoiceNode extends BaseNode {
|
|||||||
/** 承载维度标签,如 ["values", "conflict_style"] */
|
/** 承载维度标签,如 ["values", "conflict_style"] */
|
||||||
dimensions: DimensionTag[];
|
dimensions: DimensionTag[];
|
||||||
options: ChoiceOption[];
|
options: ChoiceOption[];
|
||||||
|
timer?: { seconds: number; optionId: string };
|
||||||
|
untimed?: boolean;
|
||||||
|
puzzle?: { questions: { prompt: string; options: string[]; answer: number }[]; hints: string[]; exitLabel?: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChoiceOption {
|
export interface ChoiceOption {
|
||||||
@@ -146,9 +149,29 @@ export interface IdentityNode extends BaseNode {
|
|||||||
export type StoryNode = DialogueNode | ChoiceNode | NarrationNode | CutsceneNode | IdentityNode | ChapterEndNode;
|
export type StoryNode = DialogueNode | ChoiceNode | NarrationNode | CutsceneNode | IdentityNode | ChapterEndNode;
|
||||||
|
|
||||||
export interface Chapter {
|
export interface Chapter {
|
||||||
|
estimatedMinutes?: number;
|
||||||
id: string;
|
id: string;
|
||||||
act: number;
|
act: number;
|
||||||
title: string;
|
title: string;
|
||||||
startNodeId: string;
|
startNodeId: string;
|
||||||
nodes: StoryNode[];
|
nodes: StoryNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface BranchPack {
|
||||||
|
id: string;
|
||||||
|
dimension: 'attachment' | 'values' | 'conflict_style' | 'emotional_stability' | 'culture';
|
||||||
|
anchor: { chapter: string; afterNode: string };
|
||||||
|
estimatedMinutes: number;
|
||||||
|
startNodeId: string;
|
||||||
|
nodes: StoryNode[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChoiceBehavior {
|
||||||
|
decisionMs?: number;
|
||||||
|
timedOut?: boolean;
|
||||||
|
interrupted?: boolean;
|
||||||
|
hintsUsed?: number;
|
||||||
|
answers?: number[];
|
||||||
|
answerCorrect?: boolean[];
|
||||||
|
solved?: boolean;
|
||||||
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export type JevAnswer =
|
|||||||
};
|
};
|
||||||
|
|
||||||
export interface JevClient {
|
export interface JevClient {
|
||||||
|
adaptiveScoring?: boolean;
|
||||||
ask(state: unknown, questions: Record<string, JevQuestion>): Promise<Record<string, JevAnswer>>;
|
ask(state: unknown, questions: Record<string, JevQuestion>): Promise<Record<string, JevAnswer>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +87,7 @@ export class HttpJevClient implements JevClient {
|
|||||||
headers.Authorization = `Bearer ${this.apiKey}`;
|
headers.Authorization = `Bearer ${this.apiKey}`;
|
||||||
}
|
}
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
|
signal: AbortSignal.timeout(15000),
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify({ model: 'jev-latest', state, questions } satisfies JevRequest),
|
body: JSON.stringify({ model: 'jev-latest', state, questions } satisfies JevRequest),
|
||||||
@@ -121,6 +123,7 @@ function prng(seed: number, n: number): number[] {
|
|||||||
|
|
||||||
/** 无 key 开发用:确定性伪随机返分(同一输入永远同一输出,输出形状与真实 API 对齐)。 */
|
/** 无 key 开发用:确定性伪随机返分(同一输入永远同一输出,输出形状与真实 API 对齐)。 */
|
||||||
export class MockJevClient implements JevClient {
|
export class MockJevClient implements JevClient {
|
||||||
|
adaptiveScoring = true;
|
||||||
async ask(state: unknown, questions: Record<string, JevQuestion>): Promise<Record<string, JevAnswer>> {
|
async ask(state: unknown, questions: Record<string, JevQuestion>): Promise<Record<string, JevAnswer>> {
|
||||||
const stateKey = JSON.stringify(state) ?? '';
|
const stateKey = JSON.stringify(state) ?? '';
|
||||||
const answers: Record<string, JevAnswer> = {};
|
const answers: Record<string, JevAnswer> = {};
|
||||||
@@ -177,6 +180,7 @@ export function getJevClient(): JevClient {
|
|||||||
} else {
|
} else {
|
||||||
console.warn('[jev] 未配置 VITE_JEV_PROXY_URL / VITE_TYPESAFE_API_KEY,降级为 MockJevClient(确定性伪随机返分)');
|
console.warn('[jev] 未配置 VITE_JEV_PROXY_URL / VITE_TYPESAFE_API_KEY,降级为 MockJevClient(确定性伪随机返分)');
|
||||||
singleton = new MockJevClient();
|
singleton = new MockJevClient();
|
||||||
|
singleton.adaptiveScoring = false;
|
||||||
}
|
}
|
||||||
return singleton;
|
return singleton;
|
||||||
}
|
}
|
||||||
|
|||||||
+95
-18
@@ -1,4 +1,5 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import type { ChoiceBehavior } from '../engine/types';
|
||||||
import type { StoryEngine, PendingFollowUp } from '../engine/engine';
|
import type { StoryEngine, PendingFollowUp } from '../engine/engine';
|
||||||
import type { ChoiceNode, FollowUpDimension, IdentityNode, IdentityProfile, NarrationNode } from '../engine/types';
|
import type { ChoiceNode, FollowUpDimension, IdentityNode, IdentityProfile, NarrationNode } from '../engine/types';
|
||||||
import { VoiceInput } from './VoiceInput';
|
import { VoiceInput } from './VoiceInput';
|
||||||
@@ -32,12 +33,24 @@ interface Props {
|
|||||||
export function StoryPlayer({ engine, onFinish }: Props) {
|
export function StoryPlayer({ engine, onFinish }: Props) {
|
||||||
// tick 只是触发重渲染;引擎是唯一事实源
|
// tick 只是触发重渲染;引擎是唯一事实源
|
||||||
const [tick, setTick] = useState(0);
|
const [tick, setTick] = useState(0);
|
||||||
|
const [advancing, setAdvancing] = useState(false);
|
||||||
|
const [error, setError] = useState('');
|
||||||
const rerender = () => setTick((t) => t + 1);
|
const rerender = () => setTick((t) => t + 1);
|
||||||
void tick;
|
void tick;
|
||||||
|
|
||||||
const node = engine.currentNode();
|
const node = engine.currentNode();
|
||||||
const chapter = engine.currentChapter();
|
const chapter = engine.currentChapter();
|
||||||
const variant = engine.state.activeVariant;
|
const variant = engine.state.activeVariant;
|
||||||
|
async function advance() {
|
||||||
|
if (advancing) return;
|
||||||
|
setAdvancing(true);
|
||||||
|
setError('');
|
||||||
|
try {
|
||||||
|
await engine.advance();
|
||||||
|
if (engine.state.finished) onFinish();
|
||||||
|
else rerender();
|
||||||
|
} catch (e) { setError(String(e)); } finally { setAdvancing(false); }
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -54,7 +67,7 @@ export function StoryPlayer({ engine, onFinish }: Props) {
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
{node.type === 'dialogue' && (
|
{node.type === 'dialogue' && (
|
||||||
<div className="dialogue-box" onClick={() => { engine.advance(); if (engine.state.finished) onFinish(); else rerender(); }}>
|
<div className="dialogue-box" onClick={advance}>
|
||||||
<div className="sprite" style={{ background: SPRITE_COLORS[node.sprite ?? 'scene'] }}>
|
<div className="sprite" style={{ background: SPRITE_COLORS[node.sprite ?? 'scene'] }}>
|
||||||
{node.speaker.slice(0, 1)}
|
{node.speaker.slice(0, 1)}
|
||||||
</div>
|
</div>
|
||||||
@@ -67,10 +80,21 @@ export function StoryPlayer({ engine, onFinish }: Props) {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{node.type === 'choice' && (
|
{node.type === 'choice' && (
|
||||||
<ChoiceView node={node} renderText={(t) => engine.renderText(t)} onPick={async (id) => { await engine.choose(id); rerender(); }} />
|
<ChoiceView key={node.id} node={node} renderText={(t) => engine.renderText(t)} onPick={async (id, behavior) => { await engine.choose(id, behavior); rerender(); }} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{node.type === 'narration' && (
|
{node.type === 'narration' && node.pointId === 'C3-02' && (
|
||||||
|
<div className="narration-box">
|
||||||
|
<div className="mechanism-tag">问心之室 · 仅你可见</div>
|
||||||
|
<p className="narration-prompt">{engine.renderText(node.prompt)}</p>
|
||||||
|
<p className="hint">这里不录音、不留讲述文本,也不把室内告别用于画像。</p>
|
||||||
|
<button className="primary" onClick={async () => { await engine.resolvePrivateIllusion('farewell_done'); rerender(); }}>走进幻境,完成告别</button>
|
||||||
|
<button className="secondary" onClick={async () => { await engine.resolvePrivateIllusion('farewell_partial'); rerender(); }}>走进幻境,中途折返</button>
|
||||||
|
<button className="secondary" onClick={async () => { await engine.resolvePrivateIllusion('exit_room'); rerender(); }}>不进入,转身离开</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{node.type === 'narration' && node.pointId !== 'C3-02' && (
|
||||||
<NarrationView
|
<NarrationView
|
||||||
node={node}
|
node={node}
|
||||||
pendingFollowUp={engine.state.pendingFollowUp}
|
pendingFollowUp={engine.state.pendingFollowUp}
|
||||||
@@ -84,7 +108,7 @@ export function StoryPlayer({ engine, onFinish }: Props) {
|
|||||||
{node.type === 'identity' && <IdentityView node={node} engine={engine} onDone={rerender} />}
|
{node.type === 'identity' && <IdentityView node={node} engine={engine} onDone={rerender} />}
|
||||||
|
|
||||||
{node.type === 'cutscene' && (
|
{node.type === 'cutscene' && (
|
||||||
<div className="cutscene" onClick={() => { engine.advance(); rerender(); }}>
|
<div className="cutscene" onClick={advance}>
|
||||||
<div className="cutscene-placeholder">▶ {node.video}</div>
|
<div className="cutscene-placeholder">▶ {node.video}</div>
|
||||||
<p>{engine.renderText(node.caption)}</p>
|
<p>{engine.renderText(node.caption)}</p>
|
||||||
<span className="hint">点击继续 ▸</span>
|
<span className="hint">点击继续 ▸</span>
|
||||||
@@ -96,41 +120,94 @@ export function StoryPlayer({ engine, onFinish }: Props) {
|
|||||||
<h2>{chapter.title} · 完</h2>
|
<h2>{chapter.title} · 完</h2>
|
||||||
<button
|
<button
|
||||||
className="primary"
|
className="primary"
|
||||||
onClick={() => {
|
disabled={advancing}
|
||||||
engine.advance();
|
aria-busy={advancing}
|
||||||
if (engine.state.finished) onFinish();
|
onClick={advance}
|
||||||
else rerender();
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{node.nextChapter ? '进入下一章' : '生成人格画像报告'}
|
{node.nextChapter ? '进入下一章' : '生成人格画像报告'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{error && <p role="alert">{error}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ChoiceView({ node, renderText, onPick }: { node: ChoiceNode; renderText: (t: string) => string; onPick: (id: string) => void }) {
|
function ChoiceView({ node, renderText, onPick }: { node: ChoiceNode; renderText: (t: string) => string; onPick: (id: string, behavior?: ChoiceBehavior) => Promise<void> }) {
|
||||||
const [busy, setBusy] = useState(false);
|
const [busy, setBusy] = useState(false);
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
const [hints, setHints] = useState(0);
|
||||||
|
const [answers, setAnswers] = useState<number[]>(() => node.puzzle?.questions.map(() => -1) ?? []);
|
||||||
|
const [remaining, setRemaining] = useState(node.timer?.seconds ?? 0);
|
||||||
|
const started = useRef(0);
|
||||||
|
const submitted = useRef(false);
|
||||||
|
const interrupted = useRef(false);
|
||||||
|
const pickRef = useRef(onPick);
|
||||||
|
useEffect(() => { pickRef.current = onPick; }, [onPick]);
|
||||||
|
useEffect(() => { started.current = Date.now(); }, []);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!node.timer) return;
|
||||||
|
const markInterrupted = () => { if (document.hidden) interrupted.current = true; };
|
||||||
|
document.addEventListener('visibilitychange', markInterrupted);
|
||||||
|
return () => document.removeEventListener('visibilitychange', markInterrupted);
|
||||||
|
}, [node.timer]);
|
||||||
|
async function pick(id: string, timedOut = false) {
|
||||||
|
if (submitted.current) return;
|
||||||
|
submitted.current = true;
|
||||||
|
setBusy(true);
|
||||||
|
setError('');
|
||||||
|
try {
|
||||||
|
await pickRef.current(id, {
|
||||||
|
decisionMs: node.untimed || interrupted.current ? undefined : Math.max(0, Date.now() - started.current),
|
||||||
|
timedOut,
|
||||||
|
interrupted: interrupted.current || undefined,
|
||||||
|
...(node.puzzle ? { answers, hintsUsed: hints } : {}),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
submitted.current = false;
|
||||||
|
setError(String(e));
|
||||||
|
setBusy(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const timeoutPick = useRef(pick);
|
||||||
|
useEffect(() => { timeoutPick.current = pick; });
|
||||||
|
useEffect(() => {
|
||||||
|
if (!node.timer) return;
|
||||||
|
const timer = node.timer;
|
||||||
|
const interval = window.setInterval(() => {
|
||||||
|
const left = Math.max(0, timer.seconds - (Date.now() - started.current) / 1000);
|
||||||
|
setRemaining(left);
|
||||||
|
if (left === 0 && !submitted.current) void timeoutPick.current(timer.optionId, true);
|
||||||
|
}, 100);
|
||||||
|
return () => window.clearInterval(interval);
|
||||||
|
}, [node.timer]);
|
||||||
return (
|
return (
|
||||||
<div className="choice-box">
|
<div className="choice-box">
|
||||||
<p className="choice-prompt">{renderText(node.prompt)}</p>
|
<p className="choice-prompt">{renderText(node.prompt)}</p>
|
||||||
|
{node.timer && <progress aria-label="剩余决策时间" max={node.timer.seconds} value={remaining} />}
|
||||||
|
{node.puzzle && <>
|
||||||
|
{node.puzzle.questions.map((q, i) => <fieldset key={i} disabled={busy}>
|
||||||
|
<legend>{q.prompt}</legend>
|
||||||
|
{q.options.map((label, j) => <label key={j} className="puzzle-option">
|
||||||
|
<input type="radio" name={`${node.id}-${i}`} checked={answers[i] === j} onChange={() => setAnswers((old) => old.map((a, k) => k === i ? j : a))} />{label}
|
||||||
|
</label>)}
|
||||||
|
</fieldset>)}
|
||||||
|
{node.puzzle.hints.slice(0, hints).map((hint, i) => <p key={i}>{hint}</p>)}
|
||||||
|
<button className="secondary" disabled={busy || hints >= node.puzzle.hints.length} onClick={() => setHints((h) => h + 1)}>请教温简</button>
|
||||||
|
</>}
|
||||||
<div className="choice-options">
|
<div className="choice-options">
|
||||||
{node.options.map((o) => (
|
{node.options.filter((o) => o.id !== node.timer?.optionId).map((o) => (
|
||||||
<button
|
<button
|
||||||
key={o.id}
|
key={o.id}
|
||||||
className="choice-btn"
|
className="choice-btn"
|
||||||
disabled={busy}
|
disabled={busy || (!!node.puzzle && !o.id.endsWith('-EXIT') && answers.includes(-1))}
|
||||||
onClick={async () => {
|
onClick={() => pick(o.id)}
|
||||||
setBusy(true);
|
|
||||||
await onPick(o.id);
|
|
||||||
setBusy(false);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{renderText(o.label)}
|
{renderText(o.label)}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
{error && <p role="alert">{error}</p>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,18 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import type { EventStore, StoryEvent } from '../../engine/eventStore';
|
import type { EventStore } from '../../engine/eventStore';
|
||||||
import type { JevClient } from '../../services/jev';
|
import type { JevClient } from '../../services/jev';
|
||||||
import { BADGE_MAP, PROFILE_DIMENSIONS } from './badges';
|
import { BADGE_MAP } from './badges';
|
||||||
|
import { scoreProfile } from '../../engine/scoring';
|
||||||
|
import type { DimensionScore } from '../../engine/scoring';
|
||||||
|
export type { DimensionScore } from '../../engine/scoring';
|
||||||
import { ShareCard } from './ShareCard';
|
import { ShareCard } from './ShareCard';
|
||||||
|
|
||||||
export interface DimensionScore {
|
|
||||||
id: string;
|
|
||||||
label: string;
|
|
||||||
score: number;
|
|
||||||
max: number;
|
|
||||||
confidence: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
store: EventStore;
|
store: EventStore;
|
||||||
jev: JevClient;
|
jev: JevClient;
|
||||||
onReplay: () => void;
|
onReplay: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 把 append-only 事件轨迹汇总为喂给 Jev 的 state 文本(profile 身份字段是硬过滤字段,不进画像) */
|
|
||||||
function buildState(events: StoryEvent[]): string {
|
|
||||||
const lines = events
|
|
||||||
.filter((e) => e.kind !== 'profile')
|
|
||||||
.map((e) => {
|
|
||||||
if (e.kind === 'choice') {
|
|
||||||
return `[${e.pointId}] 选择「${e.optionLabel}」(维度: ${e.dimensions.join('/') || '字段采集'})`;
|
|
||||||
}
|
|
||||||
if (e.kind === 'followup') {
|
|
||||||
return `[${e.pointId}] 追问(${e.followUpDimension})回答: ${e.transcript}`;
|
|
||||||
}
|
|
||||||
if (e.kind === 'followup_skipped') {
|
|
||||||
return `[${e.pointId}] 追问(${e.followUpDimension})回答: 记不清了(跳过)`;
|
|
||||||
}
|
|
||||||
const t = e.transcript === null ? '(沉默)' : `(讲述: ${e.transcript})`;
|
|
||||||
return `[${e.pointId}] 讲述${t} 情绪签名: ${e.emotionSignature ?? '未知'}`;
|
|
||||||
});
|
|
||||||
return `用户在互动剧情《苍梧疑冢》第一幕中的行为轨迹(按时间排序,含初刷与重玩轨迹):\n${lines.join('\n')}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Act 1 结束后的画像报告页:Jev 一次调用并行问 7 个维度 score */
|
/** Act 1 结束后的画像报告页:Jev 一次调用并行问 7 个维度 score */
|
||||||
export function ReportPage({ store, jev, onReplay }: Props) {
|
export function ReportPage({ store, jev, onReplay }: Props) {
|
||||||
const [scores, setScores] = useState<DimensionScore[] | null>(null);
|
const [scores, setScores] = useState<DimensionScore[] | null>(null);
|
||||||
@@ -48,35 +23,14 @@ export function ReportPage({ store, jev, onReplay }: Props) {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
(async () => {
|
(async () => {
|
||||||
const events = await store.list();
|
|
||||||
// 最近一次身份登记的称呼(旧数据缺 name 字段时回退空串 → 只显示编号)
|
|
||||||
const name = [...events].reverse().find((e) => e.kind === 'profile')?.profile?.name ?? '';
|
|
||||||
if (!cancelled) setPlayerName(name);
|
|
||||||
const state = buildState(events);
|
|
||||||
// 7 维 score 问题,criteria 为 10 级有序数组(索引 0-9 对应 1-10 级)
|
|
||||||
const criteria = Array.from({ length: 10 }, (_, i) => `${i + 1} 级`);
|
|
||||||
const questions = Object.fromEntries(
|
|
||||||
PROFILE_DIMENSIONS.map((d) => [
|
|
||||||
d.id,
|
|
||||||
{
|
|
||||||
type: 'score' as const,
|
|
||||||
instructions: `根据用户行为轨迹,评估其「${d.label}」维度水平:${d.description}`,
|
|
||||||
criteria,
|
|
||||||
},
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
try {
|
try {
|
||||||
const answers = await jev.ask(state, questions);
|
const events = await store.list();
|
||||||
|
// 最近一次身份登记的称呼(旧数据缺 name 字段时回退空串 → 只显示编号)
|
||||||
|
const name = [...events].reverse().find((e) => e.kind === 'profile')?.profile?.name ?? '';
|
||||||
|
if (!cancelled) setPlayerName(name);
|
||||||
|
const result = await scoreProfile(events, jev);
|
||||||
if (cancelled) return;
|
if (cancelled) return;
|
||||||
setScores(
|
setScores(result);
|
||||||
PROFILE_DIMENSIONS.map((d) => {
|
|
||||||
const a = answers[d.id];
|
|
||||||
// API score 是 [0, criteria.length-1] 的概率加权浮点值,+1 映射到 1-10 级展示
|
|
||||||
return a?.type === 'score'
|
|
||||||
? { id: d.id, label: d.label, score: Math.round((a.score + 1) * 10) / 10, max: 10, confidence: a.confidence }
|
|
||||||
: { id: d.id, label: d.label, score: 0, max: 10, confidence: 0 };
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (!cancelled) setError(e instanceof Error ? e.message : String(e));
|
if (!cancelled) setError(e instanceof Error ? e.message : String(e));
|
||||||
}
|
}
|
||||||
@@ -144,6 +98,7 @@ export function ReportPage({ store, jev, onReplay }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
<span className="dimension-value">
|
<span className="dimension-value">
|
||||||
{s.score.toFixed(1)}/{s.max} · 置信度 {Math.round(s.confidence * 100)}%
|
{s.score.toFixed(1)}/{s.max} · 置信度 {Math.round(s.confidence * 100)}%
|
||||||
|
{s.confidence < 0.6 && ' · 证据不足'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
+1
-12
@@ -1,15 +1,4 @@
|
|||||||
import type { DimensionTag } from '../../engine/types';
|
export { PROFILE_DIMENSIONS } from '../../engine/scoring';
|
||||||
|
|
||||||
/** 画像报告的 7 个实测维度(CONTEXT.md 实测框架) */
|
|
||||||
export const PROFILE_DIMENSIONS: { id: DimensionTag; label: string; description: string }[] = [
|
|
||||||
{ id: 'attachment', label: '依恋类型', description: '你在亲密与分离面前的姿态' },
|
|
||||||
{ id: 'conflict_style', label: '冲突处理风格', description: '分歧当前,你先做什么' },
|
|
||||||
{ id: 'values', label: '价值观与人生优先级', description: '两体面或两难堪之间,你让什么先走' },
|
|
||||||
{ id: 'emotional_stability', label: '情绪稳定性', description: '突发与悔恨面前的波形' },
|
|
||||||
{ id: 'risk', label: '风险偏好', description: '稳妥落点与精确落点之间的你' },
|
|
||||||
{ id: 'culture', label: '文化底蕴', description: '碑文、星象与礼制,实测而非自报' },
|
|
||||||
{ id: 'mate_preference', label: '择偶偏好', description: '你信任谁、把后背交给谁' },
|
|
||||||
];
|
|
||||||
|
|
||||||
/** 勋章映射:最高置信度维度 → 称号(写死的 MVP 映射表) */
|
/** 勋章映射:最高置信度维度 → 称号(写死的 MVP 映射表) */
|
||||||
export const BADGE_MAP: Record<string, { title: string; flavor: string }> = {
|
export const BADGE_MAP: Record<string, { title: string; flavor: string }> = {
|
||||||
|
|||||||
Reference in New Issue
Block a user