Files
SoundGame/docs/design/21-keeper.md
T
xsl cd4c5ec65a docs(new8): 批次 4 策划落地(21-keeper/22-bridge)——8 款设计文档全部齐备
- keeper:时空双线(风暴/老唱片签名+锚句)、回忆插叙固定时序、trust×truth 双标量、148 句
- bridge:四象限系统状态音(2.0s±50ms 逐毫秒可断言)、告警仲裁 R1-R8+双危机 7 场景穷举、危机事件卡、忠诚账本、154 句
- 13 号文档 §23 六款断言指针;README new8 索引补齐;章程进度账 8 款设计 ☑(批次 2 资产/开发 ▶ 进行中)
2026-09-10 01:02:49 +08:00

540 lines
51 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 21 · 《守塔人》(keeper) — 孤岛灯塔叙事 RPG 设计文档
> **给开发的实现要点摘要**
> 1. 叙事模式(声命线范式,radio/abyss/cipher 同骨架):节点链 onComplete 串行 + seqToken 打断作废 + pendingChoice 缓存;复制 `web/radio/` 结构起步,状态机 6 宏状态(INTRO/WATCH/MEMORY/CALM/ENDING + MENU 覆盖层)。
> 2. 手势零豁免:全部走全局默认映射(单击=继续/跳句、双击=重听、左右滑=抉择、上滑=状态、下滑=模态菜单 §5.2 约定、长按=引擎快进)。
> 3. 台词依据 [21-keeper-voice-ids.md](21-keeper-voice-ids.md)(148 句:双性别 121 + 平铺 NPC 27);NPC 电台/老唱片滤波在**资产侧** ffmpeg 带通(radio 先例),引擎零改动。
> 4. **本作核心差异层=时空双线**(§6):现在(风暴+电台静电)/回忆(老唱片底噪+唱片滤波)全靠音色+底噪签名区分;插叙=节点链 auto(≤6 句/≤90s),进入/回返有固定过渡音时序;**不新增 guide earcon**(统一 8 音词表纪律),时空签名住内容层(sfx+底噪+BGM+锚句)。
> 5. 信任/真相双轻量标量(radio rep 同款纪律,档位措辞逐值为真);软失败=「灯灭过一刻钟/他自己冲滩」叙事差分,零死亡零倒计时(P4);结算链 800ms 手势死窗(abyss/地下城 H1 同款)。
> 6. 章程任务 N4(research/new8/00-program.md §2):单局 15-25 分钟、9 抉择点(实选 8-9)、4 结局、分支局部化。
> 7. 断言清单住本文档 §9(abyss §21 先例;13 号文档 §23 指针待批次测试轮同步登记)。
## 1. 概述
- **一句话**:风暴夜,孤岛灯塔的电台里,一声比一声近的求救,牵出你家三代守塔人的秘密——守灯,还是救人;说出真相,还是守住秘密。
- **题材(声音即世界)**:黑暗风暴里视觉归零,**电台人声=另一条命、风暴与雾角=你的处境、老唱片底噪=过去**——「用耳朵守一夜」与纯音频载体天然同构。两个时空只靠音色与底噪区分,声音本身就是叙事本体。
- **单局**:一个完整风暴夜(一条抉择路径)21-24 分钟;风暴从起到歇=天然三幕,分歧局部化在每幕内部;9 个抉择点(实选 8-9 个),4 结局。
- **核心循环**:值守(塔内叙事链)→ 电台(阿澈对白+抉择点)→ 抉择结果 →(关键抉择触发**回忆插叙**:唱针落下→父辈/祖辈片段→风声回来)→ 回到现在继续值守 → 幕间喘息。跨幕积累「信任」(阿澈信你有几分)与「真相度」(三代秘密翻开几层),风歇前 p9 决定秘密的归宿。
- **与 radio/abyss 的差异点**(骨架同、差异层不同):radio 靠电话音质立「声音即存在」,abyss 靠声呐双通道立「用耳朵找人」;keeper 靠**时空音频签名**立「回忆会开口说话」——插叙不是过场动画,是给现在抉择供货的信息与重量(发条知识=插叙2 兑现 p7 生死细节;祖父忏悔=插叙3 给 p9 完整真相可说)。
- **风险预防(本作特有,策划期显式设计)**:① 时空误认=剧情混乱——签名规格显式教学一次(rev_teach)+上滑时位句+中断复述带时空位(§6.4/§6.5);② 家族人物多音色——三代人+求救者共 3 个 NPC 音色 ≤5,区分度逐人标注(§5.3/voice-ids 头表)。
## 2. 状态机
### 2.1 状态列表
| 状态 | 内容 | BGM | guide.state |
|---|---|---|---|
| `INTRO` | 开场链:塔名口播 + 一句规矩 + 今晚目标 | `bgm/storm` | explore |
| `WATCH` | 值守叙事:现在线对白链 + 抉择点 + 结果 | `bgm/storm` | explore;抉择点=choice |
| `MEMORY` | 回忆插叙:auto 链 ≤6 句,首尾锚句,链末自动回 WATCH | `bgm/memory` | explore(不新增签名) |
| `CALM` | 幕间:风暴进度通报(+跨档信任播报),自动链 ≤12s | `bgm/storm`(不切) | settle |
| `ENDING` | 结局链 + 尾声回响(epilogue),结算态 | `bgm/dawn` | settle |
| `MENU` | 模态覆盖层(任意叙事态下滑呼出,暂停一切链) | `bgm/storm`(低响度淡入) | menu |
- CALM 自动链 ≤12s:foghorn → lull_N(风暴进度整句)→(跨档才播)信任档位句 → 自动进下一幕;不切 BGM(风暴进程靠 amb_storm 游戏层 gain 分幕:幕一 0.5 / 幕二 1.0 / 幕三 0.7)。
- 结局链/c3_dark/插叙链入口处盖 `_settleGraceUntil = now+800ms` 手势死窗(abyss §2.2 同款),窗内手势一律吞掉,关键叙事完整播出。
- MENU 打开时 `guide.disarm()` + 取消一切看门狗/自动链;关闭后重播当前节点(局面复述;MEMORY 中关闭=重播当前回忆节点,底噪仍 vinyl)。
### 2.2 转移图(文本)
```
┌───────────── 结算单击=新风暴夜 ─────────────┐
▼ │
INTRO ──链完单击──> WATCH(C1) ──幕一链完──> CALM(1) ──────────┤→ WATCH(C2)
│ …CALM(2) 同型)…
│ p2左/p3左|p4左/p6左 触发:
▼
MEMORY ──插叙链 auto──> 回 WATCH(原 now 线续走)
│
WATCH(C3) ──高潮链完──> CALM-lite(g9) ──> ◆p9
│
d9 结果链 ──> ENDING(e1..e4 + ep 回响链) ──┘
任意叙事态(INTRO/WATCH/MEMORY/CALM/ENDING):下滑 → MENU(模态)
MENU:单击=循环切换选项 → 双击=确认执行 → 上滑=帮助 → 下滑/「回到值守」项=关闭回原状态(重播当前节点)
```
- 全部叙事态为节点链驱动(radio playNode 模式);CALM 与插叙链 `auto:true`,WATCH 叙事节点链末等待单击(radio 实现核对 #1 同口径)。
- MEMORY 是显式宏状态(BGM/底噪/存档 timeline 都按它切换),但没有独立等待点:链 auto 完成即回 WATCH;插叙期间开菜单=暂停,关闭=重播当前回忆节点。
### 2.3 每状态手势映射(6+2 全覆盖,零豁免)
| 手势 | INTRO | WATCH | MEMORY | CALM | ENDING | MENU |
|---|---|---|---|---|---|---|
| 上滑 | 帮助+状态,随后重播当前段 | 帮助+状态(时位句+信任/真相档),随后重播当前节点 | 帮助+状态(**prog_mem_g/f 时位句先行**),随后重播当前回忆节点 | 帮助+状态,自动链继续 | 帮助+状态(结局名+信任档) | 帮助(sys_help) |
| 下滑 | 模态菜单 | 模态菜单 | 模态菜单(停插叙链) | 模态菜单 | 模态菜单 | 关闭菜单 |
| 左滑 | 无效 → error 震+gd_invalid | 无效 → gd_invalid(喂无进展轨) | 无效 → gd_invalid | 无效 → gd_invalid | 无效 → gd_invalid | 无效 → error 震 |
| 右滑 | 无效 → gd_invalid | 无效 → gd_invalid;ASK 态=新风暴夜 | 无效 → gd_invalid | 无效 → gd_invalid | 无效 → gd_invalid | 无效 → error 震 |
| 单击 | 播放中=跳过本句;链末=进 C1 | 播放中=跳过本句(p* 与锚句首播不可跳);链末=继续 | 播放中=跳过本句(**锚入/锚出句首播不可跳**);无链末等待(auto 回返) | 跳过本句(自动链) | **再来一夜**(新风暴夜) | 循环切换选项 |
| 双击 | 重听当前段 | 重听当前节点台词(含抉择提示) | **重听当前回忆节点** | 重听幕间链 | 重听结局链 | **确认执行**(菜单约定覆盖重听) |
| 长按 | 引擎接管:旁白临时 1.7×(12 号 §2.2,叙事类不豁免) | 同左 | 同左 | 同左 | 同左 | 同左(快进菜单语音) |
| 长按松开 | 引擎接管:恢复 settings.speed | 同左 | 同左 | 同左 | 同左 | 同左 |
- 豁免登记:**无**。六个基础手势全部符合全局宪法默认语义;菜单走 §5.2 R3 模态约定(单击循环+双击确认)。
- ASK(存档询问态,radio 同款):单击=继续上次 / 右滑=新风暴夜(两者 runs 均 +1,cipher §8 统一口径);上滑/下滑/左滑/双击=无效(error 震+gd_invalid)。
- 抉择 prompt(voice/p1..p9 及 p8_boat/p8_reef/p9a/p9b 变体)播放中单击不可跳(PROMPT_RE 门)、左右滑缓存 `pendingChoice` 播完即生效(radio W2 契约照抄)。
- 继承 radio G5 跳过公平契约:**首播必完整(p* 首播不可跳 / 插叙首尾锚句与链末关键句保护 / ASK 恢复首句锁)、复播可即掐(锁不随复播重新武装、插叙中段句可跳、复播滑动照常预缓存)**。
- 无效手势零静默:所有「无效」格均有 error 震 + gd_invalid(onceKey 同局限次,F1.4 idle-only);longpress/longpressend/tapcancel 全状态合法(radio 实现修复 #2 教训)。
- 键盘通道:三通道并行,游戏代码零改动;操作提示句按 `gesture.inputMode()` 选 `_k` 变体资产(§7.3、voice-ids 集中分组)。
## 3. 抉择树
### 3.1 全局骨架(三幕共享主干,分歧局部化)
```
INTRO: gd_brief → b_open → b_rain → b_rule → b_goal ──链完单击──> C1
【C1 幕一·风暴起】教学幕 · 2 抉择(p1/p2)+插叙1 ──> CALM(1) ──> C2
【C2 幕二·风暴顶】2 抉择(p3/p4)+插叙2 +危机拍1 + p5 ──> CALM(2) ──> C3
【C3 幕三·风歇前】p6(+插叙3) + p7 核心 + 危机拍2 + p8 双形态 ──> 签收 ◆p9 ──> ENDING
```
分支纪律:**每幕内部分支、幕末收敛**到 CALM/签收句;跨幕只传标量(trust、rev1/rev2/rev3、p7/p9 派生枚举),不生成组合节点。最坏路径与最好路径节点数同型。
### 3.2 分幕结构(◆=抉择点,〔〕=条件差分,【插叙】=MEMORY auto 链)
```
【C1 幕一·风暴起】
c1_tune → amo_a → amo_b → ◆p1
├左 接住人: c1_hold → c1_holdo → c1_holde(trust+1)─┐
└右 要船位: c1_ask → c1_asko → c1_aske(即时纠偏)───┤
▼
amo_c(回澜号伏笔)→ ◆p2
├左 翻暗格: c1_open → rev_teach(全局一次) →【插叙1】→ c1_diary(truth+1)─┐
└右 守灯: c1_focus → c1_focuse ──────────────────────────────────────────┤
▼
CALM(1): foghorn → lull_1 →〔跨档才播 tr_*〕→ C2
【插叙1·祖父的年代】memory_in → rev_g1(锚入) → rev_g2 → rev_g3 → rev_g4(锚出) → memory_out
【C2 幕二·风暴顶】
c2_back → amo_d → ◆p3
├左 口诀: c2_song → c2_songo →【插叙2】→ c2_key(truth+1)─┐
└右 岸台: c2_shore → c2_shoreo ─────────────────────────────┤
▼
amo_e → ◆p4
├左 真话: c4_true →〔插叙2 未触发 ?【插叙2兜底】+c2_key : 直进〕→ c4_trueo(trust+1)─┐
└右 带过: c4_pass → c4_passo ───────────────────────────────────────────────────────────┤
▼
【危机拍1】hull_crack 前置2s 渐强 + danger 签名/震 → c2_crack → amo_f → ◆p5
├左 堵漏: c5_patch → c5_patcho(trust+1)─┐
└右 救生衣: c5_suit → c5_suito ────────────┤
▼
CALM(2): foghorn → lull_2 →〔跨档才播 tr_*〕→ C3
【插叙2·父亲的最后一晚】memory_in → rev_f1(锚入) → rev_f2 → rev_f3 → rev_f4 → rev_f5(锚出) → memory_out
【C3 幕三·风歇前】
c3_eye →〔rev2 ? c3_page → ◆p6 : m6_seal 封锁句(播后自动走右,不摆假选项)〕
├左 读完:【插叙3】→ c6_reade(truth+1)─┐
└右 合上: c6_shut ────────────────────────┤
▼
◆p7(核心:守灯还是救人;提示句全表最长)
├左 离塔救人:〔rev2 ? c7_go(满发条,配 lamp_mech): c7_go2(半卷)〕
│ →【危机拍2】wave_wall 前置2s+danger → c3_wall → ◆p8_boat
│ ├左 循渔歌: c8_song → c8_songo ─┐
│ └右 推坐标: c8_math → c8_matho ─┤
│ ▼
│ c9_return →〔rev2 ? — : c3_dark(灯灭过一刻钟,800ms 死窗)〕─┐
└右 守灯引渡: c7_hold →【危机拍2】c3_wall → ◆p8_reef │
├左 老航道: c8_old → c8_oldo ─┐ │
└右 新礁缝: c8_new → c8_newo ─┤ │
▼ ▼
〔trust≥3 ? c9_land : c9_found〕──────────┴→ 签收(两路通用,电台或塔内对谈)
【签收】g9_in → g9_knot → ◆p9〔truth=3 ? p9a : p9b〕
├左 说出:〔truth=3 ? d9_tell_a : d9_tell_b〕→ amo_g → d9_telle ─┐
└右 守住: d9_keep → amo_h → d9_keepe ────────────────────────────┤
▼
ENDING: e*_a → e*_b(trust 档 × p9 四选一)→ ep_lead → ep_*(条件选播)→ ep_shore
【插叙3·祖父的忏悔】memory_in → rev_c1(锚入) → rev_c2 → rev_c3 → rev_c4 → rev_c5(锚出) → memory_out
```
### 3.3 分支局部化说明
- 跨幕状态全部是标量:`trust`(int 0-4)、`rev1/rev2/rev3`(bool,插叙完成数=truth)、`p7`('boat'|'lamp')、`p9`(bool)。没有任何「幕 i 分支 × 幕 j 分支」组合节点。
- 条件差分全部是**节点内选播**(radio w1_say/w1_say2、abyss p7 双形态先例):插叙2 兜底(p4 左)、c7_go/c7_go2(rev2)、c9_land/c9_found(trust)、c9_return 后 c3_dark 插播(rev2=false)、p9a/p9b 与 d9_tell_a/b(truth)、ep_* 选播。树形不变。
- 树规模:148 句台词、约 75 个节点;任一路径到达全部 9 个抉择点,p6 在 rev2=false 时被 m6_seal 自动走右(实选 8-9 个)。
### 3.4 四结局达成条件
| 结局 | 条件 | 主题 | 开环钩子 |
|---|---|---|---|
| e1 灯与归人 | trust ≥ 3 且 p9=左 | 人接住了,真相也说全了 | 「回澜号的账,翻开了第一页」 |
| e2 守夜人 | trust ≥ 3 且 p9=右 | 人救了,秘密再压一代 | 「有些东西,再压一代人」 |
| e3 半明半暗 | trust < 3 且 p9=左 | 你说了,他半信半疑 | 「碎片也是真话」 |
| e4 各自的塔 | trust < 3 且 p9=右 | 隔海两塔,各自沉默 | 「谁也没再提那晚」 |
- 尾声回响(P2 回响感):epilogue 逐项点名——阿澈(接回塔/自渡/天亮被找到,按 p7×trust)、灯的记录簿(满发条自转/灭过一刻钟/整夜未眨眼,按 p7×rev2)、祖父日志(见光/压回黑暗,按 p9×truth≥1)、父亲的班次(rev2=true 替他签字);岸台晨报恒播收尾(b_goal 货轮承诺闭环)。玩家每一层翻开的与守住的都被点名。
## 4. 数值设计
### 4.1 变量
| 变量 | 类型 | 初值 | 说明 |
|---|---|---|---|
| `trust`(信任) | int | 0 | 阿澈对你的信任,幕一/二积累,幕三不动 |
| `rev1/rev2/rev3` | bool | false | 插叙1/2/3 完成位;truth = 三者之和 |
| `p7` | str | null | 'boat'(离塔救人)/'lamp'(守灯引渡),核心抉择派生 |
| `endings{}` / `runs` | map/int | — | 结局收集与重玩计数(存档展示用) |
### 4.2 抉择计分表(左=走心·翻开/说出 / 右=克制·程序/守秘)
| 抉择 | 左(走心) | 右(克制) | 备注 |
|---|---|---|---|
| p1 接住人/要船位 | trust+1 | 0 | 左路阿澈声音稳住(首个正反馈);右路 c1_aske 即时纠偏不扣分 |
| p2 翻暗格/守灯 | truth+1(rev1=true) | 0 | 左路=插叙1(日志残篇到手,缺最后一页) |
| p3 口诀/岸台 | trust+1,truth+1(rev2=true) | 0 | 左路=插叙2 主触发(发条知识+缺页线索 c2_key) |
| p4 真话/带过 | trust+1;rev2=false 时兜底插叙2(truth+1) | 0 | 插叙2 的第二把钥匙(p3 右的补偿路) |
| p5 堵漏/救生衣 | trust+1 | 0 | 双活路(两路都活着,差分在反应句与尾声心境) |
| p6 读完/合上 | truth+1(rev3=true) | 0 | 左路=插叙3(祖父忏悔全量);**rev2=false 时左路封锁**(m6_seal 播出后自动走右,abyss g9_dry 先例——不摆假选项) |
| p7 离塔救人/守灯引渡 | 0(p7='boat') | 0(p7='lamp') | 核心宿命抉择:不动 trust;后果走 rev2(发条)与 trust(引渡)差分 |
| p8_boat 循渔歌/推坐标 | 0 | 0 | 双活路差分(海上找人方式) |
| p8_reef 老航道/新礁缝 | 0 | 0 | 双活路差分(引渡路线) |
| p9 说出/守住 | 真相出口 | 秘密出口 | 不动 trust;提示句按 truth 选 p9a/p9b |
### 4.3 信任档位表(账目真实:每档句义对每个取值皆为真)
| 档 | trust 区间 | 整句 | 真实性核验 |
|---|---|---|---|
| tr_deep | 3-4 | 「他把命,交给了你的声音。」 | ≥3 时 p7 右他完全照做(c9_land)✓;p7 左他海上应答配合 ✓ |
| tr_half | 1-2 | 「他信一半,疑一半。」 | 1-2 时 p7 右他半路改主意(c9_found)✓ |
| tr_thin | 0 | 「今晚,他只信自己的手。」 | 0 = 四个信任点全失,p7 右必自渡 ✓(附 warning 震,radio G2 同款) |
- 阈值 3 = 「多数走心 + 容忍 1-2 次克制」落在高信任档——把「他是否把命交给你」压在玩家自己的累计上,而非单点(radio rep ≥5 同款纪律)。
- 跨档被动播报(挂 CALM 链尾,跨档才播);上滑 status 随时可查。
**真相度逐值表(truth=插叙完成数,每值一句、逐值为真)**
| truth | 整句 | 核验 |
|---|---|---|
| 0 | 「旧账压在灯底,你一页没翻。」 | rev1/2/3 全 false ✓(暗格未开) |
| 1 | 「灯下的旧账,你翻开了一角。」 | 仅插叙1(日志残篇)✓ |
| 2 | 「祖父和父亲的事,你都翻到了。」 | 插叙1+2 ✓(缺页在手未读) |
| 3 | 「三代人的账,今夜见底了。」 | 插叙1+2+3 ✓(忏悔全文,p9a 解锁) |
### 4.4 结局与差分验算(设计期穷举,playtest 剧本 A-F 引用)
- **trust ∈ {0..4} 全档可达**(四个独立 ±1 抉择 p1/p3/p4/p5);e1/e2 ⟺ trust≥3(如 p1左 p3左 p4左 p5右=3),e3/e4 ⟺ trust≤2(全右=0)。
- **truth ∈ {0..3} 全档可达**:rev1=p2左;rev2=p3左∨p4左;rev3=p6左∧rev2。0(p2右p3右p4右)、1(p2左+双右)、2(p2左+p3左)、3(再 p6左)。
- **p6 封锁与 p9 变体联动**:rev2=false → m6_seal 自动右;truth=3 → p9a/d9_tell_a(全真相),truth<3 → p9b/d9_tell_b(碎片:只确认「那晚守塔的是我爷爷、灯灭过一下」——前者是家族常识、后者来自阿澈自己的话,**truth=0 也句句为真**,归因真话律)。
- **灯三态**(p7×rev2):p7 左+rev2 → ep_lamp_full(满发条自转,rev_f4 知识兑现);p7 左+¬rev2 → c3_dark+ep_lamp_dark(半卷发条走到头,**复刻祖父之夜**,归因指向「没听完父亲那一晚」的实际选择);p7 右 → ep_lamp_hold(整夜未停)。
- **阿澈三态**(p7×trust):p7 左 → 恒被接回(c9_return,人已上艇不失去);p7 右+trust≥3 → 自渡成功(c9_land);p7 右+trust<3 → 天亮被渔队找到(c9_found,活着,措辞克制——零死亡红线)。
- **最坏路径完整性**:剧本 C(全右)仍走完全部 9 抉择点、3 结局分支可差分(ep_amo_dawn/ep_lamp_hold/ep_log 不播)——树深固定,无死路。
- **纸面承诺安全性**:trust≥3 ⟹ p3/p4 至少一左 ⟹ rev2=true ⟹ 缺页在手(c2_key)——e1_a「那页纸攥在手里」/e2_b「缺页放回灯底」对 trust≥3 两结局**恒为真**;e3/e4(trust<3)文案零纸面引用,truth 任意取值皆不穿帮(归因真话律在结局文案侧的落点)。
### 4.5 家族秘密 gating 顺序(秘密揭示的三把钥匙与三道门)
| 层 | 秘密 | 钥匙(抉择) | 门(插叙) | 兑现(现在线收益) |
|---|---|---|---|---|
| 层1 | 灯下藏着东西 | p2 左(翻暗格) | 插叙1(祖父:「灯下压着的东西,比灯重」) | 日志残篇 + 缺页悬念(tru_one) |
| 层2 | 父亲的最后一次值守 | p3 左(口诀)或 p4 左(真话,兜底) | 插叙2(父亲电台:「上满发条能自己转一夜」) | 手电缺页线索(c2_key)+ 发条知识(c7_go 差分) |
| 层3 | 祖父的忏悔(回澜号真相) | p6 左(**前置=层2 已开**) | 插叙3(祖父:「我离灯三刻,回澜号,七个人」) | p9a 完整真相可说 + e1/e2 重量 |
- 顺序不可跳:层3 的门只认层2 的钥匙(m6_seal 封锁);层2 有两把钥匙(p3/p4)保证「错过 p3 仍可全真相」(补偿路,abyss hasLog 双入口同型)。
- 阿澈的回环:amo_c(幕一伏笔「我爷爷……白角的灯灭过一下」)→ amo_g(p9 左揭晓「我爷爷,就在那七个人里」)——玩家的抉择把两条家族线缝上。
## 5. 音频三层映射
### 5.1 BGM(状态→曲目,Mureka instrumental,3 首 ≈ $0.09;台账 tools/mureka_log.md)
| 状态 | 曲目 | 切换 | Mureka prompt 草稿(英文,seamless loop) |
|---|---|---|---|
| INTRO/WATCH/CALM | `bgm/storm` | 开局淡入 800ms,全夜不切;语音 duck 引擎自理 | "Storm night lighthouse vigil, tense low piano motif over dark strings, restrained and lonely, distant thunder and wind, no vocals, seamless loop, 60-90 seconds" |
| MEMORY | `bgm/memory` | **插叙进入** crossfade 800ms;回返淡回 storm 800ms | "Old phonograph memory waltz, music box and muted strings with vintage vinyl warmth, fragile nostalgia, slightly out-of-time, no vocals, seamless loop, 60-90 seconds" |
| ENDING | `bgm/dawn` | d9 结果链播完后淡入 1500ms | "Calm grey dawn after a storm at sea, soft piano and warm strings, gulls far away, quiet relief, no vocals, seamless loop, 60-90 seconds" |
| MENU | `bgm/storm`(低响度) | 呼出低响度淡入 500ms;关闭淡回 800ms | 同 storm(省 1 首,塔内场景同构;abyss menu 用 deck 先例) |
- `bgm=0` 时全部静默(引擎约定);幕的推进不靠切曲——靠 amb_storm 游戏层 gain 分幕(0.5/1.0/0.7)+ lull_N 进度整句(账目在语音不在音乐)。
### 5.2 SFX 清单(SA3=Stable Audio 3 / SYN=程序合成;one-shot 真峰值 ≤-3dBFS;amb 循环由游戏层 `buffer.loop` 实现,radio phone_ring 先例)
| id | 用途 | 来源 | prompt(英文)/ 合成参数 |
|---|---|---|---|
| `sfx/amb_storm` | 风暴底噪 loop(现在时签名①;分幕 gain 0.5/1.0/0.7) | SA3 | "Storm wind and heavy waves hitting a lighthouse rock, sustained gale, night, seamless loop" |
| `sfx/amb_room` | 塔内底噪 loop(炉火+木构+雨打玻璃) | SA3 | "Inside a lighthouse room at night, oil stove crackle, wooden beams creak, rain on glass, seamless loop" |
| `sfx/amb_vinyl` | **老唱片底噪 loop(回忆时签名②;插叙期间唯一底噪)** | SA3 | "Vintage 78 rpm phonograph record surface noise, warm crackle and hiss, no music, seamless loop" |
| `sfx/memory_in` | **插叙进入过渡音:唱针落下+转速走稳** | SA3 | "Phonograph needle dropping onto a record, brief run-in groove then steady rotation, 1 second" |
| `sfx/memory_out` | **插叙回返过渡音:唱针抬起+轻划** | SA3 | "Phonograph needle lifting off a record with a short scratch, 0.6 seconds" |
| `sfx/radio_tune` | 调频找台(C1 首通/信号重连) | SA3 | "Turning an old shortwave radio dial through static, settling on a weak signal, 3 seconds" |
| `sfx/static_burst` | 阿澈信号断续气口(信息音,idle-only) | SA3 | "Shortwave radio static burst with faint ghost of a distant voice, 2 seconds" |
| `sfx/radio_beep` | 电台接通确认 | SYN | 双音上行 0.3s(abyss link_beep 同款) |
| `sfx/amb_engine` | 阿澈侧渔船引擎 loop(通话期对侧 amb,voice 在播时 -6dB) | SA3 | "Small fishing boat engine running rough in a storm, water sloshing in bilge, seamless loop" |
| `sfx/hull_crack` | 危机拍1:船板开裂(前置 2s 渐强) | SA3 | "Wooden boat hull cracking under stress, splintering and groaning, 3 seconds" |
| `sfx/wave_wall` | 危机拍2:浪墙(前置 2s 渐强) | SA3 | "Massive wave wall rising and crashing, deep roar, 4 seconds" |
| `sfx/lamp_mech` | 灯座机械上发条(c7_go;rev_f4 兑现音) | SA3 | "Clockwork lighthouse rotation mechanism being wound, heavy ratchet clicks, 3 seconds" |
| `sfx/hatch_open` | 灯座暗格开启(c1_open) | SA3 | "Old hidden compartment sliding open, metal and wood scrape, 2 seconds" |
| `sfx/flash_click` | 手电尾盖旋开两响(c3_page) | SYN | 两次短金属 click(120ms 间隔) |
| `sfx/foghorn` | 雾角两长一短(b_rain 承诺音:开场/幕间×2/ep_shore) | SA3 | "Lighthouse foghorn, two long and one short blast, deep brass, distant storm behind, 4 seconds" |
| `sfx/ui_confirm` / `ui_menu` | 菜单/确认(程序合成兜底) | SYN | 同全局先例 |
- 预警窗纪律:hull_crack/wave_wall 均**前置 2s 起渐强**(L2),danger 签名与 danger 震同窗(radio C5/abyss D2-D4 先例);全游戏仅此两处 danger。
- 承诺兑现绑定:foghorn 在 b_rain 口播「两长一短」后,开场/CALM(1)/CALM(2)/ep_shore 四处必播(§6.5 清单 + §9 断言计数)。
### 5.3 Voice 划分(详见 21-keeper-voice-ids.md)
| 组 | 声部 | 落位 | 数量 |
|---|---|---|---|
| 守塔人(你)KEEPER | 双性别(female=Serena / male=Uncle_Fu + KEEPER instruct 常量) | `voice/female\|male/` | 42 句 |
| 岸台/系统/向导 SHORE | 双性别(female=Vivian / male=Dylan + SHORE instruct 常量) | `voice/female\|male/` | 79 句 |
| NPC 平铺 ×3 | 阿澈=Dylan(电台滤波)/ 父亲=Uncle_Fu(唱片滤波)/ 祖父=Eric(唱片滤波),**不做双性别** | `voice/` 根(平铺回退命中)+ 滤波转码 | 27 句 |
- NPC ≤5 约束满足(3 人);玩家自己的声音跟随全局 `settings.voice`;岸台值班员是向导人设(L4 角色化系统声,radio 导播/abyss 调度同型)。
- 合计 148 句(≤160 预算 ✓,含 `_k` 键盘变体 8 对);音频文件 = 121×2 + 27 = 269 条(×2 格式 = 538 文件)。
## 6. 时空双线设计(本作核心差异层)
### 6.1 引擎能力边界(对照 web-engine-api.md)
| 能力 | 引擎现状 | 方案 |
|---|---|---|
| voice 运行时 EQ/滤波 | ❌ voice 链无滤波插入点 | **资产侧转码滤波**(radio §6.2 先例):ffmpeg `highpass+lowpass+压缩器` 逐 NPC 定档(§5.3/voice-ids) |
| BGM crossfade | ✅ fadeMs 800ms 内置 | 插叙进入/回返直接 `play('bgm/...')` 切换 |
| sfx 循环 | ❌ 即发即弃 | 游戏层 `buffer.loop=true` 持句柄(amb_storm/amb_room/amb_vinyl/amb_engine),插叙切换时淡入淡出(radio `_ringSrc` 先例) |
| sfx vs voice 互让 | ❌ sfx 总线独立无 duck | **游戏层 idle 门**(§6.6 R1):memory_in/out、static_burst 只在 `!audio.isVoicePlaying()` 时放行,否则挂队列表句间补发 |
| 语音 duck 垫乐 | ✅ 内置(duck 20%,300ms) | 直接依赖 |
| 变速不变调 | ✅ SoundTouch | 直接依赖(长按 1.7× / 2.2× 视障档) |
### 6.2 时空签名规格(误认=剧情混乱,规格逐维度显式)
| 维度 | 现在(风暴夜) | 回忆(老唱片) |
|---|---|---|
| BGM | `bgm/storm`(低音钢琴+暗弦+远雷) | `bgm/memory`(八音盒+老唱片暖染圆舞) |
| 底噪 | amb_storm(分幕 gain)+ amb_room | **amb_vinyl(唯一底噪:storm/room 双双淡出)** |
| 过渡音 | 回返:memory_out + storm 渐起 | 进入:memory_in(唱针落下) |
| 语音音质 | KEEPER/SHORE 不滤波(塔内近讲);阿澈=电台滤波 | 祖父/父亲=唱片滤波;**KEEPER 锚句不滤波(「现在的你」在回忆里旁白——干净人声 vs 唱片人声,强化「你在看回忆」)** |
| 时位句 | prog_m1/m2/m3(上滑链首段) | prog_mem_g / prog_mem_f(上滑链首段 + 恢复链首段) |
| 台词锚 | (now 句自然在场) | 锚入 rev_g1/rev_f1/rev_c1 + 锚出 rev_g4/rev_f5/rev_c5 |
- **earcon 纪律:时空切换零新增 guide earcon**——统一 8 音词表中 MEMORY/WATCH 同用 `explore` 签名;时空签名住**内容层**(sfx 过渡音+底噪+BGM+台词锚),语义靠语音(L2 铁律:earcon 只做粗标记,训练 8.5 轮结论不赌)。
- 底噪互斥是硬约束:任意时刻 amb_vinyl 与 amb_storm/amb_room **零重叠**(§9 转写断言);插叙期间阿澈的 amb_engine 也停(叙事自洽:插叙都发生在塔内独处拍,电台此刻静默)。
### 6.3 插叙触发与回返时序(精确到步骤)
| 步 | 事件 | 时序 |
|---|---|---|
| 1 | now 结果句播完 → 静默一拍 | 600ms |
| 2 | `memory_in`(1.0s,idle 门放行)+ BGM crossfade storm→memory 800ms + amb 切换(storm/room 淡出 400ms / vinyl 淡入 400ms) | 与步 2 同时起 |
| 3 | 插叙链 auto:锚入(KEEPER,不可跳首播)→ NPC 2-3 句(中段可跳)→ 锚出(KEEPER,不可跳首播) | ≤6 句 ≤90s |
| 4 | `memory_out`(0.6s)+ crossfade memory→storm 800ms + amb 回切 | 链末即起 |
| 5 | 回返后 now 句接播(c1_diary / c2_key 或 c4_trueo / c6_reade) | memory_out 播完接 |
- 插叙链入口 800ms 手势死窗(§2.2);链末**不设等待**(auto 回返——回忆不是关卡,是信息颗粒);「停下来回味」由双击重听当前回忆节点承接(叙事类等待点合法回味 + 可重听,A2/P1)。
- 插叙触发点全集(3 处):p2 左→插叙1;p3 左或 p4 左→插叙2(兜底);p6 左→插叙3(前置 rev2=true)。
### 6.4 「我在哪」复述机制(P3 打断回来说得清)
| 层 | 机制 |
|---|---|
| 被动 | 底噪+BGM 时空签名 2s 可辨(A1:有无唱片底噪/风声) |
| 主动 | 上滑 status 链**首段=时位句**:now → prog_m1..3;memory → prog_mem_g(祖父年代)/ prog_mem_f(父亲最后一晚)——「现在是回忆」可随时重听确认 |
| 台词锚 | 每段插叙首尾锚句 = 台词级时空位(「唱针落下。四十年前……」「风声回来了……」) |
| 中断恢复 | 存档带 `timeline`('now'/'mem_g'/'mem_f');恢复链 = timeline=mem* 时**先播 prog_mem_*** 再重播当前回忆节点(§9 断言:恢复首句=时位句, ≤5s 回到可玩) |
| 菜单复述 | MEMORY 中开菜单关闭后重播当前回忆节点,底噪仍 vinyl(timeline 不丢) |
### 6.5 教学承诺清单(dungeon「铁器=楼梯」教训:每句承诺必须真实存在且可感知)
| 教学句 | 承诺 | 兑现点(断言锁定) |
|---|---|---|
| rev_teach「听到唱针落下、风声退后,是回忆。风声回来,就是现在」(全局一次,首插叙前) | 时空签名四要素 | 每次插叙必有 memory_in+vinyl+锚入 / memory_out+storm+锚出;全剧本零漏(§9) |
| b_rain「雾角两长一短——塔在,我也在」 | 雾角=塔的报平安音 | foghorn 播点=开场/CALM(1)/CALM(2)/ep_shore 恰四处 |
| b_rule「风暴夜,灯不能灭」 | 灯=规矩本体 | 灯三态兑现:ep_lamp_full/dark/hold + c3_dark 差分归因(§4.4) |
| b_goal「今夜两艘货轮过境。守住灯,也守住频道」 | 货轮/频道双目标 | c3_dark「过境的货轮,平安」+ ep_shore 晨报「航道平安」闭环 |
| rev_f4「塔上的机械上满发条,能自己转一夜」 | 发条知识 | p7 左 rev2 差分:c7_go/c7_go2、ep_lamp_full/ep_lamp_dark(剧本 A/E 对照) |
| prog_m1..3 幕句=风暴三进程 | 「风暴起/到顶/在退」 | CALM 切幕+amb_storm gain 分幕(0.5/1.0/0.7)一致 |
### 6.6 通道仲裁规则(并发声流 ≤2)
- **R1 过渡音不叠语音**:memory_in/out、static_burst 与任何 voice 播放区间零重叠——idle 门只在 voice 空闲放行,在播则排队到句间空隙(abyss sonarGate 同型)。
- **R2 插叙主导段**:前景=插叙链人声+vinyl 底噪,BGM=memory 曲;电台(阿澈)此刻静默,叙事自洽。
- **R3 通话期底噪退位**:阿澈通话期间 amb_engine 常驻低响度、voice 在播时游戏层降 -6dB(abyss R3 先例);amb_storm 同步 -4dB——「海况让给人声」。
- **R4 危机拍优先**:hull_crack/wave_wall 即发即弃可叠底噪;danger 签名+danger 震同窗;当前 voice 句播完后危机链才插续。
- **R5 回忆可重听**:MEMORY 双击=重听当前回忆节点(关键线索可重听,P1);插叙中段句可跳、首尾锚句首播不可跳(G5)。
- 声流账:任意时刻 ≤2 条前景流(voice 串行 1 + 信息音/底噪 1),BGM/amb 为背景层——ASA ≤2 约束。
## 7. 向导层接入
### 7.1 状态签名 earcon / turn cue
| 时机 | 向导动作 | 说明 |
|---|---|---|
| INTRO 进入 | `guide.state('explore')` | 叙事态签名 |
| WATCH/MEMORY 进入 | `guide.state('explore')` | 同一枚签名(时空不换 earcon,§6.2 纪律) |
| 每个抉择点 p* 起播 | `guide.state('choice')` | 选项句本身即 turn cue(W2 契约零插播) |
| CALM 进入 | `guide.state('settle')` | 喘息/结算签名 |
| ENDING 进入 | `guide.state('settle')` | 同上 |
| MENU 打开 | `guide.state('menu')` | 菜单签名 |
| 危机拍1/2 | `guide.state('danger')` | 音效前置 2s 渐强同窗(全游戏仅两处) |
| 无效手势 | `guide.error({voice:'gd_invalid', onceKey})` | error 震 + full 档语音,idle-only(F1.4) |
- earcon 词表:只用全项目统一 8 音(menu/explore/battle/choice/settle/danger/turn/nudge),**零新增**;battle 不用。
### 7.2 看门狗双轨(radio F1 + dungeon E0-1 教训:乱按也要被抢救)
**无操作轨**(guide watchdog,任何手势复位;两级不同句、用尽即停、永不惩罚):
| 等待点 | full 档 | standard 档 | 级别内容 |
|---|---|---|---|
| INTRO/WATCH/CALM 链末等待 | [25, 50] | [13, 30] | ① gd_continue ② gd_again(storyline F1.1 同款) |
| 抉择点 p* 播完无操作 | [25, 50] | [25, 50] | 重播当前 p* 提示句(13 号 §16 先例;空点单击=重挂计时) |
| ENDING 结算等待 | [25, 50] | [13, 30] | gd_continue / gd_again |
| MEMORY 链末 | 不挂(auto 回返) | 不挂 | 资产缺失兜底:30s 单句看门狗且 `isVoicePlaying()===false` 才续(radio N1 先例) |
**无进展轨**(游戏层计时,dungeon E0-1 ②同型;挂 WATCH 链末与 p* 等待两个可饿死点):
| 项 | 规则 |
|---|---|
| 进展定义 | 节点推进 / 抉择结算 / 重听。**无效手势(滑错、空点)不计进展**——无操作轨被它们复位、无进展轨不被复位,乱按 96s 必被抢救 |
| 阈值 | 60s → gd_prog1;120s → gd_prog2;用尽即停(每等待点一轮) |
| 有真实进展 | 全程零 prog 播出(不误伤正常玩家) |
| 双轨去重 | 两轨短句内容同级交叉登记;同一等待点先播过 gd_* 同义级则无进展轨跳过 |
- 向导插播一律 `queue:true`,绝不顶掉在播对白;error/watchdog 类语音 idle-only(在播则跳过且过期丢弃,F1.4)。
### 7.3 键盘变体机制(PC 键盘玩家)
- 运行时按 `gesture.inputMode()`(settings.input auto/touch/key)选 `_k` 变体资产:key → `voice/<gender>/<id>_k.*`,touch → 无后缀版;auto 跟随最近使用通道。`_k` 缺失回退无后缀版(check_all_assets 校验配对)。
- 变体清单(8 对,集中分组见 voice-ids §G0.a 与 C1 首个抉择):gd_brief / gd_continue / gd_again / sys_help / sys_menu / sys_skip / sys_continue_ask / **p1**(首个抉择的键位绑定)。
- 抉择提示 p2..p9 措辞统一「向左滑/向右滑」;键盘玩家由 p1_k 与 sys_help_k 完成首次绑定(左=←,右=→),后续语义不变。启动遮罩文案引擎自理(游戏零改动)。
### 7.4 继承 radio 三轮验证机制(照抄不重设计)
| 机制 | 来源 | 落点 |
|---|---|---|
| 帮助链去重(同等待点第 2 次上滑只震+nudge) | radio G1 | `_helpSeen` 按等待点键控(MEMORY 按插叙段键控) |
| 状态链顺序「状态在前、帮助在后」 | radio G6 | prog_m* →〔mem: prog_mem_*〕→ tr_* → tru_* → sys_help |
| 空点确认轻震(不可跳句上单击补 beat [8]) | radio G4 | p*/锚句/F4 锁各 tap 屏蔽分支 |
| tr_thin 播报位附 warning 震 | radio G2 | 全部 tr_thin 出口(含上滑复听) |
| 菜单报名游戏侧排队零截断 | radio 实现修复 #3 | `_menuQueue` 排水 |
| stop() 先 `seqToken++` 作废在途链 | radio 实现修复 #6 | 同款 |
| 新局清 choiceArmed/pendingChoice | radio 实现修复 #7 | newNight() |
| 长按误报无效(longpress 全状态合法) | radio 实现修复 #2 | 仅 left/right(ASK 另含 up/down/doubletap)报无效 |
## 8. 存档设计
### 8.1 进度 data 结构(PUT /api/progress {game:'keeper'})
```json
{
"schema": 1,
"updatedAt": "2026-09-09T12:00:00Z",
"summary": "第2幕·风暴顶 · 信任:信一半 · 结局2/4",
"phase": "WATCH",
"node": "amo_e",
"act": 2,
"timeline": "now",
"trust": 2,
"flags": { "rev1": true, "rev2": true, "rev3": false },
"p7": null,
"endings": { "e2": true },
"runs": 3
}
```
- 写入时机:每幕 CALM 进入、每个抉择结算后、ENDING 进入、菜单「保存并回大厅」。
- 断点续玩:读档 → sys_continue_ask(_k)(单击=继续上次 / 右滑=新风暴夜,runs 均 +1 统一口径);继续 = 恢复 phase+node+timeline+全部数值并**局面复述**:timeline=mem* 时恢复链 = prog_m* → prog_mem_g/f → 重播当前回忆节点(时位句先行,「我在哪个时空」一句话说清);now 则直接重播当前节点链;重进 ≤5s 可玩(L5);恢复首句受 F4 跳句锁(radio G5 例外条款)。
- summary 格式:进行中 `第N幕·<幕名> · 信任<档名> · 结局X/4`(幕名=风暴起/风暴顶/风歇前);已收夜 `风暴夜已过·<结局名> · 结局X/4`(大厅卡片直接显示)。
## 9. playtest 断言清单(登记 13 号文档 keeper 节,格式同 §21;`settings.set({guide:'off'})` 纯玩法验证,向导行为住 guide.test)
**关键路径**(剧本 A:信任满·全真相·出海接人·说出 → e1,≤60 次手势):
1. 遮罩点按 → INTRO 链(gd_brief → b_open → b_rain → b_rule → b_goal,voice 层有播放记录)→ 链末单击 → C1:c1_tune → amo_a → amo_b → ◆p1。
2. p1 左 → c1_hold(o)(e)(trust=1,首个正反馈)→ amo_c → p2 左 → c1_open(配 hatch_open)→ rev_teach(全局一次)→ **插叙1 时序断言**:memory_in → bgm 切 memory → rev_g1..g4 → memory_out → storm 回 → c1_diary(truth=1)→ CALM(1):foghorn+lull_1+tr_half(跨档)→ C2。
3. C2:c2_back → amo_d → p3 左 → c2_song(o) → 插叙2(rev_f1..f5,同款时序)→ c2_key(truth=2)→ amo_e → p4 左 → c4_true →〔插叙2 已完成:兜底不播〕→ c4_trueo(trust=3)→ 危机拍1(hull_crack 前置 2s 与 danger 同窗)→ c2_crack → amo_f → p5 左(trust=4)→ CALM(2)。
4. C3:c3_eye → c3_page → p6 左 → 插叙3(rev_c1..c5)→ c6_reade(truth=3)→ p7 左 → c7_go(配 lamp_mech,满发条版)→ 危机拍2(wave_wall+danger+danger 震)→ c3_wall → p8_boat 左 → c8_song(o) → c9_return → g9_in → g9_knot → **p9a**(truth=3 变体)→ 左 → d9_tell_a → amo_g → d9_telle → ENDING(e1):e1_a/b + ep_lead + ep_amo_home + ep_lamp_full + ep_log_told + ep_dad + ep_shore → 结算单击重开回 INTRO。
**边界断言**:
- **四结局可达(含信任逐点验算)**:剧本 B(p1左 p2左 p3左 p4左 p5左 p6右 p7右 p8_reef右 p9右,trust=4/truth=2)→ e2:c9_land + p9b + ep_amo_solo/ep_lamp_hold/ep_log_kept/ep_dad;剧本 C(p1右 p2右 p3右 p4右 p5右 → m6_seal 自动右 → p7右 p8_reef左 p9左,trust=0/truth=0)→ e3:c9_found + p9b/d9_tell_b + ep_amo_dawn/ep_lamp_hold,**ep_log_* 两句皆不播**(truth=0 日志未出场)、**ep_dad 不播**(rev2=false);剧本 D(同 C 但 p9 右)→ e4。各局 trust/truth 逐检查点与 §4.2 一致。
- **插叙触发与回返**:p2 左→插叙1、p3 左→插叙2、p6 左→插叙3 的节点顺序断言;**剧本 F**(p1左 p2左 p3右 p4左 p5左)断言插叙2 兜底在 c4_true 与 c4_trueo 之间播出且 c2_key 随后播出;回返后 now 线下一节点正确(c1_diary / c2_key 或 c4_trueo / c6_reade);插叙链 auto 完成即回 WATCH(无链末等待)。
- **时空签名断言(核心)**:全剧本转写断言——amb_vinyl 与 amb_storm/amb_room 播放区间**零重叠**;每次插叙 memory_in/memory_out 成对出现且计数=插叙完成数;锚入句(rev_g1/rev_f1/rev_c1)与锚出句(rev_g4/rev_f5/rev_c5)在位且首播不可跳(单击=beat 轻震不跳句);插叙期间 bgm=memory、回返后 bgm=storm;插叙期间 amb_engine 停播。
- **教学承诺兑现**(§6.5 清单逐条):rev_teach 全局恰一次(第二次插叙前不重播);foghorn 计数=开场+CALM(1)+CALM(2)+ep_shore 恰四处;rev_f4→剧本 A 播 c7_go/剧本 E 播 c7_go2(发条差分);b_goal→c3_dark 与 ep_shore 货轮闭环;prog_m1..3 与三幕 amb gain 档一致。
- **p6 封锁不摆假选项**:rev2=false 时 p6 提示句不播、m6_seal 播出后自动走 c6_shut,该点左右滑无效果(不丢不误触)。
- **信任/真相账目真实**:上滑链顺序 prog_m* →〔mem: prog_mem_g/f〕→ tr_* → tru_* → sys_help(G6);档位句与当前值一一对应(tr_deep≥3 / tr_half=1-2 / tr_thin=0;tru 四值逐句);tr_thin 各播报位附 warning 震。
- **看门狗双轨**:WATCH 链末 25s/50s → gd_continue/gd_again;p* 播完 25s/50s → 当前 p* 重播两次后静默;**乱按 96s**(每 2s 注入一个无效滑动):无操作轨零播出 + 无进展轨 60s→gd_prog1、120s→gd_prog2;有真实进展全程零 prog;MEMORY 链末不挂看门狗;`guide=off` 全程零定时器零向导事件。
- **键盘变体**:settings.input='key' 会话播出 gd_brief_k/sys_help_k/p1_k 且对应无后缀版零播出;'touch' 反之;'auto':keydown 后下一句用 _k。
- **中断复述含时空位**:杀进程重进(存档 timeline='mem_f'、node=rev_f3)→ sys_continue_ask → 单击 → 恢复链首句=prog_m2 → prog_mem_f → 重播 rev_f3 起链(恢复首句不可跳,第二句起可跳);now 态存档(node=amo_e)恢复=重播该节点链。
- **pendingChoice**:p* 播放中左右滑 → 播完立即生效(不丢、不重复、连滑取最后一次)。
- **p* 不可跳**:p* 播放中单击 → 不跳句(PROMPT_RE 门)+ beat 轻震(G4)。
- **idle-only**:向导 gd_* 在对白在播时跳过且不占 onceKey(F1.4);queue 句排在当前句后零截断。
- **菜单**:下滑开菜单 → 上滑帮助 → 双击「回到值守」→ phase/node 不变、当前节点重播;**插叙中开关菜单**后底噪仍 vinyl、重播当前回忆节点;单击循环顺序 = [回到值守, 重新开始今夜, 保存并回大厅];确认回大厅 → sys_menu_lobby 播出且 PUT/localStorage 有最新 data。
- **结算链死窗**:e* 结局链/c3_dark/插叙链开播后 800ms 内注入在途 tap → 手势被吞、链完整有序播出。
- **危机拍**:hull_crack/wave_wall 前置 2s 渐强、danger 签名与音效同窗、danger 震遍间隔 ≥400ms;当前 voice 句播完危机链才插续。
- **资产缺失**:屏蔽任意 voice 资产 → 30s 看门狗自动续链到结局不卡死;NPC 滤波资产缺失回退原声可播。
- **数字规范**:prog_*/tr_*/tru_*/gd_* 全部整句字面量,静态审计无运行时拼接碎片。
**失败即不通过**:任何等待 >5s 无声(静默死局);无效手势零反馈;**vinyl 与 storm/room 底噪重叠**;**插叙缺过渡音或缺锚句**;结局计数与 §3.4 不符;档位句与 trust/truth 值不符。
## 10. 节奏曲线(风暴三幕)
| 段 | 内容 | 目标时长 | 设计目标 |
|---|---|---|---|
| 开场 | 塔名+规矩+目标+第一声呼救 | 1-1.5 min | 首输入 ~30s(INTRO 链 ~30s 链末单击);规矩一句讲完(P4);雾角承诺音埋下 |
| 幕一 风暴起 | p1/p2 + 插叙1 + CALM(1) | 4-5 min | **首个正反馈 ≤3min**(~2.5min p1 左阿澈声音稳住);**时空签名教学+首次兑现**(rev_teach→插叙1 四要素全播);两抉择条条有出路 |
| 幕二 风暴顶 | p3/p4 + 插叙2 + 危机拍1 + p5 | 6-7 min | 情感段(父亲真相 p4);第一危机拍(前置 2s 预警);插叙2 供货发条知识(p7 兑现预告) |
| 幕三 风歇前 | p6+插叙3 + p7 核心 + 危机拍2 + p8 | 7-8 min | 紧张峰值:三代秘密见底→「守灯还是救人」宿命抉择→浪墙下的执行差分;**复刻祖父之夜的暗线**(剧本 E 灯灭) |
| 签收+结局 | p9 + 结局链 + 尾声回响 | 2.5-3 min | 心结抉择(说/瞒×全/碎片)+ ep 逐项点名(P2 回响);开环钩子拉下一夜 |
- 合计 ≈ 21-24 min(15-25 达标);每 30-60s 一个反馈颗粒(阿澈反应句/结果句/档位句/插叙锚句),无沉默尴尬期(P3)。
- 难度曲线 = 情感与信息复杂度(接住人→翻开过去→宿命抉择→秘密归宿),操作复杂度恒定(永远二选一)——P4「规则一遍听懂」由恒定操作面保证;风暴强度=氛围曲线(amb gain 三档),不构成玩法压力(无倒计时无死亡)。
## 11. 自检清单(策划交付自验;☐ = 待开发/测试轮填)
| # | 硬约束/教训 | 落地 | ✓ |
|---|---|---|---|
| 1 | FTUE 首输入 <60s | INTRO 链 ~30s 链末单击(§10) | ☑ |
| 2 | FTUE 首个正反馈 ≤3min | ~2.5min p1 左阿澈稳住(c1_holdo+c1_holde) | ☑ |
| 3 | 学习期零惩罚 | 幕一全组合不扣资源;右路只即时纠偏(c1_aske);无超时惩罚 | ☑ |
| 4 | 失败三问归因+归因真话律 | c1_aske/c3_dark/c9_found 指向实际选择;d9_tell_b 在 truth=0 仍句句为真;§4.4 无假归因 | ☑ |
| 5 | 资源账目真实(radio rep_low 教训) | tr_* 三档逐区间为真;tru_* 四值逐值为真;跨档才播 | ☑ |
| 6 | 手势宪法 6 手势默认语义零豁免 | §2.3 全覆盖;菜单 §5.2 模态约定 | ☑ |
| 7 | 长按显式登记 | 无豁免:长按=引擎快进(12 号叙事类不豁免),§2.3 在案 | ☑ |
| 8 | 向导:签名/turn cue/看门狗两级不同句 | §7.1/§7.2;gd_continue→gd_again;MEMORY 链末不挂 | ☑ |
| 9 | 看门狗双轨(dungeon E0-1/radio F1 教训) | 无操作轨+无进展轨(§7.2);乱按 96s 必被抢救 | ☑ |
| 10 | 插播 queue 不顶对白;纠错 idle-only 过期丢弃 | §7.2(F1.4 契约);过渡音 idle 门(§6.6 R1) | ☑ |
| 11 | 死亡/失败结算链完整(dungeon H1 教训) | e* 链/c3_dark/插叙链 800ms 手势死窗;阿澈恒活着(零死亡红线) | ☑ |
| 12 | 教学承诺兑现(dungeon「铁器=楼梯」教训) | §6.5 承诺清单 6 条逐条绑兑现点+断言 | ☑ |
| 13 | 单次播报 ≤3 选项 | 全部抉择恒 2 选项;菜单 3 项循环 | ☑ |
| 14 | 并发声流 ≤2 + 通道仲裁 | voice 串行+底噪/信息音互斥(R1-R5,§6.6);断言零重叠 | ☑ |
| 15 | earcon ≤7、统一词表、语义靠语音 | 只用统一 8 音中 7 个(battle 不用),零新增;时空签名住内容层 | ☑ |
| 16 | 台词一句一文件 ≤40 字、数字分档整句、无运行时拼接 | voice-ids 全表 ≤35 字(最长 p7);prog/tr/tru 分档整句;句内数字写死 | ☑ |
| 17 | 多音字规避/同 instruct 常量/无语速词 | voice-ids 头部+文末自查(落/数/缝/卷/转/重 试听项) | ☑ |
| 18 | PC 键盘变体(提示句集中+标注) | 8 对 _k 变体集中分组(voice-ids §G0.a);inputMode() 运行时选择 | ☑ |
| 19 | P1:可打断/重听(含回忆节点)/status key/不当白痴 | 跳句+双击(MEMORY 重听 R5)+上滑时位句链+成功零补救教学 | ☑ |
| 20 | P2:3 分钟见效/结局回响 | ~2.5min 阿澈声音稳住;ep 逐项点名(阿澈/灯/日志/父亲班次) | ☑ |
| 21 | P3:3 分钟颗粒/中断复述含时空位/单手 | 一幕=4-8min 段落+边界存档;恢复链 prog_mem_* 时位句先行;全手势免视单手 | ☑ |
| 22 | P4:一遍懂/容错/无高压倒计时/不骂人 | 一句规矩;零死亡零倒计时;NPC 句 ≤27 字+逗号停顿 | ☑ |
| 23 | 抉择树 4 结局可走通验算 | §4.4 穷举+§9 剧本 A-D(e1-e4 各一条实路径)+E/F 差分剧本 | ☑ |
| 24 | 单局 15-25 min | §10 曲线 ≈21-24 min | ☑ |
| 25 | 分支不爆炸(radio 纪律) | 跨幕仅 trust+3 bool+p7/p9 派生;~75 节点固定深度(§3.3) | ☑ |
| 26 | 台词 ≤160、NPC ≤5 单音色 | 148 句(双 121+NPC 27)/3 个 NPC 音色(voice-ids 账目) | ☑ |
| 27 | 引擎边界诚实 | §6.1 能力表:滤波/loop/互让全写明资产侧或降级方案 | ☑ |
| 28 | 存档带 summary、断点续玩 | §8:data+summary 格式、timeline 恢复链、≤5s、局面复述 | ☑ |
### 批 1 教训内化确认(5 条全落地)
| 教训 | 本作机制 | 断言 |
|---|---|---|
| 教学承诺必须兑现 | §6.5 六条承诺清单(时空签名/雾角/灯/货轮/发条/三幕) | §9「教学承诺兑现」块 |
| 看门狗双轨 | §7.2 无操作轨+无进展轨;无效手势不复位进展轨 | §9「看门狗双轨」块 |
| 档位措辞真实 | §4.3 tr 三档逐区间+tru 四值逐值为真 | §9「信任/真相账目真实」块 |
| 失败结算链完整不被打断 | 800ms 手势死窗盖 e* 链/c3_dark/插叙链;seqToken 作废在途链 | §9「结算链死窗」块 |
| PC 键盘操作提示 | 8 对 _k 变体+inputMode() 选择+集中分组标注 | §9「键盘变体」块 |
### 时空签名教学-兑现验算(本作特有自检)
| 承诺(rev_teach 四要素) | 教学点 | 兑现次数(剧本 A) | 断言位 |
|---|---|---|---|
| 唱针落下(memory_in) | rev_teach 全局一次(首插叙前) | 插叙 1/2/3 各一,共 3 | §9 插叙触发与回返 |
| 风声退后(vinyl 底噪、storm/room 淡出) | 同上 | 3 段插叙全程,零重叠 | §9 时空签名断言 |
| 唱针抬起+划针(memory_out) | 同上 | 3 次,与 in 成对 | §9 时空签名断言 |
| 风声回来(storm 回切+锚出句) | 同上 | 3 次(锚句 rev_g4/rev_f5/rev_c5) | §9 时空签名断言 |
| 「我在哪」三层兜底 | 上滑时位句(prog_m*/prog_mem_*)+恢复链时位句先行+菜单关闭底噪不丢 | 全程可查;§8/§9 | §9 中断复述含时空位 |
### 结局验算(3-4 结局条款 → 定 4)
e1=trust≥3∧p9左(剧本 A:trust4/truth3);e2=trust≥3∧p9右(剧本 B:trust4/truth2,p9b);e3=trust<3∧p9左(剧本 C:trust0/truth0,p9b+d9_tell_b);e4=trust<3∧p9右(剧本 D)。四格互斥完备、各有一条实路径、trust/truth 全档可达(§4.4)。
### 台词 ≤40 字验算
voice-ids 全表最长 = p7(35 字),次长 p9a/p9b(各 32 字)、d9_tell_a(31 字);NPC 句最长 amo_c(25 字)——全部 ≤40 ✓(逐句账见 voice-ids 文末自查)。