Files
SoundGame/docs/design/19-cipher.md
T
xsl 4b39ae2e49 feat(new8/batch3-4): cipher/teahouse/keeper/bridge 全套交付——new8 八款全部可交付收官
四泳道并行全流水线(资产→开发→测试→玩家迭代闭环),双双 R1-R2 连续达标裁定可交付:
- cipher《暗码》223 断言:摩尔斯解码嵌叙事/四结局矩阵/暴露度逐NIGHT验算/三层兜底;R2 三修(链首标记音/预警震/结局计数句+4句补录)4/4 全满分档
- teahouse《深夜茶馆》205 断言:泡茶时机窗/经济逐晚对账/多晚弧线;连续 2 轮 4/4 愿意玩 P0=0
- keeper《守塔人》257 断言:时空签名教学-兑现/插叙触发回返/trust×truth 档位/4结局;R2 F1/F2 坐实(震型豁免/首播跳句锁)4/4
- bridge《星环舰长》336 断言:巡检节奏逐毫秒/危机×系统态矩阵/仲裁7场景/忠诚差分/评级验算;R1 当场修F1-F4、R2 F5 菜单顺延保护 4/4 上行

公共:index.html 四卡片+profiles.js 四桶+check_all_assets 登记(预接线)、00-program 八款进度账收账、
gen_voice_dual 四游戏段、README/HANDOVER 阵容更新(11 款)、voice_text.json 四游戏键、mureka 台账(累计$1.26/$2.00)

验证:四游戏 playtest 223/205/257/336 全绿 + guide 122/122 + 全项目 15 游戏资产终检绿
遗留:keeper 三代人声滤波人耳试听(最高优先,ASR 对滤波句失明)、bridge 音频 32MB 超软指引 2MB(压缩候选)
2026-09-14 05:48:13 +08:00

56 KiB
Raw Blame History

19 · 《暗码》(cipher) — 1940 密战叙事 RPG 设计文档

给开发的实现要点摘要

  1. 叙事模式(声命线范式,radio/abyss 同骨架):节点链 onComplete 串行 + seqToken 打断作废 + pendingChoice 缓存;复制 web/radio/(或 web/abyss/)结构起步,状态机 6 宏状态(INTRO/RX/DECODE/RELAY/NIGHT/ENDING + MENU 覆盖层)。
  2. 核心增量 = 电文解码小局嵌叙事(DECODE 态):摩尔斯音由运行时程序合成模块(morse.js,guide earcon 同先例)直接排程 oscillator——不走音频文件(有损编码在 70-160ms 短音上有 ms 级抖动,毁时值精确);事件钩子供 playtest 固定字符断言。
  3. 手势:全局默认映射 + 唯一豁免 E1(DECODE 空闲长按=翻密码本求提示,与引擎快进时间互斥,detective E2 同款);菜单走 00 号 §5.2 模态约定。摩尔斯三层兜底:正常译(原速+候选对照)/ 慢放(双击第 2 次 0.5×)/ 提示直译(密码本 3 页,逐值账目)。
  4. 暴露度 = 步进隐藏资源(radio 口碑同型):译错 +1/封(教学封 T1 恒 0)、危险抉择 +1/+2;分档整句、跨档才播、上滑可查;档位措辞逐值为真(book_1..0 / exp 四档均按实数措辞)。
  5. 译错归因真话:教学封 T1 译错即时归因(真词字母的摩尔斯写死在句里)+ 无限重选零惩罚;正片 T2-T4 译错不揭穿(进入剧情误读分支),真相在尾声回响兑现(ep_* 与 flags 单源对应,detective 信息单源思想)。
  6. 台词依据 19-cipher-voice-ids.md(139 句:双性别 134 + 平铺 NPC 5);键盘变体 11 对 _k 集中标注;结算链 800ms 手势死窗(abyss/地下城 H1 同款)。
  7. 章程任务 N3(research/new8/00-program.md §2):单局 15-25 分钟、9 抉择点(4 译词 3 选 + 5 叙事 2 选)、4 结局、分支局部化。断言清单住本文档 §9;13 号文档已登记指针(§22)。

1. 概述

  • 一句话:1940 年江城,你是电讯处的夜班报务员,也是「北风」安在敌台内部的一双耳朵——一晚四封密电过你的手,每个词怎么译、报不报、报给谁,都由你按下。
  • 题材(声音即世界):谍战的本体就是听——摩尔斯的滴答是情报本身、报务员的「手法」是 fingerprint、电台静电是世界底色。「用耳朵当武器」与纯音频载体天然同构:视觉谍战需要的文件/密写/表情,在这里全部是声音。
  • 单局:一个完整夜班(一条抉择路径)19-22 分钟;四封电文共享主干(截获→解码→处置→夜结算),分歧局部化在每封内部;9 个抉择点(4 译词 + 5 叙事,全部恒在),4 结局。
  • 核心循环:截获(调谐+电文摩尔斯首播)→ 解码小局(听音辨词:候选对照/慢放/提示三层兜底)→ 译文揭晓(你的认知,对错不揭穿)→ 处置抉择(报北风/压日志/呈处长/亲自交接/反喂假电)→ 夜结算(暴露度跨档播报)→ 下一封。终局 p9 用「发最后一电」把整晚收在你的电键上。
  • 与 radio/abyss 的差异点(骨架同、压力源不同):radio 零压力、abyss 资源稀缺(氧气);cipher 的压力 = 隐藏的怀疑账本(暴露度)+ 认知风险(你永远不确定自己译对了没有——译错不是失败,是走另一条真实存在的剧情)。全部宽限化:无倒计时、无中途死亡、软失败只损失「情报效果」不损失玩家已有物。

2. 摩尔斯解码小局(信息设计,本作核心差异层)

2.1 电码呈现规格

电码表(标准摩尔斯;本作 4 封电文只用到 13 个字母,模块内置全 A-Z 表,断言只锁用到的):

字母 码 字母 码 字母 码 字母 码
A ·− H ···· O −−− T −
B −··· I ·· P ·−−· Y −·−−
D −·· L ·−·· R ·−· S ···
N −·

时值与音色(运行时合成,web/cipher/morse.js):

参数 值 说明
单位 u 原速 80ms(dit=1u 音、dah=3u 音、字内隙 1u、字母隙 3u) ≈15 WPM 等效;u 是常量写死,不随 settings.speed(见边界表)
慢放档 u=160ms(0.5×),字母隙加宽至 6u 双击第 2 次触发;三层兜底第 2 层
候选对照档 u=110ms,字母隙 5u 密码本「念给你听」的质感
音色 电文 750Hz / 候选 620Hz,正弦 + 5ms cos 边沿,gain 0.5 频率差 = 「电波里的」vs「密码本上的」,防听感混淆
首尾 lead-in 400ms 静音,结尾 400ms 静音 排程器自然包含
时长例 BAY 原速 ≈2.6s / HAND ≈2.8s / TOP ≈2.8s;慢放 ≈2× 单次听觉负荷远低于 40s 红线
事件钩子 morse.play(word,{hz,unit}) 逐元素回调 `{type:'dit' 'dah',letter}`
  • 为什么运行时合成而非 SA3/SYN 文件:①时值精确是玩法命根(H···· vs S··· 只差一个 80ms 的 dit,ogg/m4a 有损编码在短音上有数 ms 抖动与预卷,文件方案直接否决);②三档速度 × 双频率 × 13 字母的组合用文件不可维护;③零资产缺失风险。guide earcon(14 号裁定 3)运行时合成先例。

2.2 电文与候选设计表(信息单源:真词/候选/辨析位写死)

封 电文名 真词(义) 候选(义) 共享 辨析位 密码本提示揭示
T1 船期 BAY(货船) DAY(客车)/ RAY(巡逻艇) _AY 1:B(−···)/D(−··)/R(·−·) 第 1 字母是 B,一长三短
T2 名单 TOP(黑名单) TAP(窃听令)/ TIP(线人费) T_P 2:O(−−−)/A(·−)/I(··) 第 2 字母是 O,三长
T3 交接 HAND(今夜交接) SAND(明晨交接)/ LAND(地点变更) _AND 1:H(····)/S(···)/L(·−··) 第 1 字母是 H,四短
T4 处置令 SPY(内线确认) SAY(约谈)/ SLY(内部排查) S_Y 2:P(·−−·)/A(·−)/L(·−··) 第 2 字母是 P,短两长短
  • 候选纪律:三候选等字母数、共享其余字母、唯一辨析位;提示揭示辨析位字母 ⟹ 候选唯一(每行验算成立,playtest 断言「提示流可全对」)。
  • 难度曲线:T1(B/D 差一个尾 dit,教学期有慢放+免费提示兜底)→ T2(中间字母 O/A/I 时值差异大,最易)→ T3(H/S/L:H 与 S 只差一个 80ms 尾 dit,全作最狠听辨)→ T4(P/L 需听完整字母,中高)。曲线说明:难度=听辨精度,操作面恒定(左右滑+单击+双击)。
  • 电文摩尔斯(固定字符断言对象):BAY=−··· / ·− / −·−−;TOP=− / −−− / ·−−·;HAND=···· / ·− / −· / −··;SPY=··· / ·−−· / −·−−。

2.3 解码交互流(DECODE 态内)

RX 链:rx*_in(引入)→ sfx/tune_seek → 电文摩尔斯首播(原速 750Hz,auto,不可跳)
      → guide.turn → dv*_ask(提示句,choiceArmed)→ 浏览等待
浏览:左右滑 = 循环切换候选(每切自动预听:候选摩尔斯 620Hz 对照档 → 静默一拍 → cand_* 词义句)
      双击 = 重听电文(原速);【再双击】= 慢放 0.5×(两级内容不同:速度即差异)
      长按(语音空闲)= 翻密码本(§2.5)
      单击 = 进确认:cf_confirm + 电文重播原速一遍 → 再单击 = 定下(判定出口 800ms 死窗)
确认态:左右滑 = 取消回浏览;双击 = 照常重听电文
  • 三层兜底(成本递增的梯度,全部可独立走通,P3 嘈杂/P4 疲惫的双保险):
层 手段 成本 覆盖
1 正常译 原速电文 + 候选对照预听匹配 最快 安静环境/耳力好
2 慢放 双击第 2 次 0.5×,无限次免费 时间 通勤嘈杂听不清长短音(P3);无限兜底层
3 提示直译 密码本 3 页,每页直译 1 封的辨析位字母 资源(页) P4 保底:提示+候选对照 ⟹ 唯一解(§2.2 验算)
  • 教学期(T1):dv1_teach/dv1_teach2 先教「双击重听→再双击慢放」「左右滑候选、单击选定」;T1 判定有即时反馈(对=success 震+dv1_right;错=dv1_wrong 即时真话归因+无限重选,零惩罚)——正片 T2-T4 判定无对错反馈,直入剧情(译错是你不知道的分支)。教学承诺「练习有答案,正片靠耳朵」在 dv1_right 说破。

2.4 密码本机制(提示额度经济)

项 规则
页数 初始 3 页(叙事=北风给你的三页「电码补遗」);全局不再补页
兑换 DECODE 空闲长按(E1 豁免)或菜单「翻密码本」项(P4 冗余入口);消耗 1 页 → 播 hint_*(「第 N 个字母,是 ○」+ 摩尔斯口述),留在浏览态
教学免费 T1 第 1 次翻页不扣(hint_t1:「我教你一页」)——Prime→Teach 承诺兑现
账目真实 状态链随时可查:book_3「还剩三页」/ book_2「还剩两页」/ book_1「只剩最后一页」/ book_0「一页都不剩了」——逐值整句,不拼接
页数用尽 再翻 = error 震 + sfx/paper_page(翻空本声),无语音(零静默、不轰炸)
与结局关系 页数不进结局判定(只换准确度);纯提示流(T1 免费+3 页)可全对(§5.5 剧本 E 验算)

2.5 译错归因真话(detective 信息单源思想)

场景 播报 真话依据
T1 译错(教学) dv1_wrong「不对。听清楚:B,一长,三短。」 真词字母的摩尔斯写死在句里,与 §2.2 电码表一致;无限重选零惩罚
正片译错 不揭穿:rev_* 按你选的候选播「你的认知」(rev_top/tap/tip…) 旁白只说你以为的事;真词唯一、由数据表单源决定
后果分支 你的行动按认知展开(报错船期/留错时间/呈报错令);暴露度 +1 「译文进了纸面,与对家底档不符」——事件表单源(§5.2)
真相揭晓 尾声 ep_hand_no「码头的灯,白亮了一夜」/ ep_spy_no「差的那三个字母,差了一整条命」 ep_* 由 flags(t3==hand 等)选播,播报=存储;禁止「运气不好」类假归因

3. 状态机

3.1 状态列表

状态 内容 BGM guide.state
INTRO 开场链:夜班开场 + 一句规矩 + 今晚目标 + 处长夜令 bgm/radio_room explore
RX 截获:引入句 + 调谐 + 电文摩尔斯首播(auto 链) bgm/radio_room(摩尔斯期间 −6dB) explore;摩尔斯首播后由 turn cue 承担
DECODE 解码小局:浏览/确认/重听/慢放/提示(§2.3) 同上 choice
RELAY 处置叙事:rev_* 揭晓链 + 后果 + 叙事抉择 p2/p4/p6/p8 bgm/radio_room;RELAY(4) 起 crossfade 至 bgm/closing explore;抉择点=choice
NIGHT 夜结算:n* 过渡句 + 暴露档位跨档播报(如有),auto ≤12s bgm/night settle
ENDING 结局链 + 尾声回响(epilogue),结算态 bgm/ending settle
MENU 模态覆盖层(任意叙事态下滑呼出,暂停一切链) bgm/radio_room 低响度淡入 menu
  • 附 ASK(存档询问态,radio 同款):单击=继续上次 / 右滑=新夜班;上滑/下滑/左滑/双击=无效(error 震+gd_invalid)。

3.2 转移图(文本)

                    ┌──────────── 结算单击=新夜班 ────────────────┐
                    ▼                                            │
 INTRO ──链完──> RX(1) ──首播完──> DECODE(1) ──两段确认+判定──> RELAY(1) ──链完──> NIGHT ─┤→ RX(2)
                (调谐+引入+摩尔斯首播    浏览/确认/重听/慢放/提示     揭晓+p*+结果链        │   …(T2/T3 同型)…
                  auto,首播不可跳)        (T1 判定有即时反馈)                        │
                 └──────────────────────────────────────────────────────────────────────┘
 RELAY(4) ──链完──> 签收 g9_in → g9_knot → ◆p9 ──> d9 结果链 ──> ENDING(e1..e4 + ep 回响链)

 任意叙事态(INTRO/RX/DECODE/RELAY/NIGHT/ENDING):下滑 → MENU(模态)
   MENU:单击=循环切换选项 → 双击=确认执行 → 上滑=帮助 → 下滑/「回到电波」项=关闭回原状态
         (RELAY 关闭后重播当前节点;DECODE 关闭后重挂浏览并自动预听当前候选——局面复述)
  • RX 首播链与 NIGHT 链 auto:true;DECODE 浏览为武装等待;RELAY 叙事节点链末等待单击(radio 实现核对 #1 同口径)。
  • 判定出口(DECODE 第 2 击确认)、NIGHT 链、d9 结果链、e* 结局链入口处盖 _settleGraceUntil = now+800ms 手势死窗(dungeon H1/abyss 同款),窗内手势一律吞掉——揭晓与结算链完整播出。
  • MENU 打开时 guide.disarm() + 取消一切看门狗/自动链 + 停摩尔斯排程;关闭后按原状态复述(L5 局面复述,radio closeMenu 先例)。

3.3 每状态手势映射(6+2 全覆盖)

手势 INTRO/RX DECODE 浏览 DECODE 确认 RELAY NIGHT ENDING MENU
上滑 帮助+状态,随后重播当前段 状态链(prog_t→book 档→exp 档→sys_help,状态在前),留在浏览 同左 帮助+状态,随后重播当前节点 帮助+状态,自动链继续 帮助+状态(结局名+档) 帮助(sys_help)
下滑 模态菜单 模态菜单 模态菜单 模态菜单 模态菜单 模态菜单 关闭菜单
左滑 无效 → error 震+gd_invalid 上一候选(循环,预听);首播中=beat 轻震(摩尔斯不可跳) 取消确认,回浏览 抉择=左 无效 → gd_invalid 无效 → gd_invalid 无效 → error 震
右滑 无效 → gd_invalid 下一候选(循环,预听) 取消确认,回浏览 抉择=右 无效 → gd_invalid 无效 → gd_invalid 无效 → error 震
单击 播放中=跳过本句;链末=继续 进确认:cf_confirm + 电文重播 定下判定(800ms 死窗 → 揭晓链) 播放中=跳过本句(p* 不可跳);链末=继续 跳过本句(自动链) 再来一局(新夜班) 循环切换选项
双击 重听当前段 重听电文(原速);第 2 次=慢放 0.5×(任何其他手势重置计数) 同左(确认态保留) 重听当前节点台词(含抉择提示) 重听夜结算链 重听结局链 确认执行(菜单约定覆盖重听)
长按 引擎接管:旁白临时 1.7×(12 号 §2.2,叙事类不豁免) 空闲=翻密码本(E1 豁免);语音在播=引擎快进 同左 引擎快进 引擎快进 引擎快进 引擎快进(菜单语音)
长按松开 引擎接管:恢复 settings.speed 同左 同左 同左 同左 同左 同左

豁免登记(必须显式,仅 1 条):

# 豁免 理由(先例)
E1 DECODE 空闲长按 = 翻密码本求提示(核心资源动词快捷入口) 绑定条件=无语音在播(!audio.isVoicePlaying()),与长按快进(仅在播时生效)时间互斥、零冲突(detective E2 同款);摩尔斯为 sfx 域不触发快进;菜单「翻密码本」提供冗余入口(P4 容错)
  • 其余全部符合宪法默认语义:单击=确认/继续(两段确认=确认的两击)、双击=重听(重听对象=电文)、左右滑=选择/移动、上滑=状态、下滑=菜单(00 号 §5.2 模态约定);菜单=单击循环+双击确认。
  • 继承 radio G5 跳过公平契约原文:首播必完整(摩尔斯首播不可跳、p* 不可跳、链末关键句保护、ASK 恢复首句锁)、复播可即掐(锁不随复播重新武装、链中段句可跳、复播滑动照常预缓存)。
  • 抉择 prompt(p2..p9)播放中单击不可跳(PROMPT_RE 门)+ beat 轻震(G4);左右滑缓存 pendingChoice 播完即生效(radio W2 契约)。
  • 无效手势零静默:所有「无效」格 error 震 + gd_invalid(onceKey 同局限次,F1.4 idle-only)。
  • 首播保护窗:电文摩尔斯首播发生在 RX 链尾、DECODE 入口前(§3.2),期间一切玩法手势(左右滑/单击/双击)= beat 轻震(G4),不判无效、不惩罚——摩尔斯走 sfx 域本无跳句通道,「不可跳」由构造保证;首播完 400ms 内 guide.turn + dv*_ask 落地,浏览武装。

3.4 摩尔斯/语音双通道仲裁(abyss 声呐双通道 R1-R5 同型)

  • 通道定义:摩尔斯通道 = morse.js 的 osc 排程(电文+候选)+ sfx 层(静电/电键);语音通道 = voice 层(你+北风+处长)。BGM 第三层背景(voice 播时引擎 duck)。
  • R1 摩尔斯不叠语音:摩尔斯排程只在 !audio.isVoicePlaying() 时起振;在播则挂队列表、voice onComplete/句间轮询补发(abyss sonarGate 同款)。候选预听链 = 候选摩尔斯 → 静默一拍(≥500ms)→ cand_* 词义句(给耳朵留对照时间)。
  • R2 电文主导段(首播/重听/慢放):前景=摩尔斯,BGM dip;voice 只在摩尔斯结束后接播(dv*_ask 固定在首播后)。
  • R3 BGM 退位:摩尔斯排程期间游戏层把 BGM 降 −6dB(abyss 远程脉冲垫先例),不消失(A2 无静默死局)。
  • R4 危机拍优先:T4 揭晓 danger 拍(step_halt 前置 2s 渐强 + danger 签名 + danger 震);当前 voice 句播完后危机链才插续(radio C5/abyss D2 同款)。
  • R5 摩尔斯可重听:DECODE 双击=重听电文(P1 关键线索可重听;第 2 次=慢放是本作对 R5 的加强)。
  • 声流账:任意时刻 ≤2 条前景流(voice 串行 1 + 摩尔斯/信息音 1),BGM/amb 为背景层——ASA ≤2 约束 ✓。

引擎能力边界(对照 docs/web-engine-api.md):

能力 引擎现状 方案
摩尔斯精确时值 sfx 走文件(有损编码 ms 抖动) 运行时 osc 模块(§2.1;guide earcon 先例),三档速度一个参数
morse 与 voice 互让 ❌ sfx 总线独立无 duck 游戏层 morseGate 空闲门(R1)
BGM 手动 dip ❌ 引擎只对 voice duck 游戏层 gain:摩尔斯期间 −6dB(R3)
voice 运行时滤波 ❌ 不使用(detective 先例:区分度靠 speaker+instruct+语境;处长=当面训话不需滤波)
sfx 循环 ❌ 即发即弃 游戏层 buffer.loop=true(amb_room/static_bed,radio _ringSrc 先例)
变速不变调 ✅ SoundTouch(仅 voice) 摩尔斯不受 settings.speed/长按快进影响(u 恒定)——边界登记:P1 2.2× 档语速下电文仍原速,慢放兜底;列入 live 真人耳测项

4. 抉择树

4.1 全局骨架(四封电文共享主干,分歧局部化)

INTRO: b_open → b_rule → b_goal → b_chief ──> RX(1)
【T1 船期·教学】BAY 译词 ◆p1 + 报/压 ◆p2 ──> NIGHT(1) ──> RX(2)
【T2 名单】TOP 译词 ◆p3 + 暗处理/呈报 ◆p4(known/blind 差分)──> NIGHT(2) ──> RX(3)
【T3 交接】HAND 译词 ◆p5 + 亲自/留书 ◆p6 ──> NIGHT(3) ──> RX(4)
【T4 处置令·高潮】SPY 译词 ◆p7 + 处长试探 ◆p8(反喂/装傻,known/blind 差分)──> 签收 ◆p9 ──> ENDING

分支纪律:每封电文内部分支、夜结算时收敛;跨电文只传标量(expose、hints、4 个译文 flag、4 个抉择 flag),不生成组合节点。最坏路径与最好路径节点数同型。

4.2 分封结构(◆=抉择点,〔〕=条件差分)

【T1 船期·教学】
RX(1): rx1_in → tune_seek → 【摩尔斯首播 BAY,不可跳】→ dv1_teach → dv1_teach2 → dv1_ask → 浏览
  ◆p1 译词(3 选 1,两段确认)
   ├候选 BAY(对): dv1_right(success 震)──────────────────────┐
   └候选 DAY/RAY(错): dv1_wrong(即时真话归因)→ 回浏览无限重选 ┘(T1 恒收敛为 BAY)
  RELAY: rev 由 dv1_right 承担 → ◆p2
   ├左 报北风: r1a_send → r1a_e(med_ok 预埋)──┐
   └右 压日志: r1b_hold → r1b_e(expose+1)────┴→ NIGHT(1): n1 → 〔跨档 exp_*〕→ RX(2)

【T2 名单】
RX(2): rx2_in → 首播 TOP → dv2_ask → 浏览 → ◆p3 译词
   ├TOP(对): rev_top「黑名单。头一个代号,叫码头」→ p4_known ──┐
   └TAP/TIP(错,不揭穿): rev_tap / rev_tip → p4_blind ─────────┤(expose 译错+1)
   ◆p4: ├左 暗处理: r2a_act → r2a_e(expose+1;known=warn 码头 / blind=压下)┐
        └右 呈报: r2b_act → m2_chief → r2b_e(mateListed)────────────────┴→ NIGHT(2): n2 …

【T3 交接】
RX(3): rx3_in → 首播 HAND → dv3_ask → 浏览 → ◆p5 译词
   ├HAND(对): rev_hand → 〔p6 行动按「今夜」〕────────────┐
   └SAND/LAND(错): rev_sand / rev_land → 〔按误译时间行动〕┤(expose+1)
   ◆p6: ├左 亲自去: r3a_go → r3a_e(expose+2)──────────────┤
        └右 留书暗号: r3b_act →〔t3==hand ? r3b_e_ok : r3b_e_no〕┴→ NIGHT(3): n3 …

【T4 处置令·高潮】
RX(4): rx4_in → 首播 SPY → dv4_ask → 浏览 → ◆p7 译词
   ├SPY(对): rev_spy(danger 拍:step_halt 前置 2s+签名+震)→ p8_known ──┐
   └SAY/SLY(错): rev_say / rev_sly → p8_blind ──────────────────────────┤(expose+1)
   处长试探: m4_chief_a → m4_chief_b → ◆p8
   ├左 反喂假电: r4a_feed(配 key_send)→ r4a_e(expose+2,feed=true)──┐
   └右 装傻: r4b_act → m4_chief_c → r4b_e ────────────────────────────────┴→ 签收
【签收】g9_in → g9_knot → ◆p9〔t4==spy ? p9_known : p9_blind〕
   ├左 发最后一电: d9_send(key_send)→〔pre≤4 ? d9_send_e : d9_caught_e〕→ ENDING(e1|e3)
   └右 关机: d9_sleep → d9_sleepe → ENDING(e2|e4)

ENDING: e*_a → e*_b(按 expose 档 × p9 四选一)→ ep_lead → ep_med_* / ep_list_* / ep_hand_* / ep_spy_*(按 flags 选播)

4.3 分支局部化说明

  • 跨电文状态全部是标量:expose(int)、hints(int 0-3)、译文 flag t1..t4(每封所选候选 id;T1 恒真词)、抉择 flag p2..p9(左/右)。无任何「电文 i × 电文 j」组合节点。
  • 条件差分全部是节点内选播(radio w1_say/w1_say2 先例):p4/p8/p9 的 known/blind 双提示(由 t2/t4 决定)、r3b_e_ok/no(由 t3 决定)、d9_send_e/caught_e(由 expose 档决定)、ep_* 8 句(由 4 组 flag 决定)。树形不变。
  • 树规模:139 句台词、约 75 个节点;任一路径恒走 9 个抉择点(4 译词 + p2/p4/p6/p8/p9)——比 radio/abyss 更规整(无跳点路径),操作面完全恒定(P4)。

4.4 四结局达成条件

结局 条件 主题 开环钩子
e1 最后一电 p9 前 expose ≤4 且 p9=左 警报发出、北风撤尽,你随挑水的队伍出城 「新的密码本,在城外渡口等我」
e2 值完这个班 p9 前 expose ≤4 且 p9=右 平安交班,一切照旧——但北风的呼叫没人接 「下一次呼叫,你接不接?」
e3 孤灯 p9 前 expose ≥5 且 p9=左 发报被测向,你拍了三遍警报,坐等门开;码头撤空 「审讯室的灯,也很亮」
e4 长夜 p9 前 expose ≥5 且 p9=右 两点五十九分,脚步停在门口;最后一页密码进了炉子 「耳机里还有一封译不完的电文」
  • 处置令的烈度随暴露档缩放(fiction 自洽):低暴露=处长没有足够把握(约谈/排查);高暴露=今夜三时收网。
  • 尾声回响(P2):ep_lead 后逐封点名——药品(接住/白过江)、名单(少了一个人/多了红圈)、交接(上船/白亮一夜)、处置令(译对/差三个字母)——玩家每一封的译与错都被点名。

5. 数值设计

5.1 变量

变量 类型 初值 说明
expose(暴露度) int 0 步进隐藏资源:译错/危险抉择累积,只涨不跌;档位句播报(§5.3)
hints(密码本余页) int 3 翻一页 −1;T1 首次免费(不扣);全局不补
t1..t4(译文) str null 各封所选候选 id;T1 教学期恒收敛真词;t2/t3/t4 记玩家认知
p2/p4/p6/p8/p9 'L'/'R' null 叙事抉择;派生 med_ok/mateListed/handOk/feed 等 flag
endings{} / runs map/int — 结局收集与重玩计数(存档展示用)

5.2 暴露度累积事件表(单源,账目可验算)

事件 条件 Δexpose 叙事依据(真话)
T1 译错 教学期无限重选,恒收敛 0 学习期零惩罚(A5)
T2/T3/T4 译错 选非真词(无论后续左右) +1/封 你的译文进了纸面(值机日志/呈报/交接条),与对家底档不符
p2 右·压日志 T1 +1 值机日志少一封,晨检对不上号
p4 左·暗处理 T2 +1 卷宗缺页/抽屉藏报,暗手的痕迹
p6 左·亲自去 T3 +2 值班室空岗一刻钟,日志停了一行
p8 左·反喂 T4 +2 拍假电的机器记录抹不掉(哪怕手法伪装了)
p9 左·发报 终局 +2(结局判定用 p9 前值) 开机信号进了值机室的登记
  • 上限:3(译错)+1+1+2+2 = 9(p9 前);下限 0。全对全保守 = 0(calm 档),符合「一晚干净值机」。
  • 每次事件不播数字(隐藏资源);危机感由档位句+氛围音承载;状态链随时可查档位(radio 口碑同型)。

5.3 暴露度档位表(措辞逐值为真)

档 expose 整句 真实性核验
calm 平安 0 「这一晚,还没人看你第二眼。」 0 事件 ✓
low 疑云 1-2 「处长端着茶,多看了你一眼。」 1-2 个纸面破绽=被注意 ✓
mid 被盯 3-4 「走廊里的脚步声,慢了下来。」 3-4 个破绽=例行留意 ✓
hot 危局 ≥5 「门外,好像有人换了岗。」 ≥5=行动级怀疑(与 p9 收网门 ≥5 一致)✓
  • 跨档才播(挂 NIGHT 链尾);上滑状态链随时可查;hot 档各播报位附 warning 震(abyss oxy_crit/G2 同款)。
  • p9 收网门 = hot 档阈值 5:档位句说的就是结局判定用的(radio rep_low/abyss floor(oxy/3) 教训内化——措辞与数值同一个源)。

5.4 密码本账(逐值整句)

余页 整句
3 「密码本,还剩三页。」
2 「密码本,还剩两页。」
1 「密码本,只剩最后一页。」
0 「密码本,一页都不剩了。」

5.5 暴露度与四结局验算(设计期穷举,playtest 剧本引用)

设 wrong = T2/T3/T4 译错封数(0-3),risk = 危险抉择分(p2右1 + p4左1 + p6左2 + p8左2,0-6):

  • p9 前 expose = wrong + risk ∈ [0, 9];结局门:≤4 → e1/e2(p9 左/右);≥5 → e3/e4。
  • 剧本 A(全对全右,p9 左):wrong=0,risk=0 → expose=0(calm)→ p9_known 左 → d9_send_e → e1;ep 全 ok(med_ok/list_no/hand_ok/spy_ok——p4 右呈报=名单进卷宗,ep_list_no 是呈报的真实代价)。
  • 剧本 B(同 A,p9 右):expose=0 → e2(d9_sleep/d9_sleepe)。
  • 剧本 C(全错全左,p9 左):wrong=3,risk=1+2+2=5 → expose=8(hot)→ p9_blind 左 → d9_caught_e(danger 拍)→ e3;逐检查点:NIGHT(1) 后 0(p2 左不加分)→ calm;NIGHT(2) 后 2(wrong T2=1 + p4 左=1)→ low;NIGHT(3) 后 4(+wrong T3 +p6 左 2)→ mid;签收前 8(+wrong T4 +p8 左 2)→ hot。跨档播报 3 次。
  • 剧本 D(同 C,p9 右):expose=8 → e4。
  • 剧本 E(提示流 P4):T1 免费提示(hints 3 不动)+ T2/T3/T4 各 1 页(3→2→1→0,book_2/1/0 逐值播出)→ 提示揭示辨析位 ⟹ 候选唯一 → 全对;全右 → expose=0 → e1/e2 任选。纯提示流可通关 ✓。
  • 剧本 F(慢放流 P3):T2-T4 每封双击×2 慢放后译对,零提示 → 全对;expose=0。纯慢放可通关 ✓。
  • 边界带验算:wrong=2 + risk=2(p2右+p4左)= 4 → 仍 e1/e2(宽容:两处失误不毁局,P4);wrong=1 + risk=4 = 5 → e3/e4(风险路径必热)。

6. 音频三层映射

6.1 BGM(状态→曲目,Mureka instrumental,4 首 ≈ $0.12;共性后缀 10 号 §8 照 radio 追加)

状态 曲目 切换 Mureka prompt 草稿(英文,seamless loop)
INTRO/RX/DECODE/RELAY/MENU bgm/radio_room 开局淡入 800ms;MENU 低响度淡入 500ms;摩尔斯期间 −6dB(R3) "1940s late night wireless station room, warm tube radio hum, distant muffled swing jazz through a wall, rain on window, clock tick, smoky and quiet, no vocals, seamless loop, 60-90 seconds"
RELAY(4) 起(T4 高潮至签收) bgm/closing crossfade 800ms 进 "Tense noir waltz for a spy's last night, muted brushed drums, low strings, subtle telegraph tick rhythm, restrained dread, no vocals, seamless loop, 60-90 seconds"
NIGHT bgm/night crossfade 600ms 进;进下一 RX 淡回 600ms "Quiet night interlude, sparse felt piano over warm vinyl static and soft rain, lonely but safe, no drums, no vocals, seamless loop, 60-90 seconds"
ENDING bgm/ending d9 结果链播完后淡入 1500ms "Dawn after a long night of rain, warm strings and soft piano, exhausted hope, quiet resolution, no vocals, seamless loop, 60-90 seconds"
  • bgm=0 时全部静默(引擎约定);menu 用同曲低响度(省 1 首,值班室同场景同构)。

6.2 SFX 清单(SA3=Stable Audio 3 / SYN=程序合成;one-shot 真峰值 ≤-3dBFS;amb 循环由游戏层 buffer.loop 实现)

id 用途 来源 prompt(英文)/ 合成参数
sfx/tune_seek 调谐找频(每封 RX 起) SA3 "Hand turning a vintage radio tuning dial, static sweeps, faint morse ghost, 3 seconds"
sfx/static_bed 电台静电底噪 loop(RX/DECODE) SA3 "Shortwave radio static bed, soft crackle and hum, seamless loop"
sfx/amb_room 值班室 amb loop(挂钟+雨+暖炉) SA3 "1940s office night ambience, wall clock tick, rain on window, faint stove hum, seamless loop"
sfx/paper_page 密码本翻页(提示兑换/开局发本/页数用尽) SA3 "Thin paper page turn of a small codebook, dry, close"
sfx/key_send 你的电键拍发(p8 左反喂 / p9 左发报) SA3 "Vintage brass telegraph key sending morse, spring clatter and clicks, 4 seconds"
sfx/pen_scratch 处长记事笔(试探戏/呈报) SA3 "Fountain pen scratching on paper, short strokes, 2 seconds"
sfx/tea_cup 茶杯落桌(处长场标记音) SA3 "Tea cup set down on wooden desk, porcelain clink, soft"
sfx/step_halt 门外脚步停住(T4 danger 拍 / hot 档) SA3 "Footsteps in corridor stopping outside a door, heavy boots, tense, 4 seconds"
sfx/lock_click 门锁/枪机(e3/e4 链) SA3 "Door lock slowly turning, metal click, ominous, 2 seconds"
sfx/dawn_bell 黎明远钟(e1/e2 链) SA3 "Distant morning bell over light rain, calm after a long night, 4 seconds"
sfx/ui_confirm / ui_menu 菜单/确认(程序合成兜底) SYN 同全局先例
(摩尔斯) 电文/候选/重听/慢放全部音 SYN 运行时模块 §2.1 时值表;零文件资产
  • 预警窗纪律:T4 danger 拍 step_halt 前置 2s 渐强(L2),danger 签名与音效同窗(radio 刹车/abyss 滑坡先例);e3 链首 danger 签名一次。
  • 承诺绑定:rx4_in 教「快手七的手法你听过」后,p9 左 d9_send 与 p8 左 r4a_feed 必播 key_send(§7.5 清单+断言)。

6.3 Voice 划分(详见 19-cipher-voice-ids.md)

组 声部 落位 数量
报务员(你)OP 双性别(female=Serena / male=Uncle_Fu + OP instruct 常量) voice/female|male/ 20 句
北风电台(上线联络+系统/向导)WIND 双性别(female=Vivian / male=Dylan + WIND instruct 常量) voice/female|male/ 114 句
处长 NPC 平铺单音色(Eric),不做双性别 voice/ 根(平铺回退命中) 5 句
  • 人物账:有台词人物 = 你 + 北风(上线联络人,向导人设 L4,radio 导播同型)+ 处长(明线上级)——上级/联络人 ≤2 约束满足;另有 1 个无台词角色「快手七」(对家报务手,存在=电波里的手法音,abyss 小郑先例)。
  • 合计 139 句(≤150 预算 ✓,含 _k 键盘变体 11 对);音频文件 = 134×2 + 5 = 273 条(×2 格式 = 546 文件)。

7. 向导层接入

7.1 状态签名 earcon / turn cue

时机 向导动作 说明
INTRO 进入 guide.state('explore') 叙事态签名
DECODE 进入(首播后) guide.state('choice') + guide.turn({haptic:true}) 摩尔斯首播完 → turn 签名+confirm 震=「轮到你选词」;dv*_ask 随后落地
每个叙事抉择 p* 起播 guide.state('choice') 选项句本身即 turn cue(W2 契约零插播)
NIGHT 进入 guide.state('settle') 夜结算签名
ENDING 进入 guide.state('settle') 同上
MENU 打开 guide.state('menu') 菜单签名
T4 揭晓 danger 拍 / e3 链首 guide.state('danger') 全游戏仅两处(step_halt 前置 2s 同窗 + danger 震)
无效手势 guide.error({voice:'gd_invalid', onceKey}) error 震 + full 档语音,idle-only(F1.4)
  • earcon 词表:只用全项目统一 8 音中的 7 个(menu/explore/choice/settle/danger/turn/nudge),零新增;battle 不用。

7.2 看门狗双轨(radio F1 + dungeon E0-1 教训:乱按也要被抢救)

无操作轨(guide watchdog,任何手势复位;两级不同句、用尽即停、永不惩罚):

等待点 full 档 standard 档 级别内容
DECODE 浏览无操作 [10, 25] [10, 25](onEscalate 游戏层补播) ① gd_tune1「电文在等。左右滑,听候选词。」② gd_tune2「双击,把电文再听一遍。再双击,会放慢。」(必须不同句)
INTRO/RELAY 链末等待 [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

无进展轨(游戏层计时,仅挂 DECODE 浏览一个可饿死点):

项 规则
进展定义 译词判定落定 / 叙事抉择结算 / 节点推进 / 翻密码本。无效手势与纯浏览(切候选/重听)不计进展——乱滑 96s 也必被抢救
阈值 60s → gd_prog1「光切词没有用。双击,先听电文。」;120s → gd_prog2「别急。停下来,把电文听完整。」;用尽即停(每封一轮)
有真实进展 全程零 prog 播出(不误伤)
双轨去重 两轨短句内容同级交叉登记;同一等待点先播过 gd_tune* 则无进展轨跳过同义级
  • 向导插播一律 queue:true,绝不顶掉在播对白;error/watchdog 类语音 idle-only(在播则跳过且过期丢弃,F1.4)。
  • 单句语音看门狗(资产缺失兜底):30s 超时且 isVoicePlaying()===false 才自动续链,长句续约不掐(radio N1 先例照抄)。

7.3 键盘变体机制(PC 键盘玩家)

  • 运行时按 gesture.inputMode()(settings.input auto/touch/key)选 _k 变体资产:key → voice/<gender>/<id>_k.*,touch → 无后缀版;auto 跟随最近使用通道。_k 缺失回退无后缀版(check_all_assets 校验配对)。
  • 变体清单(11 对,集中分组见 voice-ids §G0.a + M1):gd_brief / gd_continue / gd_again / gd_tune1 / gd_tune2 / sys_help / sys_menu / sys_skip / sys_continue_ask / dv1_teach2(首个候选导航绑定)/ p2(首个叙事抉择绑定)。
  • 抉择提示 p4..p9 措辞统一「向左滑/向右滑」;键盘玩家由 p2_k 与 sys_help_k 完成首次绑定,后续语义不变。启动遮罩文案引擎自理,游戏零改动。

7.4 继承 radio/abyss 三轮验证机制(照抄不重设计)

机制 来源 落点
帮助链去重(同等待点第 2 次上滑只震+nudge) radio G1 _helpSeen 按等待点键控
状态链顺序「状态在前、帮助在后」 radio G6 prog_t → book 档 → exp 档 → sys_help
空点确认轻震(不可跳上单击补 beat [8]) radio G4 p*/摩尔斯首播/链末句各 tap 屏蔽分支
exp_hot 播报位附 warning 震 radio G2 / abyss 全部 hot 出口(含上滑复听)
菜单报名游戏侧排队零截断 radio 实现修复 #3 _menuQueue 排水
stop() 先 seqToken++ 作废在途链 + 停摩尔斯排程 radio 实现修复 #6 同款(摩尔斯句柄一并 stop)
新局清 choiceArmed/pendingChoice/双击慢放计数 radio 实现修复 #7 newNight()
长按误报无效(longpress 全状态合法) radio 实现修复 #2 仅 left/right(ASK 另含 up/down/doubletap)报无效

7.5 教学承诺清单(dungeon「铁器=楼梯」教训:每句承诺必须真实存在且可感知)

教学句 承诺 兑现点(断言锁定)
dv1_teach「先双击,把它重新听一遍」 双击=重听电文 DECODE 全程恒可用(原速)
gd_tune2/dv1_teach「再双击,会放慢」 第 2 次双击=慢放 0.5× 慢放计数器任何封恒在;时值断言 u=160
dv1_teach2「左右滑,听候选词。单击,选定一个」 候选预览/两段确认 全部 DECODE 恒同语义(含确认态取消)
hint_t1「我教你一页:第一个字母,是 B」 T1 提示免费 + 提示给辨析位字母 T1 翻页不扣;hint_* 与 §2.2 辨析位一致
b_rule「规矩就一条:听不清,就再听一遍」 重听/慢放无限免费 全局无重听惩罚;慢放不限次
b_goal「今晚频道里有四封密电」 恒 4 封 剧本 A 恰 4 封走完(断言)
b_chief「四封电文,一封都不许漏」 同上(明线口径) 同一计数
rx4_in「快手七的手法你听过」→r4a_feed「借快手七的手法」 提到发报必闻电键声 p8 左/p9 左必播 key_send(零漏播断言)
dv1_right「练习有答案」(正片无对错反馈) 教学期即时反馈、正片不揭穿 T1 判定出口有反馈句;T2-T4 判定出口零对错语音(断言)

8. 存档设计

8.1 进度 data 结构(PUT /api/progress {game:'cipher'})

{
  "schema": 1,
  "updatedAt": "2026-09-09T12:00:00Z",
  "summary": "第2封·名单 · 暴露:疑云 · 结局1/4",
  "phase": "DECODE",
  "node": "dv2_ask",
  "tel": 2,
  "expose": 2,
  "hints": 2,
  "t": { "t1": "bay", "t2": null, "t3": null, "t4": null },
  "picks": { "p2": "L", "p4": null, "p6": null, "p8": null, "p9": null },
  "endings": { "e2": true },
  "runs": 3
}
  • 写入时机:每封 RX 进入、译词判定落定后、每个抉择结算后、NIGHT 进入、ENDING 进入、菜单「保存并回大厅」。
  • 断点续玩:读档 → sys_continue_ask(_k)(单击=继续上次 / 右滑=新夜班;两者 runs 均 +1,统一口径修 radio 遗留);继续 = 恢复 phase+node+全部数值并局面复述:RELAY 恢复=重播当前节点链;DECODE 恢复=prog_t → book 档 → gd_tune2 → 重挂浏览(自动预听当前候选,电文可双击重听)——一句话 ≤5s 回到可玩(L5);恢复首句受 F4 跳句锁(radio G5 例外条款)。
  • summary 格式:进行中 第N封·<电文名> · 暴露:<档名:平安/疑云/被盯/危局> · 结局X/4;已收台 已收台·<结局名> · 结局X/4(大厅卡片直接显示,人话)。

9. playtest 断言清单(登记 13 号 §22 指针;settings.set({guide:'off'}) 纯玩法验证,向导行为住 guide.test)

关键路径(剧本 A:全对全右+p9 左 → e1,≤60 次手势):

  1. 遮罩点按 → INTRO 链(b_open 起,voice 层有播放记录)→ rx1_in → tune_seek → 摩尔斯固定字符断言:事件序列 == BAY 编码(dah,dit,dit,dit / dit,dah / dit,dah,dah,dah),750Hz,u=80±5ms,BGM 同时段 −6dB(R3)→ dv1_teach → dv1_teach2 → dv1_ask → choiceArmed。
  2. DECODE 浏览:右滑 ×3 循环切换,每次预听 = 候选摩尔斯(620Hz,u=110,与 voice 零重叠——morseGate 断言)→ cand_* 词义句;双击 → 电文原速重播;再双击 → 慢放(u=160,字母隙 6u,750Hz);单击 → cf_confirm + 电文重播 → 单击 → 判定出口 800ms 死窗内注入在途 tap 被吞。
  3. T1 译对:dv1_right(success 震)→ RELAY:p2 左(pendingChoice 播完生效)→ r1a_send/r1a_e → n1 → NIGHT(expose=0 calm 档,零跨档播报)→ RX(2)。
  4. T2:摩尔斯==TOP 断言;译对 → rev_top → p4_known 播出(t2=='top')→ 右 → r2b_act → m2_chief → r2b_e → n2。
  5. T3:摩尔斯==HAND;译对 → rev_hand → p6 右 → r3b_e_ok(t3=='hand')→ n3。
  6. T4:摩尔斯==SPY;译对 → rev_spy(danger 拍断言:step_halt 前置 2s 渐强 + danger 签名 + danger 震)→ m4_chief_a/b → p8_known → 右 → r4b_act → m4_chief_c → r4b_e。
  7. 签收:g9_in → g9_knot → p9_known 左 → d9_send + key_send → pre=0 ≤4 → d9_send_e → ENDING(e1):e1_a/b + ep_lead + ep_med_ok/ep_list_no/ep_hand_ok/ep_spy_ok → 结算单击 → 新夜班回 INTRO(runs+1)。

边界断言:

  • 四结局可达(含暴露度逐点验算):剧本 B(同 A,p9 右)→ e2;剧本 C(T2/T3/T4 全译错 + p4/p6/p8 全左):NIGHT(1) 后 expose=0(calm)/NIGHT(2) 后 2(low,跨档播 exp_low)/NIGHT(3) 后 4(mid)/签收前 8(hot,exp_hot+warning 震)→ p9_blind 左 → d9_caught_e → e3;剧本 D(同 C,p9 右)→ e4(lock_click 链)。各检查点档位句与 §5.3 区间一致。
  • 三层兜底各自可走通:剧本 E(提示流)——T1 翻页免费(hints 仍 3),T2/T3/T4 各翻 1 页(book_2/1/0 逐值播出,逐页 −1 断言),按提示选定全对 → e1;第 4 封后页数 0 再长按 → error 震 + paper_page,无语音、零惩罚;剧本 F(慢放流)——每封双击×2 慢放后译对,零提示,全对(慢放不限次断言)。
  • 摩尔斯正确性:四封电文事件序列逐元素 == §2.2 表(BAY/TOP/HAND/SPY);候选预听 == 所选候选编码;慢放时值 = 原速 2×(容差 ±5ms);settings.speed=2.2 会话下 u 仍 80ms(速度边界断言)。
  • 译错归因真话:T1 错选 DAY/RAY → dv1_wrong 播出且句内字母/码与真词 B 一致 → 无限重选直至对(expose 零变化);剧本 C/D 的 ep_spy_no/ep_hand_no 与 t3/t4 flags 一致;正片判定出口零对错语音(教学承诺)。
  • morseGate:全剧本转写断言——摩尔斯起振时刻与任一 voice 播放区间零重叠(排队语义:晚于在播句结束);候选词义句在候选摩尔斯结束后 ≥500ms 接播。
  • 看门狗双轨:DECODE 快进 10s→gd_tune1、25s→gd_tune2(两级 id ≠,绝不逐字重播;两级后仅静电底噪);乱按 96s(每 2s 注入一次切候选滑动+双击重听):无操作轨零播出(被手势复位)+ 无进展轨 60s→gd_prog1、120s→gd_prog2;有真实进展(译词落定推进)全程零 prog 播出;RELAY 链末 25s/50s → gd_continue/gd_again;p* 播完 25s/50s 重播 p* 两次后静默;guide=off 全程零定时器零向导事件。
  • 键盘变体:settings.input='key' 会话播出 gd_brief_k/sys_help_k/gd_tune1_k/dv1_teach2_k/p2_k 且对应无后缀版零播出;'touch' 反之;'auto':keydown 后下一句用 _k。
  • 中断复述:杀进程重进(存档 phase=DECODE, tel=3, hints=1)→ sys_continue_ask → 单击 → 恢复链 prog_t3 → book_1 → gd_tune2 → 浏览重挂 + 当前候选自动预听(电文双击可重听);恢复首句不可跳,第二句起可跳;右滑=新夜班(runs +1 口径同结算重开)。
  • pendingChoice:p* 播放中左右滑 → 播完立即生效(不丢、不重复、连滑取最后一次)。
  • p 不可跳*:p* 播放中单击 → 不跳句(PROMPT_RE 门)+ beat 轻震(G4)。
  • idle-only:向导 gd_* 在对白在播时跳过且不占 onceKey(F1.4);queue 句排在当前句后零截断。
  • 菜单:下滑开菜单 → 上滑帮助 → 双击「回到电波」→ phase/node 不变、原状态复述;单击循环顺序 = [回到电波, 翻密码本, 重新开始今晚, 保存并回大厅];菜单「翻密码本」确认 = 长按同效(回 DECODE 执行提示流程);确认回大厅 → sys_menu_lobby 播出且 PUT/localStorage 有最新 data。
  • 资产缺失:屏蔽任意 voice 资产 → 30s 看门狗自动续链到结局不卡死;摩尔斯为运行时合成,屏蔽 sfx/bgm 全部不影响电文可听(模块零依赖断言)。
  • 数字规范:prog_/book_/exp_* 全部整句字面量,静态审计无运行时拼接碎片。

失败即不通过:任何等待 >5s 无声(静默死局);无效手势零反馈;摩尔斯字符序列/时值与 §2.2 表不符;摩尔斯与语音重叠;结局计数与 §4.4 不符;档位句与 expose 实值不符;归因假话(dv1_wrong 字母错、ep_* 与 flags 不符)。

10. 节奏曲线(每封电文目标时长)

段 内容 目标时长 设计目标
开场 夜班开场 + 一句规矩 + 处长夜令 1-1.5 min 首输入 <60s(INTRO 链 ~25s 后第一封电文首播,浏览滑动即首输入 ~t+45s);规矩一句讲完(P4)
T1 船期 教学封:两层重听教学 + 免费提示示范 + p2 3.5-4 min 首个正反馈 ≤3min(译对 dv1_right ≈ t+2min);译错即时归因+无限重选零惩罚
T2 名单 known/blind 差分首现 3-3.5 min 「你不知道自己译没译对」的第一次心跳;呼吸口(听辨最易)
T3 交接 最狠听辨(H vs S 差一个 dit) 3.5-4 min 慢放价值兑现段(P3 嘈杂场景实测点);NIGHT(3) 档位句把压力可视化
T4 处置令 高潮:danger 拍 + 处长试探 + 反喂 4-4.5 min 紧张峰值但无倒计时(P4);bgm/closing 交叉淡入
签收+结局 心结 p9 + 发最后一电 + ep 点名 2.5-3 min 整晚的「听」在你自己的电键上闭环(P2 回响);开环钩子拉下一局
  • 合计 ≈ 19-22 min(15-25 达标);每 30-60s 一个反馈颗粒(候选预听/摩尔斯/揭晓句/结果句/档位句),无沉默尴尬期(P3)。
  • 难度曲线 = 听辨精度(教学 B/D → 最易 O/A/I → 最狠 H/S → 中高 P/L)+ 认知重量(练手 → 名单有同伴 → 时间就是命 → 处置令是你),操作复杂度恒定(左右滑+单击+双击,永远如此)——P4「规则一遍听懂」由恒定操作面保证。

11. 自检清单(策划交付自验;☐ = 待开发/测试轮填)

# 硬约束/教训 落地 ✓
1 FTUE 首输入 <60s INTRO 链 ~25s 后第一封首播,~t+45s 首次滑动(§10) ☑
2 FTUE 首个正反馈 ≤3min 译对 dv1_right ≈ t+2min(§10 T1) ☑
3 学习期零惩罚 T1 译错无限重选零资源;DECODE 无超时惩罚;密码本 T1 免费 ☑
4 失败三问归因+归因真话律 dv1_wrong 字母真话;正片不揭穿+ep_* 单源对应;§2.5 无假归因 ☑
5 资源账目真实(radio rep_low 教训) book_1..0 逐值整句;exp 四档措辞逐值为真;档位阈值=收网门(§5.3/5.4) ☑
6 手势宪法 6 手势默认语义,豁免显式登记 §3.3 全覆盖;唯一豁免 E1(长按=密码本,时间互斥 detective E2 同款);菜单 §5.2 模态 ☑
7 长按显式登记 E1 在案(空闲=提示/在播=引擎快进,互斥);其余长按=快进不豁免 ☑
8 向导:签名/turn cue/看门狗两级不同句 §7.1/7.2;gd_tune1≠gd_tune2;gd_continue→gd_again ☑
9 看门狗双轨(dungeon E0-1/radio F1 教训) 无操作轨+无进展轨;纯浏览不计进展,乱按 96s 必被抢救;有进展零打扰 ☑
10 插播 queue 不顶对白;纠错 idle-only 过期丢弃 §7.2(F1.4 契约) ☑
11 死亡/失败结算链完整(dungeon H1 教训) 判定出口/NIGHT/d9/e* 链 800ms 手势死窗;seqToken+停摩尔斯(§3.2/7.4) ☑
12 教学承诺兑现(dungeon「铁器=楼梯」教训) §7.5 九条承诺清单逐条绑兑现点+断言 ☑
13 单次播报 ≤3 选项 译词恒 3 候选逐项预听(≤3 ✓);叙事抉择恒 2;菜单 4 项循环 ☑
14 并发声流 ≤2 + 双通道仲裁 voice 串行+摩尔斯互斥(R1-R5,§3.4);断言零重叠 ☑
15 earcon ≤7、统一词表、语义靠语音 只用统一 8 音中 7 个(battle 不用),零新增 ☑
16 台词一句一文件 ≤40 字、数字分档整句、无运行时拼接 voice-ids 全表 ≤30 字(最长 p8_known);prog/book/exp 分档整句;句内数字汉字写死 ☑
17 多音字规避/同 instruct 常量/无语速词 voice-ids 头部+文末自查(长/处/圈/卷/塞/着 试听项) ☑
18 PC 键盘变体(提示句集中+标注) 11 对 _k 集中分组(voice-ids §G0.a+M1);inputMode() 运行时选择 ☑
19 P1:可打断/重听(含电文)/status key/不当白痴 跳句+双击(电文重听+慢放 R5)+上滑状态链+成功零补救教学 ☑
20 P2:3 分钟见效/结局回响 ~2min 首个「世界因你而动」(药船接应);ep 逐封点名+真词差分 ☑
21 P3:3 分钟颗粒/中断复述/单手/嘈杂 一封=3-4min 完整段落+边界存档;重进一句话复述(含 DECODE 态);慢放无限=嘈杂兜底 ☑
22 P4:一遍懂/容错/无高压倒计时/不骂人 一句规矩;无倒计时无死亡;译错也是剧情;操作面恒定;NPC 句 ≤23 字 ☑
23 抉择树 4 结局可走通验算 §5.5 公式+剧本 A-D 逐检查点(e1/e2: wrong+risk≤4;e3/e4: ≥5) ☑
24 单局 15-25 min §10 曲线 ≈19-22 min ☑
25 分支不爆炸(radio 纪律) 跨封仅标量(expose/hints/t1..t4/picks);~75 节点固定深度(§4.3) ☑
26 台词 ≤150、上级/联络人 ≤2 139 句(双 134+NPC 5)/处长+北风两人(voice-ids 账目) ☑
27 引擎边界诚实 §3.4 能力表:摩尔斯文件方案否决理由/滤波不使用/速度边界写明 ☑
28 存档带 summary、断点续玩 §8:data+summary 格式、DECODE 态恢复、局面复述 ☑

批 1 教训内化确认(5 条全落地)

教训 本作机制 断言
教学承诺必须兑现 §7.5 九条承诺清单(重听/慢放/候选/单击/免费页/四封/电键声/教学反馈边界) §9「摩尔斯正确性」「教学承诺」块
看门狗双轨 §7.2 无操作轨+无进展轨;纯浏览不复位进展轨 §9「看门狗双轨」块
档位措辞真实 §5.3 exp 四档逐值为真且阈值=收网门;§5.4 book 逐值 §9「档位句与实值一致」
失败结算链完整不被打断 800ms 死窗盖判定出口/NIGHT/d9/e*;seqToken+停摩尔斯 §9 关键路径 2 + 死窗断言
PC 键盘操作提示 11 对 _k+inputMode() 选择+集中分组标注 §9「键盘变体」块

本作特有风险预防(摩尔斯听觉门槛)

风险 三层防线 验证
P3 通勤嘈杂听不清长短音 ①双击重听原速(无限)②再双击慢放 0.5×(无限、字母隙加倍)③密码本提示直译辨析位字母(3 页,T1 免费)——三层均可独立走通 §9 剧本 E/F + 三层兜底断言
P1 视障玩家音频编码接受度高,但语速档位边界 摩尔斯不受 settings.speed 影响(u 恒定)——2.2× 档语速下电文仍原速,慢放兜底;边界登记 + live 真人耳测项(转写层测不了时值听感,methodology §5 2026-09 教训:媒体播放类必须真人耳测+引擎级断言) §9「摩尔斯正确性」速度边界断言 + live 轮清单
摩尔斯时值精度 运行时 osc 合成(文件方案否决:有损编码 ms 抖动);事件钩子逐元素断言 §9 固定字符断言
「不知道译对没译错」的认知焦虑 教学期说破「练习有答案,正片靠耳朵」;档位句+ep 回响给最终真相;译错零惩罚只是分支 §9「译错归因真话」

修订记录(开发/测试轮裁定,追加不改旧文)

# 裁定 依据与影响
R1 SFX 全 SYN 兜底:§6.2 原标 SA3 的 10 件(tune_seek/static_bed/amb_room/paper_page/key_send/pen_scratch/tea_cup/step_halt/lock_click/dawn_bell)改程序合成(tools/gen_cipher_syn.py),仅 ui_confirm/ui_menu 按原计划 SYN 与摩尔斯文件方案同型的短音 ms 抖动风险 + 管线一致 + 零成本;one-shot 真峰值 ≤-3dBFS 全过(实测 -3.3~-5.1dBFS),loop 2 件首尾 50ms 环形交叉淡化无缝(seam ≤0.01)。原 SA3 prompt 保留作听感设计参考。
R2 p8_blind / p9_blind 条件笔误:19-cipher-voice-ids.md 表内「条件 t4≠top」实为 t4≠spy §4.2 抉择树(p8/p9 由 T4 译文 known/blind 差分)与 §9 断言均为 t4 维度;top 是 T2 的真词,显系复制笔误。实现按 t4==='spy' 选播。
R3 §5.5 剧本 C 检查点算术以 §5.2 事件单源表为准:NIGHT(3) 后 expose=**5(hot 档)**而非 4/mid;签收前 8 ✓ 单源表:T2-T4 译错各 +1、p6 左 +2。2+1+2=5 恰好达收网门(≥5),「跨档播报 3 次」表述作废,实为 2 次(calm→low 于 NIGHT(2)、low→hot 于 NIGHT(3),签收前 8 与 5 同档不重复播)。§9 断言按修正值实现(hot 档提前一夜可视化压力,戏剧效果不降反升)。
R4 §5.5 剧本 A「全对全右」为措辞笔误:操作序以 §9 关键路径为准(p2 左 · p4 右 · p6 右 · p8 右 · p9 左) §5.5 自身给出的 risk=0 与 ep 点名组合(med_ok/list_no/hand_ok/spy_ok)只有该路径自洽;「全右」若按字面 p2 右则 risk=1、ep_med_no,矛盾。验算 expose=0 → e1 不受影响。
R5 菜单低响度实现路径:§6.1「menu 用同曲低响度」经游戏层 _bgmBus 增益 0.4(与摩尔斯 −6dB dip 乘法复合) 引擎约定「同一首重复 play 不重启」,play(opts.volume) 无法作用于在播 BGM;省一首成立(无 menu 专属曲目)。
R6 d9_sleep 自动链(radio d9_face 同型) 关机收束节奏与发报线对齐;p9 右的收束不因链末等待拖沓。
R7 transcribe.mjs CLASS_NAME 追加 cipher 行(CipherGame) 共享 harness 接线(check_all_assets 之外唯一跨游戏触碰点),hunt/teahouse 等泳道同款需求各自追加。

| R8 | R2 修复三件套(round1 synthesis F1-F3):① NIGHT 链首按上一封抉择垫纸面标记音(p2右/p4左→paper_page,p4右→pen_scratch,现有 sfx 零新增)——「世界因你而动」可感知化;② book_1(密码本只剩最后一页)各播报位到句附 warning 震(F2);③ 结算链尾新增结局计数分档整句 end_c1..end_c4(F3,+4 句 WIND,见 19b R2 增补) | 均不改树形/不加抉择点/不增人设;断言入 §9 清单(F1 纸面音有/无双向断言、F2 warning 震、F3 计数句)。 |