项目整理:重写主文档、归档过时文档、清理死代码、新增音频制作手册
文档整理: - 重写 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
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
// games/fu.js — 《浮》
|
||||
// 感官漂流。十二个声音漂过来,抓住还是放走。
|
||||
// 结局:旁白读出你带走了什么。
|
||||
|
||||
const GAME_FU = {
|
||||
id: 'fu',
|
||||
intro: '你漂浮在某处。不知道是水,还是梦,还是别的什么。声音会漂过来。单击,把手伸过去。双击,看着它走。',
|
||||
first: 'f01',
|
||||
bgm: null,
|
||||
ambience: '../audio/mp3/ambience/amb_apartment_night_01_v1.mp3',
|
||||
initialState: { kept: [], released: [] },
|
||||
|
||||
scenes: {
|
||||
f01: {
|
||||
delay: 2000,
|
||||
narrate: '第一个。像夏天傍晚的风,带着草的气味,你说不清是哪年夏天的。它停在你身边了。',
|
||||
tap: { label: '把它留住', narrate: '它停在你手心里,有点暖。',
|
||||
state: s => ({ kept: [...s.kept, '夏天的风'], released: s.released }), next: 'f02' },
|
||||
doubleTap:{ label: '让它走', narrate: '你看着它慢慢飘走了。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '夏天的风'] }), next: 'f02' },
|
||||
},
|
||||
f02: {
|
||||
delay: 1500,
|
||||
narrate: '第二个。某人的笑声。你认识这个笑,但你说不清是谁的。',
|
||||
tap: { label: '留住它', narrate: '笑声停在你耳边,还在回响。',
|
||||
state: s => ({ kept: [...s.kept, '笑声'], released: s.released }), next: 'f03' },
|
||||
doubleTap:{ label: '让它走', narrate: '笑声远了。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '笑声'] }), next: 'f03' },
|
||||
},
|
||||
f03: {
|
||||
delay: 1500,
|
||||
narrate: '第三个。一种说不清楚的遗憾。它没有形状,但是很重。',
|
||||
tap: { label: '接住它', narrate: '你把遗憾抱在怀里了。它很重,但你没有放开。',
|
||||
state: s => ({ kept: [...s.kept, '遗憾'], released: s.released }), next: 'f04' },
|
||||
doubleTap:{ label: '让它过去', narrate: '遗憾漂过去了。你呼了口气。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '遗憾'] }), next: 'f04' },
|
||||
},
|
||||
f04: {
|
||||
delay: 1500,
|
||||
narrate: '第四个。很久没有过的安全感。轻轻的,像一床旧被子的气味。',
|
||||
tap: { label: '留住它', narrate: '你把它裹在身上了。',
|
||||
state: s => ({ kept: [...s.kept, '安全感'], released: s.released }), next: 'f05' },
|
||||
doubleTap:{ label: '放走', narrate: '安全感飘走了。你看着它走。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '安全感'] }), next: 'f05' },
|
||||
},
|
||||
f05: {
|
||||
delay: 1500,
|
||||
narrate: '第五个。想哭但没哭出来的那种感觉。像喉咙里卡着什么。',
|
||||
tap: { label: '承认它', narrate: '你承认了。那个东西松动了一点点。',
|
||||
state: s => ({ kept: [...s.kept, '没哭出来的眼泪'], released: s.released }), next: 'f06' },
|
||||
doubleTap:{ label: '让它走', narrate: '它漂走了。喉咙还是有点紧。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '没哭出来的眼泪'] }), next: 'f06' },
|
||||
},
|
||||
f06: {
|
||||
delay: 1500,
|
||||
narrate: '第六个。一首旋律,只有开头几个音,后面的你想不起来了。',
|
||||
tap: { label: '哼几下', narrate: '你在心里哼了那几个音。后面的还是想不起来,但这几个音够了。',
|
||||
state: s => ({ kept: [...s.kept, '那段旋律'], released: s.released }), next: 'f07' },
|
||||
doubleTap:{ label: '算了', narrate: '旋律消失了。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '那段旋律'] }), next: 'f07' },
|
||||
},
|
||||
f07: {
|
||||
delay: 1500,
|
||||
narrate: '第七个。被需要的感觉。有人曾对你说过你很重要,你还记得那句话。',
|
||||
tap: { label: '带着它', narrate: '你把那句话放进口袋里了。',
|
||||
state: s => ({ kept: [...s.kept, '被需要的感觉'], released: s.released }), next: 'f08' },
|
||||
doubleTap:{ label: '放开', narrate: '那句话漂走了。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '被需要的感觉'] }), next: 'f08' },
|
||||
},
|
||||
f08: {
|
||||
delay: 1500,
|
||||
narrate: '第八个。一个人的时候的自在。没人看着,没人要你做什么,只是你自己。',
|
||||
tap: { label: '收下', narrate: '你把这份自在收进来了。',
|
||||
state: s => ({ kept: [...s.kept, '独处的自在'], released: s.released }), next: 'f09' },
|
||||
doubleTap:{ label: '不需要', narrate: '它漂走了。也许你现在不需要。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '独处的自在'] }), next: 'f09' },
|
||||
},
|
||||
f09: {
|
||||
delay: 1500,
|
||||
narrate: '第九个。害怕。不知道怕什么,就是有点怕。',
|
||||
tap: { label: '承认害怕', narrate: '你承认了:我有点害怕。说出来之后,它小了一点。',
|
||||
state: s => ({ kept: [...s.kept, '害怕'], released: s.released }), next: 'f10' },
|
||||
doubleTap:{ label: '不理它', narrate: '你没有看它,害怕漂走了。也许。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '害怕'] }), next: 'f10' },
|
||||
},
|
||||
f10: {
|
||||
delay: 1500,
|
||||
narrate: '第十个。好奇心。对某件事、某个人、某个地方,你还有好奇。',
|
||||
tap: { label: '带上它', narrate: '好奇心跳进你手里了,很活跃。',
|
||||
state: s => ({ kept: [...s.kept, '好奇心'], released: s.released }), next: 'f11' },
|
||||
doubleTap:{ label: '放走', narrate: '好奇心漂走了,也许它去找别人了。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '好奇心'] }), next: 'f11' },
|
||||
},
|
||||
f11: {
|
||||
delay: 1500,
|
||||
narrate: '第十一个。疲惫。真实的、身体里的疲惫,它已经跟了你很久了。',
|
||||
tap: { label: '承认疲惫', narrate: '你说:我累了。疲惫点了点头,说我知道。',
|
||||
state: s => ({ kept: [...s.kept, '疲惫'], released: s.released }), next: 'f12' },
|
||||
doubleTap:{ label: '让它走', narrate: '疲惫漂走了。你感觉稍微轻了一点点。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '疲惫'] }), next: 'f12' },
|
||||
},
|
||||
f12: {
|
||||
delay: 2000,
|
||||
narrate: '最后一个。它没有名字。是你漂浮在这里时特有的某种感觉,说不清,但它在。',
|
||||
tap: { label: '带走它', narrate: '你把它揣在怀里了,连同它的没有名字。',
|
||||
state: s => ({ kept: [...s.kept, '无名之物'], released: s.released }), next: null },
|
||||
doubleTap:{ label: '留在这里', narrate: '你看着它留在了这片水里,这片梦里。',
|
||||
state: s => ({ kept: s.kept, released: [...s.released, '无名之物'] }), next: null },
|
||||
},
|
||||
},
|
||||
|
||||
getEnding: () => 'only',
|
||||
|
||||
endings: {
|
||||
only: {
|
||||
narrate: s => {
|
||||
let t = '你漂回来了。';
|
||||
if (s.kept.length > 0) t += `你带走了:${s.kept.join('、')}。`;
|
||||
if (s.released.length > 0) t += `你放走了:${s.released.join('、')}。`;
|
||||
t += '这就是今天的你。';
|
||||
return t;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,206 @@
|
||||
// games/houzhen.js — 《候诊》
|
||||
// 等待检查结果的两小时。
|
||||
// 单击=把注意力放在外面,双击=退回到自己里面。
|
||||
// 没有好坏结局,只有你等待的方式。
|
||||
|
||||
const GAME_HOUZHEN = {
|
||||
id: 'houzhen',
|
||||
intro: '下午一点半,你坐在医院候诊室,等一个检查结果。叫号机已经叫到了八十四号。你是九十四号。单击,把注意力放到外面。双击,退回到自己里面。',
|
||||
first: 'h01',
|
||||
bgm: null,
|
||||
ambience: null,
|
||||
initialState: { out: 0, inside: 0, moments: [] },
|
||||
|
||||
scenes: {
|
||||
// 1. 建立等待感(无选择)
|
||||
h01: {
|
||||
delay: 3000,
|
||||
narrate: '叫号机叫到了八十五号。你的号是九十四。等待的时间被切成了一小块一小块的,每一块都很结实。',
|
||||
next: 'h02',
|
||||
},
|
||||
|
||||
// 2. 左边的老人
|
||||
h02: {
|
||||
delay: 1000,
|
||||
narrate: '你左边坐着一个老人。他在看窗外,窗外是停车场,没有什么特别的。但他看得很专注。',
|
||||
tap: {
|
||||
label: '看看他',
|
||||
narrate: '你看了他一会儿。他感觉到了,朝你点了点头。你也点了点头。没有说话,但有什么东西发生了。',
|
||||
state: s => ({ out: s.out + 1, inside: s.inside, moments: [...s.moments, '和老人点头'] }),
|
||||
next: 'h03_wait',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '低头看手机',
|
||||
narrate: '你低下头,看手机。屏幕亮着,什么都有,但你没有真的看进去什么。',
|
||||
state: s => ({ out: s.out, inside: s.inside + 1, moments: s.moments }),
|
||||
next: 'h03_wait',
|
||||
},
|
||||
},
|
||||
|
||||
h03_wait: {
|
||||
delay: 5000,
|
||||
narrate: '叫号机叫到了八十八号。',
|
||||
next: 'h03',
|
||||
},
|
||||
|
||||
// 3. 一条旧消息
|
||||
h03: {
|
||||
delay: 1000,
|
||||
narrate: '你手机上有一条两天前的消息,你当时看到了,但没有回。那个人说:最近怎么样。',
|
||||
tap: {
|
||||
label: '打开来回复',
|
||||
narrate: '你打开来,打了几个字。发出去了。不知道他会不会在意这么晚才回。',
|
||||
state: s => ({ out: s.out + 1, inside: s.inside, moments: [...s.moments, '回了两天前的消息'] }),
|
||||
next: 'h04_wait',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '先不管它',
|
||||
narrate: '你把手机翻过去,屏幕朝下放在大腿上。等等再说。',
|
||||
state: s => ({ out: s.out, inside: s.inside + 1, moments: s.moments }),
|
||||
next: 'h04_wait',
|
||||
},
|
||||
},
|
||||
|
||||
h04_wait: {
|
||||
delay: 4000,
|
||||
narrate: '护士从走廊那头走过来,叫了一个名字,不是你。',
|
||||
next: 'h04',
|
||||
},
|
||||
|
||||
// 4. 一个孩子在哭
|
||||
h04: {
|
||||
delay: 1000,
|
||||
narrate: '对面有个小孩在哭,大约三四岁,蹲在椅子旁边哭。旁边的大人在轻声说什么,但他还是哭。',
|
||||
tap: {
|
||||
label: '看过去',
|
||||
narrate: '你看着那个孩子。他哭了一会儿就停了,大概是哭够了。你们没有交流,但你陪着他哭完了。',
|
||||
state: s => ({ out: s.out + 1, inside: s.inside, moments: [...s.moments, '陪孩子哭完了'] }),
|
||||
next: 'h05_wait',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '看别处',
|
||||
narrate: '你把视线移开了。每个人都有自己的事,你也有你的。',
|
||||
state: s => ({ out: s.out, inside: s.inside + 1, moments: s.moments }),
|
||||
next: 'h05_wait',
|
||||
},
|
||||
},
|
||||
|
||||
h05_wait: {
|
||||
delay: 6000,
|
||||
narrate: '叫号机叫到了九十号。还有四个人。',
|
||||
next: 'h05',
|
||||
},
|
||||
|
||||
// 5. 窗外的光线
|
||||
h05: {
|
||||
delay: 1000,
|
||||
narrate: '窗外的光线变了。下午的阳光斜进来,在地板上画了一块长方形的亮。你注意到了。',
|
||||
tap: {
|
||||
label: '看着那块光',
|
||||
narrate: '你盯着那块光看了一会儿。它在慢慢移动,非常慢,但是在动。时间在过。',
|
||||
state: s => ({ out: s.out + 1, inside: s.inside, moments: [...s.moments, '看着地板上的光'] }),
|
||||
next: 'h06_wait',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '没注意',
|
||||
narrate: '你闭上眼睛,想了想别的事。',
|
||||
state: s => ({ out: s.out, inside: s.inside + 1, moments: s.moments }),
|
||||
next: 'h06_wait',
|
||||
},
|
||||
},
|
||||
|
||||
h06_wait: {
|
||||
delay: 4000,
|
||||
narrate: '叫号机叫到了九十一号。',
|
||||
next: 'h06',
|
||||
},
|
||||
|
||||
// 6. 电话响了
|
||||
h06: {
|
||||
delay: 1000,
|
||||
narrate: '你的手机响了。是一个你不常打电话的人——是你家里的人。也许只是普通的问候。',
|
||||
tap: {
|
||||
label: '接了',
|
||||
narrate: '你接了。对方说:你在哪啊,吃了吗。你说在外面,没事。就这样。挂掉之后候诊室里还是那么安静。',
|
||||
state: s => ({ out: s.out + 1, inside: s.inside, moments: [...s.moments, '接了家人的电话'] }),
|
||||
next: 'h07_wait',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '没接',
|
||||
narrate: '你看着屏幕亮了又暗。现在不是时候。等会儿再说。',
|
||||
state: s => ({ out: s.out, inside: s.inside + 1, moments: s.moments }),
|
||||
next: 'h07_wait',
|
||||
},
|
||||
},
|
||||
|
||||
h07_wait: {
|
||||
delay: 5000,
|
||||
narrate: '叫号机叫到了九十二号。',
|
||||
next: 'h07',
|
||||
},
|
||||
|
||||
// 7. 右边的陌生人
|
||||
h07: {
|
||||
delay: 1000,
|
||||
narrate: '你右边的人一直在看同一页的杂志,翻来翻去都是那页。你不确定她在看,还是只是拿着。',
|
||||
tap: {
|
||||
label: '看过去',
|
||||
narrate: '你朝她看了一眼。你们的眼神碰了一下,然后都移开了。没有什么,就是两个陌生人在等待。',
|
||||
state: s => ({ out: s.out + 1, inside: s.inside, moments: [...s.moments, '和陌生人的眼神'] }),
|
||||
next: 'h08',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不看',
|
||||
narrate: '你没有看。你在想等会儿检查结果会说什么。',
|
||||
state: s => ({ out: s.out, inside: s.inside + 1, moments: s.moments }),
|
||||
next: 'h08',
|
||||
},
|
||||
},
|
||||
|
||||
// 8. 叫号推进(自动)
|
||||
h08: {
|
||||
delay: 4000,
|
||||
narrate: '九十三号。就差一个了。',
|
||||
next: 'h09',
|
||||
},
|
||||
|
||||
// 9. 叫到你了
|
||||
h09: {
|
||||
delay: 2000,
|
||||
narrate: '九十四号。是你。你站起来,整理了一下衣服,走向那扇门。',
|
||||
tap: {
|
||||
label: '走进去',
|
||||
narrate: '你推开门走进去了。门关上了。等待结束了。',
|
||||
state: s => s,
|
||||
next: null,
|
||||
},
|
||||
doubleTap: {
|
||||
label: '再等一下',
|
||||
narrate: '你停了一秒,深呼吸,然后走进去了。门关上了。',
|
||||
state: s => s,
|
||||
next: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
getEnding: () => 'summary',
|
||||
|
||||
endings: {
|
||||
summary: {
|
||||
narrate: s => {
|
||||
const total = s.out + s.inside;
|
||||
let t = `你等了很久。`;
|
||||
if (s.out > s.inside)
|
||||
t += `在这段时间里,你向外走了${s.out}次——看向别人,接了电话,留意窗外。`;
|
||||
else if (s.inside > s.out)
|
||||
t += `在这段时间里,你向内走了${s.inside}次——退回到自己里,先不管别的。`;
|
||||
else
|
||||
t += `你向外走了${s.out}次,也向内走了${s.inside}次,差不多一半一半。`;
|
||||
if (s.moments.length > 0)
|
||||
t += `你记住了:${s.moments.join(',')}。`;
|
||||
t += '这就是你今天等待的方式。';
|
||||
return t;
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,556 @@
|
||||
// games/linxia.js — 《林夏》(重构版)
|
||||
// 一部碎屏手机,一个北漂的夜晚,16条消息。
|
||||
// 操作:单击=接听/打开/回复,双击=拒接/忽略/不回。
|
||||
// 旁白全程解释状态,玩家不看屏幕也能明白自己在哪里。
|
||||
|
||||
const _T = '../audio/mp3/tts';
|
||||
const _MUS = '../audio/mp3/music';
|
||||
const _AMB = '../audio/mp3/ambience';
|
||||
const _SFX = '../audio/mp3/sfx';
|
||||
|
||||
const GAME_LINXIA = {
|
||||
id: 'linxia',
|
||||
intro: '今晚九点。你是林夏,二十四岁,在北京独居。手机屏幕碎了,但还能用。今晚有十六条消息,你来决定接谁,不接谁。单击接听或回复,双击拒接或忽略。',
|
||||
first: 'msg_01',
|
||||
bgm: `${_MUS}/lv11_bgm_m1_main_loop_v1.mp3`,
|
||||
ambience: `${_AMB}/amb_apartment_night_01_v1.mp3`,
|
||||
|
||||
initialState: {
|
||||
mom_link: 0, // 与妈妈的连接深度 0-3
|
||||
he_back: null, // 阿哲是否回来 null/true/false
|
||||
zhounan: 0, // 与周南互动深度 0-2
|
||||
self_heard: false, // 是否听了时光胶囊
|
||||
engage: 0, // 主动回应次数
|
||||
avoid: 0, // 回避次数
|
||||
},
|
||||
|
||||
scenes: {
|
||||
|
||||
// ══════════════════
|
||||
// 01 · 妈妈来电
|
||||
// ══════════════════
|
||||
msg_01: {
|
||||
narrate: '晚上九点零三分。手机震动,铃声响起。屏幕显示:妈妈。',
|
||||
tap: {
|
||||
label: '接听',
|
||||
audio: [`${_T}/lv11_2103_mom_call_01_wav_v1.mp3`],
|
||||
narrate: '你接了。妈妈的声音从电话里传来,带着一点日常的关心,问你今天怎么样,吃了没有。你说还好。挂掉之后,房间里又安静了。',
|
||||
state: s => ({ mom_link: s.mom_link + 1, engage: s.engage + 1 }),
|
||||
next: 'msg_02_notify',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不接',
|
||||
narrate: '铃声响了又停。妈妈没再打来。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_02_notify',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 02 · 小美语音×3(教学关)
|
||||
// ══════════════════
|
||||
msg_02_notify: {
|
||||
delay: 1500,
|
||||
narrate: '九点一刻。微信震了两下。小美给你发来三条语音消息。',
|
||||
tap: {
|
||||
label: '打开来听',
|
||||
state: s => s,
|
||||
next: 'msg_02_play',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '先不看',
|
||||
narrate: '你把手机屏幕朝下放着。消息还在那里,但你不想听。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_03',
|
||||
},
|
||||
},
|
||||
msg_02_play: {
|
||||
audio: [
|
||||
`${_T}/lv11_2115_xiaomei_voice_01_wav_v1.mp3`,
|
||||
`${_T}/lv11_2115_xiaomei_voice_02_wav_v1.mp3`,
|
||||
`${_T}/lv11_2115_xiaomei_voice_03_wav_v1.mp3`,
|
||||
],
|
||||
narrate: '小美说了很多,大意是关于上次那件事,她一直想跟你说清楚,她说她只是太担心你了。最后她问:你最近还好吗?',
|
||||
tap: {
|
||||
label: '回复她',
|
||||
narrate: '你回了消息。小美很快回说:谢谢你愿意回我。',
|
||||
state: s => ({ engage: s.engage + 1 }),
|
||||
next: 'msg_03',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不回了',
|
||||
narrate: '你听完了,但没有回。消息就这样放着。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_03',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 03 · 外卖小哥来电
|
||||
// ══════════════════
|
||||
msg_03: {
|
||||
delay: 2000,
|
||||
narrate: '九点二十分。来电话了,是一个陌生号码——是外卖小哥,说你的外卖到了,问你在哪层。',
|
||||
tap: {
|
||||
label: '接听',
|
||||
audio: [`${_T}/lv11_2120_delivery_call_01_wav_v1.mp3`],
|
||||
narrate: '你接了,告诉他楼层,外卖放门口就好。过了一会儿,手机震动,门铃响了,外卖到了。',
|
||||
state: s => ({ engage: s.engage + 1 }),
|
||||
next: 'msg_04_notify',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不接',
|
||||
narrate: '你没接。不知道外卖最后怎么样了。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_04_notify',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 04 · 阿哲语音
|
||||
// ══════════════════
|
||||
msg_04_notify: {
|
||||
delay: 2500,
|
||||
narrate: '九点半。微信震了一下。阿哲——你的前男友——给你发来一条语音。',
|
||||
tap: {
|
||||
label: '听',
|
||||
state: s => s,
|
||||
next: 'msg_04_play',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不看',
|
||||
narrate: '你没有打开。阿哲的消息放在那里。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_05_notify',
|
||||
},
|
||||
},
|
||||
msg_04_play: {
|
||||
audio: [`${_T}/lv11_2130_azhe_voice_01_wav_v1.mp3`],
|
||||
narrate: '阿哲说,他有一件东西落在你这里了,问你方便不方便他过来拿一下。语气很平,不像是有别的意思。',
|
||||
tap: {
|
||||
label: '回他',
|
||||
narrate: '你回了条消息说可以。阿哲说谢谢,说到时候再联系。你放下手机,有点说不清的感觉。',
|
||||
state: s => ({ engage: s.engage + 1 }),
|
||||
next: 'msg_05_notify',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不回',
|
||||
narrate: '你没有回。消息就这样。',
|
||||
state: s => ({ avoid: s.avoid + 1, he_back: s.he_back === null ? false : s.he_back }),
|
||||
next: 'msg_05_notify',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 05 · HR王姐语音
|
||||
// ══════════════════
|
||||
msg_05_notify: {
|
||||
delay: 2000,
|
||||
narrate: '九点四十五分。HR王姐发来语音,是关于下个月调岗的事,问你有没有空聊一聊。',
|
||||
tap: {
|
||||
label: '听',
|
||||
state: s => s,
|
||||
next: 'msg_05_play',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '明天再说',
|
||||
narrate: '你把消息标记了已读,回头再说。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_06_notify',
|
||||
},
|
||||
},
|
||||
msg_05_play: {
|
||||
audio: [`${_T}/lv11_2145_hr_voice_01_wav_v1.mp3`],
|
||||
narrate: '王姐说有个岗位的变动,你可能会受到影响,想提前跟你说一声,让你有个准备。语气是友善的,像是帮你。',
|
||||
tap: {
|
||||
label: '回她',
|
||||
narrate: '你回了消息说谢谢告知,改天约时间聊。王姐说好的。',
|
||||
state: s => ({ engage: s.engage + 1 }),
|
||||
next: 'msg_06_notify',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不回',
|
||||
narrate: '你没有回。这件事先放一放。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_06_notify',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 06 · 安安·酒吧邀约
|
||||
// ══════════════════
|
||||
msg_06_notify: {
|
||||
delay: 2000,
|
||||
narrate: '晚上十点。安安发来语音,她说她们今晚在酒吧,问你要不要过来,说来了就是了,不用提前说。',
|
||||
tap: {
|
||||
label: '听完再说',
|
||||
state: s => s,
|
||||
next: 'msg_06_play',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不去',
|
||||
narrate: '你没有打开消息。今晚不想动。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_07_notify',
|
||||
},
|
||||
},
|
||||
msg_06_play: {
|
||||
audio: [`${_T}/lv11_2200_anan_voice_01_wav_v1.mp3`],
|
||||
narrate: '安安的声音很热闹,背景里有音乐。她说来嘛来嘛,好久没见了,就几个人,很轻松的那种。',
|
||||
tap: {
|
||||
label: '答应去',
|
||||
narrate: '你回了条消息说行,等等到。安安说耶!你站起来换了件外套,但后来不知道为什么又坐下了。',
|
||||
state: s => ({ engage: s.engage + 1 }),
|
||||
next: 'msg_07_notify',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不去了',
|
||||
narrate: '你回了消息说今晚状态不太好,下次吧。安安说没事,下次一定。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_07_notify',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 07 · 寝室群×5
|
||||
// ══════════════════
|
||||
msg_07_notify: {
|
||||
delay: 2000,
|
||||
narrate: '晚上十点一刻。寝室群活了,连着震了好几下,是大学室友们在聊。你们已经很久没联系了。',
|
||||
tap: {
|
||||
label: '看看聊什么',
|
||||
state: s => s,
|
||||
next: 'msg_07_play',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '静音',
|
||||
narrate: '你把群消息静音了。不是今晚。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_08',
|
||||
},
|
||||
},
|
||||
msg_07_play: {
|
||||
audio: [
|
||||
`${_T}/lv11_2215_dorm_a_voice_01_wav_v1.mp3`,
|
||||
`${_T}/lv11_2215_dorm_b_voice_01_wav_v1.mp3`,
|
||||
`${_T}/lv11_2215_dorm_c_voice_01_wav_v1.mp3`,
|
||||
`${_T}/lv11_2215_dorm_a_voice_02_wav_v1.mp3`,
|
||||
`${_T}/lv11_2215_dorm_b_voice_02_wav_v1.mp3`,
|
||||
],
|
||||
narrate: '她们在聊上周聚会的事,有人说想你了,有人问你最近怎么样,有人说快来啊。那种熟悉的语气,你有点想念。',
|
||||
tap: {
|
||||
label: '回一条',
|
||||
narrate: '你发了一条消息,说最近还好,想你们了。群里一下子热闹起来,大家说你终于出现了。你看着那些消息,嘴角动了一下。',
|
||||
state: s => ({ engage: s.engage + 1 }),
|
||||
next: 'msg_08',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '看着但不回',
|
||||
narrate: '你看完了,但没有回复。你把手机放下。那种热闹是她们的,你在这边。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_08',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 08 · 未知号码
|
||||
// ══════════════════
|
||||
msg_08: {
|
||||
delay: 3000,
|
||||
narrate: '晚上十点半。手机响了,来电显示:未知号码。铃声有点不一样,低沉的那种。',
|
||||
tap: {
|
||||
label: '接听',
|
||||
audio: [`${_SFX}/sfx_breathing_unknown_v1.mp3`],
|
||||
narrate: '你接了。里面没有声音,只有轻微的呼吸声,然后断了。你不知道是谁,心跳加快了一下。',
|
||||
state: s => ({ engage: s.engage + 1 }),
|
||||
next: 'msg_09_notify',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不接',
|
||||
narrate: '未知号码,不接。铃声停了。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_09_notify',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 09 · 小美醉了·深夜语音×4
|
||||
// ══════════════════
|
||||
msg_09_notify: {
|
||||
delay: 3000,
|
||||
bgm: `${_MUS}/lv11_bgm_m2_suspense_v1.mp3`,
|
||||
narrate: '十点四十五分。小美又发来消息,连续四条。语音很长,而且发送时间是一条接一条。',
|
||||
tap: {
|
||||
label: '听',
|
||||
state: s => s,
|
||||
next: 'msg_09_play',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '先不听',
|
||||
narrate: '消息放那里,你没有打开。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_10_notify',
|
||||
},
|
||||
},
|
||||
msg_09_play: {
|
||||
audio: [
|
||||
`${_T}/lv11_2245_xiaomei_voice_01_seg1_wav_v1.mp3`,
|
||||
`${_T}/lv11_2245_xiaomei_voice_01_seg2_wav_v1.mp3`,
|
||||
`${_T}/lv11_2245_xiaomei_voice_01_seg3_wav_v1.mp3`,
|
||||
`${_T}/lv11_2245_xiaomei_voice_01_seg4_wav_v1.mp3`,
|
||||
],
|
||||
narrate: '小美喝多了,说话有点不清楚,但每一句都很认真。她说她一直有件事想跟你说:那年她告诉了别人,是她不对,她知道,她一直很愧疚,她说她怕你恨她。',
|
||||
tap: {
|
||||
label: '回她',
|
||||
narrate: '你打了几个字发过去。小美隔了很久才回,说谢谢你,然后说她要先睡了,明天聊。你把手机放下。',
|
||||
state: s => ({ engage: s.engage + 1 }),
|
||||
next: 'msg_10_notify',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '今晚不回',
|
||||
narrate: '你没有回。这件事太重了,不是今晚能说清楚的。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_10_notify',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 10 · 妈妈文字消息
|
||||
// ══════════════════
|
||||
msg_10_notify: {
|
||||
delay: 2500,
|
||||
narrate: '深夜十一点刚过。妈妈发来一条文字消息,就五个字:睡了吗,夏夏。',
|
||||
tap: {
|
||||
label: '回妈妈',
|
||||
narrate: '你回了:还没呢,妈你睡吧。妈妈说好,早点睡,然后发了个晚安的表情。你盯着那个表情看了一会儿。',
|
||||
state: s => ({ mom_link: s.mom_link + 1, engage: s.engage + 1 }),
|
||||
next: 'msg_11',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不回',
|
||||
narrate: '你没有回妈妈的消息。她那边大概以为你已经睡了。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_11',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 11 · 阿哲来电(关键分支)
|
||||
// ══════════════════
|
||||
msg_11: {
|
||||
delay: 2000,
|
||||
narrate: '十一点一刻。手机响了,专属铃声——是阿哲打来的。',
|
||||
tap: {
|
||||
label: '接听',
|
||||
audio: [`${_T}/lv11_2315_azhe_call_01_wav_v1.mp3`],
|
||||
narrate: '阿哲说他就在附近,问你方不方便他上来取一下那件东西。他的声音平静,但停顿了一下。',
|
||||
state: s => ({ engage: s.engage + 1 }),
|
||||
next: 'msg_11_choice',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '拒接',
|
||||
narrate: '铃声停了。阿哲没再打来。',
|
||||
state: s => ({ avoid: s.avoid + 1, he_back: false }),
|
||||
next: 'msg_12_notify',
|
||||
},
|
||||
},
|
||||
msg_11_choice: {
|
||||
narrate: '他在等你说行不行。',
|
||||
tap: {
|
||||
label: '让他来',
|
||||
narrate: '你说好,上来吧。阿哲说谢谢,说马上到。你放下手机,在房间里站了一会儿。',
|
||||
state: s => ({ he_back: true }),
|
||||
next: 'msg_12_notify',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不方便',
|
||||
narrate: '你说今晚不太方便。阿哲沉默了一秒,说行,那之后再说。挂掉了。',
|
||||
state: s => ({ he_back: false }),
|
||||
next: 'msg_12_notify',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 12 · 周南·第一条语音
|
||||
// ══════════════════
|
||||
msg_12_notify: {
|
||||
delay: 3000,
|
||||
narrate: '十一点半。一个名字出现在屏幕上:周南。你有三年没联系了,他突然发来一条语音。',
|
||||
tap: {
|
||||
label: '听',
|
||||
state: s => s,
|
||||
next: 'msg_12_play',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不打开',
|
||||
narrate: '你没有打开周南的消息。三年了,不急这一会儿。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_13_notify',
|
||||
},
|
||||
},
|
||||
msg_12_play: {
|
||||
audio: [`${_T}/lv11_2330_zhounan_voice_01_wav_v1.mp3`],
|
||||
narrate: '周南说:林夏你好,是我周南,不知道你还记不记得,就是突然想起你,也不知道你还在不在这个号,就发一条。他的声音还是老样子,只是低沉了一些。',
|
||||
tap: {
|
||||
label: '回他',
|
||||
narrate: '你回了:记得,你好啊。消息发出去了,你盯着那两个字,觉得说多说少都不对,就这样。',
|
||||
state: s => ({ zhounan: s.zhounan + 1, engage: s.engage + 1 }),
|
||||
next: 'msg_13_notify',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不回',
|
||||
narrate: '你听完了,没有回。也许三年的空白不是一条消息能填上的。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_13_notify',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 13 · 周南·长语音×4
|
||||
// ══════════════════
|
||||
msg_13_notify: {
|
||||
delay: 2500,
|
||||
bgm: `${_MUS}/lv11_bgm_m3_zhounan_v1.mp3`,
|
||||
narrate: '深夜十一点四十五分。周南又发来四条,比刚才长很多。',
|
||||
tap: {
|
||||
label: '听',
|
||||
state: s => s,
|
||||
next: 'msg_13_play',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不听了',
|
||||
narrate: '你把手机放到一边了。今晚已经够多了。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_14',
|
||||
},
|
||||
},
|
||||
msg_13_play: {
|
||||
audio: [
|
||||
`${_T}/lv11_2345_zhounan_voice_01_wav_v1.mp3`,
|
||||
`${_T}/lv11_2345_zhounan_voice_02_wav_v1.mp3`,
|
||||
`${_T}/lv11_2345_zhounan_voice_03_wav_v1.mp3`,
|
||||
`${_T}/lv11_2345_zhounan_voice_04_wav_v1.mp3`,
|
||||
],
|
||||
narrate: '周南说了他这十年:换了几个城市,做了几份工作,有一段感情,然后结束了。他说他今晚喝了一点酒,就想到你,想到你们高中的那些事,说他一直不知道当年为什么就断了联系。然后他问:你在北京过得好吗?',
|
||||
tap: {
|
||||
label: '认真回他',
|
||||
narrate: '你想了很久,然后把这段时间的事告诉了周南。说不上好不好,就是在过。他说谢谢你告诉我这些。你们聊了很久。',
|
||||
state: s => ({ zhounan: s.zhounan + 1, engage: s.engage + 1 }),
|
||||
next: 'msg_14',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '说还好',
|
||||
narrate: '你回了说还好,然后问了他现在在哪里。你们就这样聊着,没有太深入,但很平静。',
|
||||
state: s => ({ engage: s.engage + 1 }),
|
||||
next: 'msg_14',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 14 · 妈妈·第二次来电
|
||||
// ══════════════════
|
||||
msg_14: {
|
||||
delay: 3000,
|
||||
narrate: '凌晨零点一刻。妈妈又打来电话了,这么晚——她还没睡。',
|
||||
tap: {
|
||||
label: '接听',
|
||||
audio: [`${_T}/lv11_0015_mom_call_01_wav_v1.mp3`],
|
||||
narrate: s => s.mom_link >= 1
|
||||
? '你接了。妈妈说:我就是想再听到你的声音。你们聊了一会儿,说了一些很日常的话,但你都记住了。'
|
||||
: '你接了。妈妈说:没事,就是问问你睡了没。你说没呢,妈你怎么还不睡。她说想听你的声音。',
|
||||
state: s => ({ mom_link: s.mom_link + 1, engage: s.engage + 1 }),
|
||||
next: 'msg_15',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '拒接',
|
||||
narrate: '太晚了,挂掉了。妈妈没有再打来。',
|
||||
state: s => ({ avoid: s.avoid + 1 }),
|
||||
next: 'msg_15',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 15 · 时光胶囊
|
||||
// ══════════════════
|
||||
msg_15: {
|
||||
delay: 2000,
|
||||
narrate: '凌晨零点半。手机弹出一个系统通知:三年前的今天,你给三年后的自己录了一条语音,今天到了。',
|
||||
tap: {
|
||||
label: '听这条语音',
|
||||
audio: [
|
||||
`${_T}/lv11_sys_memo_01_wav_v1.mp3`,
|
||||
`${_T}/lv11_0030_self3y_voice_01_wav_v1.mp3`,
|
||||
],
|
||||
narrate: '你听到了三年前的自己的声音。她说:如果你在听这条消息,说明你撑过来了。不管现在怎么样,你撑过来了。然后她停顿了很久,说:辛苦了。',
|
||||
state: s => ({ self_heard: true, engage: s.engage + 1 }),
|
||||
next: 'msg_16',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不听了',
|
||||
narrate: '你把那个通知划掉了。也许不是现在。',
|
||||
state: s => ({ self_heard: false, avoid: s.avoid + 1 }),
|
||||
next: 'msg_16',
|
||||
},
|
||||
},
|
||||
|
||||
// ══════════════════
|
||||
// 16 · 结局触发点
|
||||
// ══════════════════
|
||||
msg_16: {
|
||||
delay: 3000,
|
||||
narrate: s => s.mom_link >= 2
|
||||
? '凌晨两点。妈妈发来一段很长的语音,她说她一直有些话想说给你听。'
|
||||
: '凌晨两点。房间很安静,窗外偶尔有车声。今晚就这样了。',
|
||||
tap: {
|
||||
label: '听妈妈说',
|
||||
audio: s => s.mom_link >= 2 ? [
|
||||
`${_T}/lv11_0200_mom_voice_01_seg1_wav_v1.mp3`,
|
||||
`${_T}/lv11_0200_mom_voice_01_seg2_wav_v1.mp3`,
|
||||
`${_T}/lv11_0200_mom_voice_01_seg3_wav_v1.mp3`,
|
||||
`${_T}/lv11_0200_mom_voice_01_seg4_wav_v1.mp3`,
|
||||
`${_T}/lv11_0200_mom_voice_01_seg5_wav_v1.mp3`,
|
||||
`${_T}/lv11_0200_mom_voice_01_seg6_wav_v1.mp3`,
|
||||
`${_T}/lv11_0200_mom_voice_01_seg7_wav_v1.mp3`,
|
||||
`${_T}/lv11_0200_mom_voice_01_seg8_wav_v1.mp3`,
|
||||
] : [],
|
||||
narrate: s => s.mom_link >= 2
|
||||
? '妈妈说了很多——关于你小时候,关于她一直担心的事,关于她说不出口但一直想说的话。你把手机贴在耳边,听完了。'
|
||||
: '你就这样坐着,今晚的声音慢慢变少了。',
|
||||
state: s => s,
|
||||
next: null,
|
||||
},
|
||||
doubleTap: {
|
||||
label: '就这样了',
|
||||
narrate: '你把手机放到床头,闭上眼睛。今晚就这样了。',
|
||||
state: s => s,
|
||||
next: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
getEnding: s => {
|
||||
if (s.he_back === true) return 'return'; // 优先级1
|
||||
if (s.mom_link >= 2 && s.engage >= 4) return 'link'; // 优先级2
|
||||
if (s.zhounan >= 2) return 'past'; // 优先级3
|
||||
if (s.self_heard) return 'self'; // 优先级4
|
||||
return 'island'; // 兜底
|
||||
},
|
||||
|
||||
endings: {
|
||||
return: {
|
||||
narrate: '《回归》。阿哲来了,或者你们说好了见面。不管那个门开不开,今晚你做了一个决定。',
|
||||
},
|
||||
link: {
|
||||
narrate: '《连接》。今晚你和妈妈说了话,不止一次。那种联系很细,但是在。也许明天你会想打电话回去。',
|
||||
},
|
||||
past: {
|
||||
narrate: '《旧路》。周南在深夜找到了你,你们聊了很久。那条从高中就断掉的线,今晚接上了一点点。',
|
||||
},
|
||||
self: {
|
||||
narrate: '《自省》。今晚你听了三年前自己的声音。她说辛苦了,你接受了这句话。这不算小事。',
|
||||
},
|
||||
island: {
|
||||
narrate: '《孤岛》。今晚大部分的消息,你都没有回应。你一个人度过了这个夜晚。这也是一种选择,你心里清楚。',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,175 @@
|
||||
// games/shouye.js — 《守夜》
|
||||
// 夜班保安,监控室,五个小时。
|
||||
// 单击=去查那个声音,双击=按兵不动。
|
||||
// 结局由你调查了什么决定。
|
||||
|
||||
const GAME_SHOUYE = {
|
||||
id: 'shouye',
|
||||
intro: '凌晨零点。你是这栋楼的夜班保安,独自坐在一楼监控室。你不能离开座位,但你有一部对讲机和一部电话。今晚五点交班。单击,去查那个声音。双击,按兵不动。',
|
||||
first: 's01',
|
||||
bgm: null,
|
||||
ambience: '../audio/mp3/ambience/amb_apartment_night_01_v1.mp3',
|
||||
initialState: {
|
||||
investigated: [],
|
||||
ignored: [],
|
||||
family_called: false,
|
||||
saw_dawn: false,
|
||||
missed_fight: true, // 默认"没管四楼争吵"
|
||||
},
|
||||
|
||||
scenes: {
|
||||
// ── 00:10 地下室水声 ──
|
||||
s01: {
|
||||
delay: 2000,
|
||||
narrate: '凌晨零点十分。地下室传来水声,不大,但是持续的,一直在响。',
|
||||
tap: {
|
||||
label: '打电话下去查',
|
||||
narrate: '你拨了地下室的内线。没人接。你下去看了一眼,是一根管子在漏水,用旧布堵住了。回到监控室,零点二十三分。',
|
||||
state: s => ({ investigated: [...s.investigated, '地下室漏水'], ignored: s.ignored }),
|
||||
next: 's02',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '先不管',
|
||||
narrate: '你听了一会儿。应该不碍事。你把注意力放回到监控屏幕上。',
|
||||
state: s => ({ investigated: s.investigated, ignored: [...s.ignored, '地下室水声'] }),
|
||||
next: 's02',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 00:47 四楼争吵 ──
|
||||
s02: {
|
||||
delay: 3000,
|
||||
narrate: '零点四十七分。四楼传来争吵声,一男一女,断断续续,能听出来语气很激动,但听不清说什么。',
|
||||
tap: {
|
||||
label: '打电话上去',
|
||||
narrate: '你拨了四楼的号码。男的接了,说没事,就是聊天,让你别担心。声音还是有点紧,但他们关掉了。你记下来了:四楼,零点五十二分,双方均在场。',
|
||||
state: s => ({ investigated: [...s.investigated, '四楼争吵'], ignored: s.ignored, missed_fight: false }),
|
||||
next: 's03',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不管,私事',
|
||||
narrate: '家务事,你管不着。你转过身去继续盯着屏幕。争吵后来停了,你不知道是什么时候。',
|
||||
state: s => ({ investigated: s.investigated, ignored: [...s.ignored, '四楼争吵'], missed_fight: true }),
|
||||
next: 's03',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 01:17 三楼脚步(老陈)──
|
||||
s03: {
|
||||
delay: 3000,
|
||||
narrate: '凌晨一点十七分。三楼有脚步声,很轻,走走停停。监控记录:三楼只有七零二亮着灯,住户是老陈,八十岁,独居。',
|
||||
tap: {
|
||||
label: '打电话问问',
|
||||
narrate: '电话通了,老陈说他在找眼镜,眼镜掉床底下了。你说没事我就是例行确认一下。他说谢谢你小伙子,然后你们聊了几句,关于他年轻时候也做过工程。你挂掉电话,一点二十八分。',
|
||||
state: s => ({ investigated: [...s.investigated, '三楼老陈'], ignored: s.ignored }),
|
||||
next: 's04',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '应该没事',
|
||||
narrate: '老人家睡不好,很正常。你记了一笔,继续监控。脚步声后来停了。',
|
||||
state: s => ({ investigated: s.investigated, ignored: [...s.ignored, '三楼脚步'] }),
|
||||
next: 's04',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 02:03 门铃无应答 ──
|
||||
s04: {
|
||||
delay: 4000,
|
||||
narrate: '凌晨两点零三分。大门门铃响了,不是访客系统,是有人在外面按了手动铃,按了两下,然后停了。没有人应答。监控画面:门口没有人了。',
|
||||
tap: {
|
||||
label: '出去看看',
|
||||
narrate: '你走到门口,什么都没有,只有一个快递包裹靠在墙边,大概是外卖或者什么。也许是送错了。你把包裹推到角落,拍了张照,两点一刻回到座位。',
|
||||
state: s => ({ investigated: [...s.investigated, '门铃'], ignored: s.ignored }),
|
||||
next: 's05',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '不是你管的事',
|
||||
narrate: '无应答,无入侵,记录在案,按流程忽略。你回到屏幕前。',
|
||||
state: s => ({ investigated: s.investigated, ignored: [...s.ignored, '门铃'] }),
|
||||
next: 's05',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 02:55 你打盹了(自动)──
|
||||
s05: {
|
||||
delay: 4000,
|
||||
narrate: s => {
|
||||
const base = '凌晨两点五十五分。你不知道从什么时候开始,眼皮变得很重……你睡着了一会儿。';
|
||||
if (s.missed_fight)
|
||||
return base + '醒来的时候,你看到监控里,四楼门口放着一个箱子,不知道是谁的,不知道什么时候放的。';
|
||||
return base + '醒来的时候,监控里一切正常。你记录:三点十二分,短暂失守。';
|
||||
},
|
||||
next: 's06',
|
||||
},
|
||||
|
||||
// ── 03:30 家人来电 ──
|
||||
s06: {
|
||||
delay: 3000,
|
||||
narrate: '凌晨三点半。你的私人手机响了。是家里的人打来的。这么晚打来——可能是什么事,也可能只是弄错时间了。',
|
||||
tap: {
|
||||
label: '接了',
|
||||
narrate: '是你妈,她说睡不着,看见你今天发的朋友圈,就想打个电话。你们聊了十分钟,说了些不重要的事。挂掉之后,监控室比之前暖了一点点。',
|
||||
state: s => ({ ...s, family_called: true }),
|
||||
next: 's07',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '没接',
|
||||
narrate: '这么晚不方便。你把手机调成静音,准备待会儿发条消息。你没有发。',
|
||||
state: s => ({ ...s, family_called: false }),
|
||||
next: 's07',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 04:12 屋顶声音 ──
|
||||
s07: {
|
||||
delay: 4000,
|
||||
narrate: '凌晨四点十二分。屋顶传来动静,断断续续,不像是机械设备,更像是有什么东西在走动。',
|
||||
tap: {
|
||||
label: '上去看',
|
||||
narrate: '你拿着手电上了顶层,推开屋顶的铁门,风吹过来,你看见了天边刚刚开始发白的颜色——还不是黎明,但是快了。然后你看见了一只流浪猫,坐在水箱上看着你。你们看了对方一会儿,然后你下来了。你是今晚唯一上了屋顶的人。',
|
||||
state: s => ({ ...s, investigated: [...s.investigated, '屋顶'], saw_dawn: true }),
|
||||
next: 's08',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '可能是猫',
|
||||
narrate: '你记了一笔,继续监控。动静后来停了。',
|
||||
state: s => ({ ...s, ignored: [...s.ignored, '屋顶声音'], saw_dawn: false }),
|
||||
next: 's08',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 05:00 交班(自动→结局)──
|
||||
s08: {
|
||||
delay: 5000,
|
||||
narrate: '五点整。接班的人进来了,你们交接了几句,递了记录本,你背上包走出去。外面已经有一点点亮了。',
|
||||
next: null,
|
||||
},
|
||||
},
|
||||
|
||||
getEnding: s => {
|
||||
if (s.saw_dawn) return 'dawn';
|
||||
const n = s.investigated.length;
|
||||
if (n >= 4 && s.family_called) return 'solid';
|
||||
if (n <= 1 && !s.family_called) return 'detached';
|
||||
if (s.missed_fight) return 'oversight';
|
||||
return 'ordinary';
|
||||
},
|
||||
|
||||
endings: {
|
||||
dawn: {
|
||||
narrate: '《你看到了什么》。今晚你一个人上了屋顶,你看到了那只猫,你看到了天边开始变色。只有你看到了。这算不上什么,但只有你知道。',
|
||||
},
|
||||
solid: {
|
||||
narrate: '《平凡守夜》。你今晚查了应该查的,接了应该接的电话。没有大事,没有遗漏。这栋楼平安了一夜,有一部分是因为你在。',
|
||||
},
|
||||
detached: {
|
||||
narrate: '《这不是我的事》。你今晚保持了距离。声音来了,你记录,你不动。你守住了一夜,但不知道楼里发生了什么。这也是一种守夜的方式。',
|
||||
},
|
||||
oversight: {
|
||||
narrate: '《疏忽》。你今晚漏掉了四楼的事。那个女人在凌晨两点多拎着箱子离开了,你没有注意到。也许和你没有关系。也许有。你不知道。',
|
||||
},
|
||||
ordinary: {
|
||||
narrate: '《普通的夜班》。你做了你能做的,漏掉了一些,照顾到了一些。和所有普通的夜班一样。',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
// 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: '《悬在中间》。你接住了一些,放走了一些。你还悬在两个答案之间。也许这就是你今晚真实的状态。',
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,308 @@
|
||||
// games/yisheng.js — 《遗声》
|
||||
// 陌生人的手机,十五条录音。
|
||||
// 单击=听这条,双击=跳过。
|
||||
// 跳过就永远不知道这条说了什么。
|
||||
// 结局由你听了多少、听了哪些决定。
|
||||
|
||||
const GAME_YISHENG = {
|
||||
id: 'yisheng',
|
||||
intro: '你收到了一部手机。手机的主人去世了一个月前。他留下了很多录音,按时间排列。你不认识他。或者你以为你不认识他。单击,听这条录音。双击,跳过,永远不知道这条说了什么。',
|
||||
first: 'r01',
|
||||
bgm: null,
|
||||
ambience: null,
|
||||
initialState: {
|
||||
heard: [],
|
||||
skipped: [],
|
||||
heard_critical: 0, // 听了几条关键录音(r10-r14)
|
||||
unlocked_last: false,
|
||||
},
|
||||
|
||||
scenes: {
|
||||
// ── 第1条:三月五日 ──
|
||||
r01: {
|
||||
narrate: '第一条录音。录制时间:三月五日,早上八点。时长:二十二秒。文件名:买橙子。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说:今天买了一袋橙子,是脐橙,比上次的甜很多。建议大家有机会可以试试。就这些。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '买橙子'], skipped: s.skipped, heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r02',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第一条。你永远不会知道这条说了什么了。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第一条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r02',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第2条:工资日 ──
|
||||
r02: {
|
||||
narrate: '第二条录音。三月十二日,下午。时长:三十八秒。文件名:发工资了。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说:今天发工资了。然后列了他这个月的支出清单,很详细——房租、电费、手机月供、还有给他妈买了一件外套。他说最后剩了不多,但够。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '发工资了'], skipped: s.skipped, heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r03',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第二条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第二条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r03',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第3条:工作烦心事 ──
|
||||
r03: {
|
||||
narrate: '第三条录音。三月十八日,晚上。时长:一分零七秒。文件名:说说工作的事。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说他们组里来了个新同事,总是在会议上抢着发言,说了很多他本来准备说的话。他说他当时没说话,他不确定那是不是对的选择。他在录音里问自己:我是懦弱了,还是留有余地?他没有答案。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '工作的烦恼'], skipped: s.skipped, heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r04',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第三条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第三条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r04',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第4条:和同事吵架 ──
|
||||
r04: {
|
||||
narrate: '第四条录音。四月二日。时长:四十九秒。文件名:吵了一架。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说他今天和那个新同事正面冲突了,是在走廊,两个人都没控制住。他说他说了一句话,事后觉得说得太重了。他在录音里把那句话又说了一遍,想听听是不是真的太重。录音里他沉默了一会儿,然后说:是的,是太重了。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '吵了一架'], skipped: s.skipped, heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r05',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第四条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第四条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r05',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第5条:电影 ──
|
||||
r05: {
|
||||
narrate: '第五条录音。四月十五日,深夜。时长:二十六秒。文件名:电影。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说他去看了一部很老的电影,黑白的,在一个小影院,观众只有六个人。他说他中途哭了,但他不确定是因为电影,还是因为电影院里那种空荡荡的感觉。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '老电影'], skipped: s.skipped, heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r06',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第五条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第五条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r06',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第6条:小林 ──
|
||||
r06: {
|
||||
narrate: '第六条录音。五月三日。时长:一分二十秒。文件名:小林的事。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他开始讲一个叫小林的朋友。他说小林是他认识最久的人,从小一起长大的,后来各自去了不同的城市,联系越来越少。他说他最近一直在想,那种联系的稀薄,是谁的责任?还是说这就是长大的代价?录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '小林的事'], skipped: s.skipped, heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r07',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第六条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第六条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r07',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第7条:小林的一句话 ──
|
||||
r07: {
|
||||
narrate: '第七条录音。五月二十日。时长:三十三秒。文件名:小林说的。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说小林打电话来了。小林在电话里说了一句话,让他很难受。小林说:感觉你现在不太一样了,变得……不知道,挺难接近的。他说他当时说了没什么,你想多了。他说他那时候应该说:我只是最近状态不好。但他没说。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '小林说的话'], skipped: s.skipped, heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r08',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第七条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第七条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r08',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第8条:感情结束了 ──
|
||||
r08: {
|
||||
narrate: '第八条录音。六月八日,凌晨。时长:两分十四秒。文件名:结束了。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说一段感情结束了,他没有细说是谁,说了有多久,只说:结束了,是我提的,我觉得这是对的,但今晚我睡不着。他说他在厨房坐着,泡了杯茶,但茶凉了也没喝。他说:我只是想把今晚的感觉记下来,不想让它消失。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '感情结束了'], skipped: s.skipped, heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r09',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第八条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第八条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r09',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第9条:是不是我的错 ──
|
||||
r09: {
|
||||
narrate: '第九条录音。六月二十日。时长:一分五十秒。文件名:在反思。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说他一直在想那段感情里他做错了什么。他列了很多条,每一条都很具体。然后他说:但是我说不清,这些错是因为我是这样的人,还是因为那时候我在经历别的事情,所以才这样。他说:这个问题很重要,但我没有答案。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '反思'], skipped: s.skipped, heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r10',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第九条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第九条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r10',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第10条(关键):有人问起你 ──
|
||||
r10: {
|
||||
narrate: '第十条录音。七月四日。时长:四十二秒。文件名:有人问起你。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说:今天有人跟我提到了你的名字。我以为我早就忘了,但我发现我还记得很多细节。他没有解释"你"是谁。他说:我觉得我应该把这件事录下来,因为我发现我不想忘记。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '有人问起你'], skipped: s.skipped, heard_critical: s.heard_critical + 1, unlocked_last: s.unlocked_last }),
|
||||
next: 'r11',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第十条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第十条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r11',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第11条(关键):他查了你 ──
|
||||
r11: {
|
||||
narrate: '第十一条录音。七月十一日。时长:一分零八秒。文件名:我查了一下。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说:我查了你现在在哪里,在做什么。不是恶意,就是想知道。他说他找到了一些信息,知道了你现在的大致状态。他说:你好像过得还不错,我很高兴,也有一点……他停了,然后说:没什么。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '他查了你'], skipped: s.skipped, heard_critical: s.heard_critical + 1, unlocked_last: s.unlocked_last }),
|
||||
next: 'r12',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第十一条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第十一条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r12',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第12条(关键):如果你听到 ──
|
||||
r12: {
|
||||
narrate: '第十二条录音。八月二日。时长:五十七秒。文件名:给不认识我的人(草稿)。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说:如果你听到这条,你肯定不认识我。我在想,把这些录音留给一个陌生人,有什么意义。然后他说:也许是因为我希望有人知道我活过,而不是认识我的人,而是真正陌生的人——你不需要对我有感情,你只是听了。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '给陌生人的草稿'], skipped: s.skipped, heard_critical: s.heard_critical + 1, unlocked_last: s.unlocked_last }),
|
||||
next: 'r13',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第十二条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第十二条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r13',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第13条(关键):一件事 ──
|
||||
r13: {
|
||||
narrate: '第十三条录音。九月一日。时长:两分三十八秒。文件名:我有一件事想说。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他深吸一口气,说:有一件事我从没对任何人说过。他停了很久,然后说:我有时候觉得活着的意义我不太找得到,不是说我要做什么,只是我不知道我在这里有什么用。他说:我知道这句话很沉,但我必须把它说出来,因为放在心里太久了。然后他说:我今天状态还可以,就是今天有这个力气把它说出来。录音结束。',
|
||||
state: s => ({ heard: [...s.heard, '那件事'], skipped: s.skipped, heard_critical: s.heard_critical + 1, unlocked_last: s.unlocked_last }),
|
||||
next: 'r14',
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第十三条。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第十三条'], heard_critical: s.heard_critical, unlocked_last: s.unlocked_last }),
|
||||
next: 'r14',
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第14条(关键):最后一条 ──
|
||||
r14: {
|
||||
narrate: '第十四条录音。十月十五日,录制时间是他去世前三天。时长:三分二十秒。文件名:最后一条,我想讲完整。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说:我知道这是最后一条了。我不是悲观,我只是知道。他说:我想对听这些录音的人说,我不后悔我活过的大多数时候。橙子是甜的,老电影是值得哭的,小林的声音我还记得,那个人,不管怎样,我不后悔爱过。他说:如果你听了很多条,谢谢你。你是陌生人,但你陪着我说完了。录音结束。',
|
||||
state: s => ({
|
||||
heard: [...s.heard, '最后一条'],
|
||||
skipped: s.skipped,
|
||||
heard_critical: s.heard_critical + 1,
|
||||
unlocked_last: s.heard.length >= 9,
|
||||
}),
|
||||
next: s => s.heard.length >= 9 ? 'r15' : null,
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你跳过了第十四条,也就是最后一条。手机里没有更多录音了。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '第十四条'], heard_critical: s.heard_critical, unlocked_last: false }),
|
||||
next: null,
|
||||
},
|
||||
},
|
||||
|
||||
// ── 第15条(隐藏):给不认识我的人 ──
|
||||
r15: {
|
||||
delay: 2000,
|
||||
narrate: '还有一条录音,没有标日期。文件名:给不认识我的人(正式版)。这条需要你听了很多条才能解锁。你解锁了。',
|
||||
tap: {
|
||||
label: '听',
|
||||
narrate: '他说:你好。你不认识我,我也不认识你。但你坐在那里,听完了我这些年说的话。我不知道你是谁,我不知道你们那边现在是白天还是夜晚。我只是想说:谢谢你愿意听一个陌生人说话。这很重要,比你知道的还要重要。录音结束。这是最后一条了。',
|
||||
state: s => ({ heard: [...s.heard, '给不认识我的人'], skipped: s.skipped, heard_critical: s.heard_critical, unlocked_last: true }),
|
||||
next: null,
|
||||
},
|
||||
doubleTap: {
|
||||
label: '跳过',
|
||||
narrate: '你连最后这条也跳过了。',
|
||||
state: s => ({ heard: s.heard, skipped: [...s.skipped, '最后一条'], heard_critical: s.heard_critical, unlocked_last: false }),
|
||||
next: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
getEnding: s => {
|
||||
if (s.unlocked_last) return 'known';
|
||||
if (s.heard_critical === 0) return 'stranger';
|
||||
if (s.skipped.length >= 7) return 'passing';
|
||||
if (s.heard.length >= 9) return 'outline';
|
||||
return 'brief';
|
||||
},
|
||||
|
||||
endings: {
|
||||
known: {
|
||||
narrate: '《我认识你》。你听完了他大部分的录音,你听到了那条隐藏的最后一条。你不认识他,但你陪他把该说的都说完了。这已经足够了。',
|
||||
},
|
||||
stranger: {
|
||||
narrate: '《陌生人》。你跳过了所有关于他和你之间联系的录音。他对你来说,还是一个完全的陌生人。也许这样也好。',
|
||||
},
|
||||
passing: {
|
||||
narrate: '《路过》。你路过了他的大部分录音,只停留了几条。你知道他存在过,但你不知道他是谁。',
|
||||
},
|
||||
outline: {
|
||||
narrate: '《轮廓》。你听了他生活的碎片,知道了一个大致的形状。你不知道他所有的事,但你知道了一个人活着的样子。',
|
||||
},
|
||||
brief: {
|
||||
narrate: '《简短的相遇》。你听了几条,然后停下了。也许你听到了你需要听的,也许你还没准备好听更多。这也是一种结果。',
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user