// 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: '《孤岛》。今晚大部分的消息,你都没有回应。你一个人度过了这个夜晚。这也是一种选择,你心里清楚。', }, }, };