Files
xsl 866f363591 项目整理:重写主文档、归档过时文档、清理死代码、新增音频制作手册
文档整理:
- 重写 game/操作说明.md 为项目主文档(6游戏/6手势/旁白/模型生成音频/TTS两步流程)
- 归档过时文档至 other_docs/(林夏原始设计、旧执行文档)
- 新增 docs/音频制作执行手册.md(从零制作游戏音频的完整流程)
- docs/ 下保留旁白音色提示词、音频脚本

代码清理:
- 删除旧架构死代码: story.js / gestures.js / profile.js
- 删除耦合旧架构的过时测试目录 game/tests/

脚本配置修正:
- check_tts_env.sh: 启动脚本路径 AutoVideo → tts-server(实际位置)
- gen_narrator_samples.py: 硬编码地址改为环境变量,默认对齐 tts-server 端口 8000
2026-06-19 22:29:04 +08:00

186 lines
8.0 KiB
JavaScript

// games/yinian.js — 《一念》
// 内心法庭。七个声音,一个决定。
// 单击=接纳这个声音,双击=让它走。
// 结局由接纳了哪些声音决定。
const GAME_YINIAN = {
id: 'yinian',
intro: '你坐在黑暗里,独自一人。今晚你必须做一个决定——你自己知道是什么。七个声音在等着和你说话。单击,让它说完。双击,让它走。',
first: 'v01',
bgm: null,
ambience: '../audio/mp3/ambience/amb_apartment_night_01_v1.mp3',
initialState: {
accepted: [], // 接纳的声音
rejected: [], // 放走的声音
// 关键标记
kept_fear: false,
kept_longing: false,
kept_apology: false,
kept_past: false,
},
scenes: {
// ── 第一个声音:三年前的自己 ──
v01: {
delay: 1000,
narrate: '第一个声音来了。是三年前的你。那时候你刚做了一个选择,你觉得那是对的。那时候的你说:我们当时想要的,你还记得吗?',
tap: {
label: '听它说完',
narrate: '你让三年前的自己说完了。她说,你当时很想要那件事。你知道那是真的。',
state: s => ({ accepted: [...s.accepted, '三年前的自己'], rejected: s.rejected, kept_past: true }),
next: 'v02',
},
doubleTap: {
label: '让它走',
narrate: '三年前的声音淡去了。你选择了不看它。',
state: s => ({ accepted: s.accepted, rejected: [...s.rejected, '三年前的自己'], kept_past: false }),
next: 'v02',
},
},
// ── 第二个声音:一个老朋友的留言 ──
v02: {
delay: 1000,
narrate: '第二个声音。是一个你很久没联系的朋友,她留了条语音。她说:你最近怎么样,我有点担心你。很久了,你一直没回。',
tap: {
label: '听下去',
narrate: '你把这份担心收进来了。她还在关心你,这件事是真实的。',
state: s => ({ accepted: [...s.accepted, '朋友的担心'], rejected: s.rejected }),
next: 'v03',
},
doubleTap: {
label: '让它走',
narrate: '留言消失了。你没有听完。',
state: s => ({ accepted: s.accepted, rejected: [...s.rejected, '朋友的担心'] }),
next: 'v03',
},
},
// ── 第三个声音:关于钱的恐惧 ──
v03: {
delay: 1000,
narrate: '第三个声音。是关于钱的恐惧。它说:你知道你现在能撑多久吗?如果这件事不成,你怎么办?它问得很具体,很冷静。',
tap: {
label: '正视它',
narrate: '你承认了:这份恐惧是真实的。你带着它,而不是假装它不存在。',
state: s => ({ accepted: [...s.accepted, '对钱的恐惧'], rejected: s.rejected, kept_fear: true }),
next: 'v04',
},
doubleTap: {
label: '先不管它',
narrate: '恐惧的声音淡下去了。你知道它还在,但今晚先不看。',
state: s => ({ accepted: s.accepted, rejected: [...s.rejected, '对钱的恐惧'], kept_fear: false }),
next: 'v04',
},
},
// ── 第四个声音:关于孤独的渴望 ──
v04: {
delay: 1000,
narrate: '第四个声音。是一种渴望——想要被人理解,想要有人在身边,想要不那么孤独。它来得轻,但是很深。',
tap: {
label: '承认它',
narrate: '你承认了这份渴望。它是真实的,不是软弱。',
state: s => ({ accepted: [...s.accepted, '对连接的渴望'], rejected: s.rejected, kept_longing: true }),
next: 'v05',
},
doubleTap: {
label: '让它走',
narrate: '渴望漂走了。你告诉自己,一个人也没什么。',
state: s => ({ accepted: s.accepted, rejected: [...s.rejected, '对连接的渴望'], kept_longing: false }),
next: 'v05',
},
},
// ── 第五个声音:一个未说出口的道歉 ──
v05: {
delay: 1000,
narrate: '第五个声音。是一个你一直没有说出口的道歉。你知道是对谁的,你知道说的是什么。这个声音只是在等你。',
tap: {
label: '说出来',
narrate: '你在黑暗里,说了那句对不起。没有人听见,但你说了。有什么东西松开了。',
state: s => ({ accepted: [...s.accepted, '那声道歉'], rejected: s.rejected, kept_apology: true }),
next: 'v06',
},
doubleTap: {
label: '还没准备好',
narrate: '道歉的声音还在那里。你没有说。它等着。',
state: s => ({ accepted: s.accepted, rejected: [...s.rejected, '那声道歉'], kept_apology: false }),
next: 'v06',
},
},
// ── 第六个声音:关于那个决定 ──
v06: {
delay: 1000,
narrate: '第六个声音。是今晚那个决定本身。它把两种可能都摆在你面前,不说话,只是等着你看清楚。',
tap: {
label: '看清楚它',
narrate: '你看了。两条路都有代价,你知道了。这不让选择变得更容易,但让它变得更真实。',
state: s => ({ accepted: [...s.accepted, '那个决定本身'], rejected: s.rejected }),
next: 'v07',
},
doubleTap: {
label: '还不想看',
narrate: '你把眼睛闭上了。还没准备好看那两条路。',
state: s => ({ accepted: s.accepted, rejected: [...s.rejected, '那个决定本身'] }),
next: 'v07',
},
},
// ── 第七个声音:现在的自己 ──
v07: {
delay: 2000,
narrate: s => {
const n = s.accepted.length;
if (n >= 5)
return '第七个声音,是现在的你。她说:你今晚接住了很多,你没有回避它们。这需要力气。';
if (n <= 1)
return '第七个声音,是现在的你。她沉默了很久,然后说:你把它们都推走了。也许你需要的不是答案,是喘息。';
if (s.kept_fear && !s.kept_longing)
return '第七个声音,是现在的你。她说:你把恐惧留下了,但把渴望推走了。你知道这意味着什么吗?';
return '第七个声音,是现在的你。她说:你接住了一些,放走了一些。这就是你今晚的状态。';
},
tap: {
label: '听下去',
narrate: '你听完了现在的自己说话。她说:不管你今晚做什么决定,你都已经在这里待过了。这算数。',
state: s => ({ accepted: [...s.accepted, '现在的自己'], rejected: s.rejected }),
next: null,
},
doubleTap: {
label: '就这样',
narrate: '你没有听完。你觉得你已经知道了你需要知道的。',
state: s => ({ accepted: s.accepted, rejected: [...s.rejected, '现在的自己'] }),
next: null,
},
},
},
getEnding: s => {
if (s.kept_apology) return 'reconcile';
const n = s.accepted.length;
if (n >= 5) return 'accept';
if (n <= 1) return 'release';
if (s.kept_fear && !s.kept_longing) return 'stuck';
return 'between';
},
endings: {
accept: {
narrate: '《接纳》。你今晚接住了很多,你没有选择只是轻装。也许你的决定,就是带着这些往前走。',
},
release: {
narrate: '《出发》。你今晚把大部分都放走了。你想要的也许是轻。轻装,也是一种勇气。',
},
stuck: {
narrate: '《困住》。你接受了恐惧,但推走了渴望。你知道风险,但忘了你想要的是什么。这个决定,也许需要再等一等。',
},
reconcile: {
narrate: '《和解》。你说出了那声道歉。不管今晚的决定是什么,你和自己和解了一点点。这很重要。',
},
between: {
narrate: '《悬在中间》。你接住了一些,放走了一些。你还悬在两个答案之间。也许这就是你今晚真实的状态。',
},
},
};