diff --git a/game1/index.html b/game1/index.html
new file mode 100644
index 0000000..0eb54dc
--- /dev/null
+++ b/game1/index.html
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+ 林夏
+
+
+
+
+
+
+
林夏
+
+ 今晚 16 条消息
+ 你接谁,不接谁
+ 决定明天的自己
+
+
+
操 作 说 明
+
+
+
来电时
+
单击 / 右滑接听
+
左滑 / 双击拒接
+
+
+
+
收到消息
+
单击播放
+
双击 / 左滑跳过
+
+
+
+
选择回复
+
单击简单回应
+
右滑温暖回应
+
左滑冷淡回应
+
上滑追问
+
下滑沉默
+
双击不回复
+
+
+
+
+
+
随时可用
+
两指长按暂停
+
三指点击退出(暂停后)
+
长按播报未读数
+
+
+
+
· 轻触屏幕开始 ·
+
+ 建议关灯 · 戴上耳机
+ 按你的节奏来
+
+
+ 本游戏涉及孤独、家庭关系等情感话题
+ 请在舒适的状态下体验
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/game1/js/audio.js b/game1/js/audio.js
new file mode 100644
index 0000000..50b5c27
--- /dev/null
+++ b/game1/js/audio.js
@@ -0,0 +1,283 @@
+// audio.js — 音频管理器 + 合成 UI 音效
+
+class AudioManager {
+ constructor() {
+ this._ctx = null;
+ this._master = null;
+ this._voiceQueue = [];
+ this._voiceEl = null;
+ this._ringEl = null;
+ this._bgmEl = null;
+ this._ambEl = null;
+ this._preloaded = {};
+ }
+
+ // ── 初始化(必须在用户手势后调用)──
+ async init() {
+ this._ctx = new (window.AudioContext || window.webkitAudioContext)();
+ if (this._ctx.state === 'suspended') await this._ctx.resume();
+ this._master = this._ctx.createGain();
+ this._master.gain.value = 1.0;
+ this._master.connect(this._ctx.destination);
+ }
+
+ // ── 合成短音(UI 反馈音效)──
+ synth({ freq = 440, freq2 = null, dur = 100, type = 'sine', vol = 0.3 } = {}) {
+ if (!this._ctx) return;
+ const t = this._ctx.currentTime;
+ const d = dur / 1000;
+
+ const osc = this._ctx.createOscillator();
+ const gain = this._ctx.createGain();
+
+ osc.type = type;
+ osc.frequency.setValueAtTime(freq, t);
+ if (freq2 != null) {
+ osc.frequency.linearRampToValueAtTime(freq2, t + d);
+ }
+
+ gain.gain.setValueAtTime(vol, t);
+ gain.gain.setValueAtTime(vol, t + d * 0.75);
+ gain.gain.linearRampToValueAtTime(0, t + d);
+
+ osc.connect(gain);
+ gain.connect(this._master);
+ osc.start(t);
+ osc.stop(t + d + 0.05);
+ }
+
+ // ── 预加载短音效 ──
+ async preload(key, url) {
+ if (this._preloaded[key]) return;
+ try {
+ const res = await fetch(url);
+ const buf = await res.arrayBuffer();
+ this._preloaded[key] = await this._ctx.decodeAudioData(buf);
+ } catch (e) {
+ console.warn('[Audio] preload failed:', key, e);
+ }
+ }
+
+ // ── 播放预加载音效 ──
+ playFX(key, { vol = 1.0, loop = false } = {}) {
+ const buf = this._preloaded[key];
+ if (!buf) return null;
+ const src = this._ctx.createBufferSource();
+ src.buffer = buf;
+ src.loop = loop;
+ const gain = this._ctx.createGain();
+ gain.gain.value = vol;
+ src.connect(gain);
+ gain.connect(this._master);
+ src.start();
+ return { src, gain, stop: () => { try { src.stop(); } catch (_) {} } };
+ }
+
+ // ── 铃声(循环)──
+ async startRingtone(url) {
+ this.stopRingtone();
+ this._ringEl = new Audio(url);
+ this._ringEl.loop = true;
+ this._ringEl.volume = 1.0;
+ try { await this._ringEl.play(); } catch (e) { console.warn('[Audio] ring error', e); }
+ }
+
+ stopRingtone() {
+ if (this._ringEl) {
+ this._ringEl.pause();
+ this._ringEl.src = '';
+ this._ringEl = null;
+ }
+ }
+
+ // ── 语音播放(多段顺序)──
+ playVoice(urls, { vol = 1.0, onEnd = null } = {}) {
+ this.stopVoice();
+ this._voiceQueue = [...urls];
+ this._playNext(vol, onEnd);
+ }
+
+ _playNext(vol, onEnd) {
+ if (this._voiceQueue.length === 0) {
+ this._voiceEl = null;
+ if (onEnd) onEnd();
+ return;
+ }
+ const url = this._voiceQueue.shift();
+ const el = new Audio(url);
+ el.volume = vol;
+ this._voiceEl = el;
+ el.onended = () => this._playNext(vol, onEnd);
+ el.onerror = () => {
+ console.warn('[Audio] voice error:', url);
+ this._playNext(vol, onEnd);
+ };
+ el.play().catch(e => console.warn('[Audio] play error', e));
+ }
+
+ stopVoice() {
+ this._voiceQueue = [];
+ if (this._voiceEl) {
+ this._voiceEl.onended = null;
+ this._voiceEl.pause();
+ this._voiceEl = null;
+ }
+ }
+
+ isVoicePlaying() {
+ return !!(this._voiceEl && !this._voiceEl.paused);
+ }
+
+ // ── BGM(淡入淡出切换)──
+ async startBGM(url, { vol = 0.35, fade = 2000 } = {}) {
+ if (this._bgmEl) {
+ const old = this._bgmEl;
+ this._fadeOut(old, fade / 2).then(() => { old.pause(); old.src = ''; });
+ }
+ const el = new Audio(url);
+ el.loop = true;
+ el.volume = 0;
+ this._bgmEl = el;
+ try {
+ await el.play();
+ this._fadeTo(el, vol, fade);
+ } catch (e) { console.warn('[Audio] BGM error', e); }
+ }
+
+ stopBGM(fade = 2000) {
+ if (this._bgmEl) {
+ const el = this._bgmEl;
+ this._bgmEl = null;
+ this._fadeOut(el, fade).then(() => { el.pause(); el.src = ''; });
+ }
+ }
+
+ // ── 环境音 ──
+ async startAmbience(url, { vol = 0.25 } = {}) {
+ if (this._ambEl) { this._ambEl.pause(); this._ambEl.src = ''; }
+ const el = new Audio(url);
+ el.loop = true;
+ el.volume = vol;
+ this._ambEl = el;
+ try { await el.play(); } catch (e) { console.warn('[Audio] ambience error', e); }
+ }
+
+ stopAmbience() {
+ if (this._ambEl) { this._ambEl.pause(); this._ambEl.src = ''; this._ambEl = null; }
+ }
+
+ // ── 音量渐变 ──
+ _fadeTo(el, target, duration) {
+ const start = el.volume;
+ const steps = 30;
+ const dt = duration / steps;
+ const dv = (target - start) / steps;
+ let i = 0;
+ const t = setInterval(() => {
+ el.volume = Math.max(0, Math.min(1, start + dv * i));
+ if (++i >= steps) clearInterval(t);
+ }, dt);
+ }
+
+ _fadeOut(el, duration) {
+ return new Promise(resolve => {
+ const start = el.volume;
+ const steps = 20;
+ const dt = duration / steps;
+ const dv = start / steps;
+ let i = 0;
+ const t = setInterval(() => {
+ el.volume = Math.max(0, start - dv * i);
+ if (++i >= steps) { clearInterval(t); resolve(); }
+ }, dt);
+ });
+ }
+}
+
+const Snd = new AudioManager();
+
+// ══════════════════════════════════════════════════
+// SFX_UI — 合成 UI 音效(不依赖音频文件)
+// ══════════════════════════════════════════════════
+
+const SFX_UI = {
+ // 确认操作(单击)
+ tap() {
+ Snd.synth({ freq: 880, dur: 45, vol: 0.2 });
+ },
+
+ // 滑动确认
+ swipe() {
+ Snd.synth({ freq: 660, dur: 60, vol: 0.18 });
+ },
+
+ // 无效操作
+ error() {
+ Snd.synth({ freq: 200, dur: 100, type: 'square', vol: 0.1 });
+ },
+
+ // 新消息到达
+ notify() {
+ Snd.synth({ freq: 523, dur: 100, vol: 0.25 });
+ setTimeout(() => Snd.synth({ freq: 659, dur: 130, vol: 0.2 }), 130);
+ },
+
+ // 进入选择模式(三音上行)
+ choiceReady() {
+ Snd.synth({ freq: 440, dur: 80, type: 'triangle', vol: 0.18 });
+ setTimeout(() => Snd.synth({ freq: 523, dur: 80, type: 'triangle', vol: 0.18 }), 100);
+ setTimeout(() => Snd.synth({ freq: 659, dur: 120, type: 'triangle', vol: 0.18 }), 200);
+ },
+
+ // 选择:温暖/接受(右滑)— 上行音
+ choiceWarm() {
+ Snd.synth({ freq: 440, freq2: 659, dur: 140, vol: 0.22 });
+ },
+
+ // 选择:冷淡/拒绝(左滑)— 下行音
+ choiceCold() {
+ Snd.synth({ freq: 523, freq2: 349, dur: 140, vol: 0.22 });
+ },
+
+ // 选择:好奇/追问(上滑)— 快速上行
+ choiceCurious() {
+ Snd.synth({ freq: 523, freq2: 784, dur: 120, vol: 0.22 });
+ },
+
+ // 选择:沉默/退缩(下滑)— 极轻低音
+ choiceSilent() {
+ Snd.synth({ freq: 330, dur: 200, vol: 0.08 });
+ },
+
+ // 跳过 / 忽略
+ skip() {
+ Snd.synth({ freq: 587, freq2: 392, dur: 80, vol: 0.12 });
+ },
+
+ // 下一条消息即将到来
+ next() {
+ Snd.synth({ freq: 440, dur: 80, vol: 0.1 });
+ setTimeout(() => Snd.synth({ freq: 523, dur: 100, vol: 0.13 }), 120);
+ },
+
+ // 打字/录音音效(~800ms)
+ typing() {
+ for (let i = 0; i < 6; i++) {
+ setTimeout(() => {
+ Snd.synth({ freq: 600 + Math.random() * 200, dur: 30, type: 'sine', vol: 0.1 });
+ }, i * 120);
+ }
+ },
+
+ // 发送音效
+ send() {
+ Snd.synth({ freq: 400, freq2: 900, dur: 120, type: 'sine', vol: 0.18 });
+ },
+
+ // 结局音效
+ ending() {
+ Snd.synth({ freq: 523, dur: 400, vol: 0.25 });
+ setTimeout(() => Snd.synth({ freq: 659, dur: 400, vol: 0.2 }), 450);
+ setTimeout(() => Snd.synth({ freq: 784, dur: 600, vol: 0.15 }), 900);
+ },
+};
diff --git a/game1/js/engine.js b/game1/js/engine.js
new file mode 100644
index 0000000..6915907
--- /dev/null
+++ b/game1/js/engine.js
@@ -0,0 +1,825 @@
+// engine.js — 游戏引擎 v2(多轮对话 + 林夏语音 + NPC反应)
+//
+// 状态流:idle → notification/ringing → playing → choosing
+// → responding → reacting → [choosing | idle] → …… → ending
+//
+// 新增状态:
+// responding — 林夏说话中(打字音效 + 语音播放)
+// reacting — NPC反应播放中
+// paused — 游戏暂停(两指长按触发)
+
+class GameEngine {
+ constructor() {
+ this._state = 'idle';
+
+ // 剧情变量
+ this.vars = {
+ MOM_LINK: false,
+ HE_BACK: null,
+ GROUP_REPLY: false,
+ ZHOUNAN_DEPTH: 0,
+ ZHOUNAN_SHARE: false,
+ SELF_RECORD: false,
+ UNREAD: 0,
+ REPLY_COUNT: 0,
+ };
+
+ this.profile = new ProfileTracker();
+ this.msgState = {};
+ this.currentMsg = null;
+
+ this._queue = [];
+ this._endingTriggered = false;
+ this._reminderTimer = null;
+
+ // v2: 对话系统
+ this._currentChoices = null; // 当前可用的选项集
+ this._pendingResolve = null; // 用于中断语音播放的 resolve
+ this._skipRequested = false; // 跳过标志
+
+ // 暂停系统
+ this._pausedState = null; // 暂停前的状态
+
+ this.gest = null;
+ }
+
+ // ── 状态 getter/setter ──
+ get state() { return this._state; }
+ set state(s) {
+ this._state = s;
+ dbg(`state: ${s}`);
+ const el = document.getElementById('hint');
+ if (!el) return;
+ const H = {
+ idle: '',
+ notification: '单击 播放 · 双击 跳过',
+ ringing: '单击 接听 · 左滑 拒接',
+ playing: '双击 跳过 · 长按 重播',
+ choosing: '→温暖 ←冷淡 ↑追问 ↓沉默',
+ responding: '双击 跳过',
+ reacting: '双击 跳过',
+ paused: '单击 继续 · 三指点击 退出',
+ ending: '',
+ };
+ el.textContent = H[s] || '';
+ }
+
+ // ══════════════════════════════════════════
+ // 初始化 & 启动
+ // ══════════════════════════════════════════
+
+ init(gestureDetector) {
+ this.gest = gestureDetector;
+ this.gest
+ .on('singletap', () => this._onTap())
+ .on('doubletap', () => this._onDoubleTap())
+ .on('swipe_left', () => this._onSwipe('left'))
+ .on('swipe_right', () => this._onSwipe('right'))
+ .on('swipe_up', () => this._onSwipe('up'))
+ .on('swipe_down', () => this._onSwipe('down'))
+ .on('longpress', () => this._onLongPress())
+ .on('twofinger_longpress', () => this._onPause())
+ .on('threefinger_tap', () => this._onExit());
+ }
+
+ async start() {
+ this._queue = [...MESSAGES];
+
+ // 内容预警
+ await this._playVoiceAsync([`${T}/v2_sys_content_warning_wav_v1.mp3`]);
+ await sleep(1000);
+
+ await Snd.startAmbience(AMBIENCE_DEFAULT, { vol: 0.15 });
+ await sleep(1500);
+ Snd.startBGM(`${MUS}/lv11_bgm_m1_main_loop_v1.mp3`, { vol: 0.1 });
+ await sleep(1500);
+
+ this._next();
+ }
+
+ // ══════════════════════════════════════════
+ // 语音播放(异步 + 可中断)
+ // ══════════════════════════════════════════
+
+ _playVoiceAsync(urls) {
+ return new Promise(resolve => {
+ this._pendingResolve = resolve;
+ Snd.playVoice(urls, {
+ onEnd: () => {
+ this._pendingResolve = null;
+ resolve();
+ },
+ });
+ });
+ }
+
+ _interruptVoice() {
+ Snd.stopVoice();
+ TTS.stop();
+ if (this._pendingResolve) {
+ this._pendingResolve();
+ this._pendingResolve = null;
+ }
+ }
+
+ // 打字 + 发送音效
+ async _playTypingSend() {
+ SFX_UI.typing();
+ await sleep(800);
+ if (this._skipRequested) return;
+ SFX_UI.send();
+ await sleep(300);
+ }
+
+ // ══════════════════════════════════════════
+ // 消息队列推进
+ // ══════════════════════════════════════════
+
+ async _next() {
+ if (this._endingTriggered) return;
+
+ const msg = this._queue.shift();
+ if (!msg) {
+ this.triggerFinalEnding();
+ return;
+ }
+
+ if (msg.requiresMomLink && !this.vars.MOM_LINK) {
+ this.triggerFinalEnding();
+ return;
+ }
+
+ this.currentMsg = msg;
+ this._currentChoices = msg.choices || null;
+ this.msgState[msg.id] = 'unread';
+ this.vars.UNREAD++;
+
+ if (msg.type === 'call') {
+ this._startRinging(msg);
+ } else {
+ this._notifyMessage(msg);
+ }
+ }
+
+ _notifyMessage(msg) {
+ this.state = 'notification';
+ Haptic.message();
+ SFX_UI.notify();
+ this._startReminder();
+ }
+
+ async _startRinging(msg) {
+ this.state = 'ringing';
+ Haptic.ring();
+ await Snd.startRingtone(msg.ringtone);
+ this._startReminder();
+ }
+
+ // ══════════════════════════════════════════
+ // 通话流程(异步)
+ // ══════════════════════════════════════════
+
+ async _answerCall(msg) {
+ Snd.stopRingtone();
+ Haptic.stop();
+ Haptic.confirm();
+ SFX_UI.tap();
+ this._clearReminder();
+
+ this.state = 'playing';
+ this.msgState[msg.id] = 'read';
+ this.vars.UNREAD = Math.max(0, this.vars.UNREAD - 1);
+
+ if (msg.stateOnAnswer) this._applyState(msg.stateOnAnswer);
+ if (msg.profileOnAnswer) this.profile.add(msg.profileOnAnswer);
+
+ this._skipRequested = false;
+
+ // 播放通话内容
+ await this._playVoiceAsync(msg.audio);
+
+ // _onDoubleTap 在 'playing' 状态下会直接处理状态转换
+ // 所以如果状态已不是 'playing',说明跳过已被处理,直接返回
+ if (this.state !== 'playing') return;
+
+ // 自动挂断(MSG-08 未知号码)
+ if (msg.autoHangup) {
+ if (msg.sfxAfter) {
+ const sfx = Array.isArray(msg.sfxAfter) ? msg.sfxAfter[0] : msg.sfxAfter;
+ await this._playVoiceAsync([sfx]);
+ if (this.state !== 'playing') return;
+ }
+ this._proceedToNext();
+ return;
+ }
+
+ // 自动回复(MSG-01/03/14 妈妈电话、外卖、妈妈第二次电话)
+ if (msg.autoReply) {
+ await this._playTypingSend();
+ if (this.state !== 'playing') return;
+
+ this.state = 'responding';
+ await this._playVoiceAsync([msg.autoReply]);
+
+ // 条件反应(MSG-14:先播前导语"真的没事?",再根据 MOM_LINK 播放不同反应)
+ if (!this._skipRequested && msg.autoReact) {
+ this.state = 'reacting';
+ // 前导语(如"真的没事?")
+ if (msg.autoReactPreamble) {
+ await sleep(500);
+ if (!this._skipRequested) {
+ await this._playVoiceAsync([msg.autoReactPreamble]);
+ }
+ }
+ // 条件分支反应
+ if (!this._skipRequested) {
+ const val = this.vars[msg.autoReact.condition];
+ const reactUrl = val ? msg.autoReact['true'] : msg.autoReact['false'];
+ if (reactUrl) {
+ await sleep(400);
+ if (!this._skipRequested) {
+ await this._playVoiceAsync([reactUrl]);
+ }
+ }
+ }
+ }
+ this._proceedToNext();
+ return;
+ }
+
+ // 通话后有选择(MSG-11 阿哲)
+ if (msg.choices) {
+ this._enterChoosing(msg.choices);
+ return;
+ }
+
+ this._proceedToNext();
+ }
+
+ _rejectCall(msg) {
+ Snd.stopRingtone();
+ Haptic.reject();
+ SFX_UI.skip();
+ this._clearReminder();
+ this.msgState[msg.id] = 'missed';
+ if (msg.profileOnMiss) this.profile.add(msg.profileOnMiss);
+ this._proceedToNext();
+ }
+
+ // ══════════════════════════════════════════
+ // 消息播放(异步)
+ // ══════════════════════════════════════════
+
+ async _playMessage(msg) {
+ this._clearReminder();
+ this.state = 'playing';
+ this.msgState[msg.id] = 'read';
+ this.vars.UNREAD = Math.max(0, this.vars.UNREAD - 1);
+
+ if (msg.bgm) Snd.startBGM(msg.bgm, { vol: 0.1 });
+
+ // 系统通知(时光胶囊)
+ if (msg.type === 'system_notification') {
+ this._playSystemNotification(msg);
+ return;
+ }
+
+ this._skipRequested = false;
+
+ // 文字消息(TTS 朗读)
+ if (msg.type === 'wechat_text' && msg.text) {
+ await TTS.speak(msg.text);
+ }
+ // 语音消息
+ else if (msg.audio?.length > 0) {
+ await this._playVoiceAsync(msg.audio);
+ }
+
+ if (this._skipRequested) return;
+ if (this.state !== 'playing') return;
+
+ this._afterPlayed(msg);
+ }
+
+ _playSystemNotification(msg) {
+ Snd.playVoice([msg.systemAudio], {
+ onEnd: () => {
+ if (this.state !== 'playing') return;
+ const audio = msg.audioByProfile?.[this.profile.label];
+ if (audio) {
+ if (msg.stateOnPlay) this._applyState(msg.stateOnPlay);
+ if (msg.profileOnPlay) this.profile.add(msg.profileOnPlay);
+ setTimeout(() => {
+ if (this.state !== 'playing') return;
+ Snd.playVoice([audio], {
+ onEnd: () => this._proceedToNext(),
+ });
+ }, 800);
+ } else {
+ this._proceedToNext();
+ }
+ },
+ });
+ }
+
+ _afterPlayed(msg) {
+ if (this.state !== 'playing') return;
+
+ if (msg.isEnding) {
+ this.triggerFinalEnding();
+ return;
+ }
+
+ if (msg.choices) {
+ this._enterChoosing(msg.choices);
+ return;
+ }
+
+ this._proceedToNext();
+ }
+
+ // ══════════════════════════════════════════
+ // 选择系统(v2 多轮对话核心)
+ // ══════════════════════════════════════════
+
+ _enterChoosing(choices) {
+ this._currentChoices = choices;
+ this.state = 'choosing';
+ SFX_UI.choiceReady();
+ Haptic.confirm();
+ Snd.playVoice([`${T}/v2_sys_wait_reply_wav_v1.mp3`]);
+ this._startReminder();
+ }
+
+ async _handleChoice(dir) {
+ const choices = this._currentChoices;
+ if (!choices) {
+ SFX_UI.error();
+ Haptic.error();
+ return;
+ }
+
+ const choice = choices[dir];
+ if (!choice) {
+ SFX_UI.error();
+ Haptic.error();
+ return;
+ }
+
+ this._clearReminder();
+ this._skipRequested = false;
+
+ // 1. 播放方向对应的情感音效
+ const sfxMap = {
+ tap: 'tap',
+ right: 'choiceWarm',
+ left: 'choiceCold',
+ up: 'choiceCurious',
+ down: 'choiceSilent',
+ };
+ if (sfxMap[dir]) SFX_UI[sfxMap[dir]]();
+ Haptic.select();
+
+ // 2. 应用状态和画像
+ if (!choice.isSilence) {
+ this.msgState[this.currentMsg.id] = 'replied';
+ this.vars.REPLY_COUNT++;
+ }
+ if (choice.state) this._applyState(choice.state);
+ if (choice.profile) this.profile.add(choice.profile);
+
+ // 3. 林夏回应
+ this.state = 'responding';
+
+ if (choice.isSilence) {
+ // 沉默:短暂停顿
+ await sleep(1500);
+ } else if (choice.linxia) {
+ // 打字 → 发送 → 林夏语音
+ await this._playTypingSend();
+ if (!this._skipRequested) {
+ await this._playVoiceAsync([choice.linxia]);
+ }
+ }
+
+ if (this._skipRequested) {
+ // 跳过了林夏回应 → 如有 followUp 进入选择,否则跳到下一条
+ if (choice.followUp?.choices) {
+ this._enterChoosing(choice.followUp.choices);
+ return;
+ }
+ this._proceedToNext();
+ return;
+ }
+
+ // 4. NPC 反应
+ if (choice.npcReact?.length > 0) {
+ this.state = 'reacting';
+ await sleep(400);
+ if (!this._skipRequested) {
+ await this._playVoiceAsync(choice.npcReact);
+ }
+ }
+
+ if (this._skipRequested) {
+ if (choice.followUp?.choices) {
+ this._enterChoosing(choice.followUp.choices);
+ return;
+ }
+ this._proceedToNext();
+ return;
+ }
+
+ // 5. 如有后续对话 → 再次进入选择
+ if (choice.followUp?.choices) {
+ await sleep(300);
+ this._enterChoosing(choice.followUp.choices);
+ return;
+ }
+
+ // 6. 推进到下一条
+ this._proceedToNext();
+ }
+
+ _skipChoice() {
+ this._clearReminder();
+ SFX_UI.skip();
+ Haptic.confirm();
+ this.profile.add({ avoidance: 1 });
+ this._proceedToNext();
+ }
+
+ // ══════════════════════════════════════════
+ // 推进到下一条
+ // ══════════════════════════════════════════
+
+ async _proceedToNext() {
+ const prev = this.currentMsg;
+ this.currentMsg = null;
+ this._currentChoices = null;
+ this.state = 'idle';
+ this._clearReminder();
+
+ if (this._endingTriggered) return;
+
+ // 特殊事件:阿哲来了(门铃)
+ if (prev?.id === 11 && this.vars.HE_BACK === true) {
+ await sleep(1500);
+ Snd.playVoice([`${SFX}/sfx_door_knock_gentle_v1.mp3`]);
+ Haptic.ring();
+ await sleep(3000);
+ }
+
+ // 检查结局D:未读过多
+ const triggered = MESSAGES.length - this._queue.length;
+ if (this.vars.UNREAD >= 8 && triggered >= 14) {
+ this._triggerEnding('D');
+ return;
+ }
+
+ // 短暂停顿后推进
+ SFX_UI.next();
+ await sleep(DEV ? 200 : 2000);
+ this._next();
+ }
+
+ // ══════════════════════════════════════════
+ // 提醒定时器
+ // ══════════════════════════════════════════
+
+ _startReminder() {
+ this._clearReminder();
+ this._reminderTimer = setInterval(() => {
+ if (this.state === 'notification') {
+ SFX_UI.notify();
+ Haptic.message();
+ } else if (this.state === 'choosing') {
+ SFX_UI.choiceReady();
+ Haptic.confirm();
+ } else if (this.state === 'ringing') {
+ Haptic.ring();
+ } else {
+ this._clearReminder();
+ }
+ }, 8000);
+ }
+
+ _clearReminder() {
+ if (this._reminderTimer) {
+ clearInterval(this._reminderTimer);
+ this._reminderTimer = null;
+ }
+ }
+
+ // ══════════════════════════════════════════
+ // 状态变量
+ // ══════════════════════════════════════════
+
+ _applyState(changes) {
+ for (const [k, v] of Object.entries(changes)) {
+ if (k === 'ZHOUNAN_DEPTH_ADD') {
+ this.vars.ZHOUNAN_DEPTH += (v || 1);
+ } else if (v !== null) {
+ this.vars[k] = v;
+ }
+ }
+ }
+
+ // ══════════════════════════════════════════
+ // 手势处理
+ // ══════════════════════════════════════════
+
+ _onTap() {
+ const s = this.state;
+
+ // 暂停中 → 恢复
+ if (s === 'paused') {
+ this._onResume();
+ return;
+ }
+
+ if (s === 'ringing') {
+ this._answerCall(this.currentMsg);
+ } else if (s === 'notification') {
+ SFX_UI.tap();
+ Haptic.confirm();
+ this._playMessage(this.currentMsg);
+ } else if (s === 'choosing') {
+ this._handleChoice('tap');
+ }
+ }
+
+ _onDoubleTap() {
+ const s = this.state;
+
+ if (s === 'paused') return;
+
+ if (s === 'ringing') {
+ this._rejectCall(this.currentMsg);
+ return;
+ }
+
+ if (s === 'notification') {
+ this._clearReminder();
+ SFX_UI.skip();
+ Haptic.confirm();
+ const msg = this.currentMsg;
+ if (msg?.profileOnSkip) this.profile.add(msg.profileOnSkip);
+ else this.profile.add({ avoidance: 1 });
+ this._proceedToNext();
+ return;
+ }
+
+ if (s === 'playing') {
+ this._skipRequested = true;
+ this._interruptVoice();
+ SFX_UI.skip();
+ Haptic.confirm();
+ const msg = this.currentMsg;
+ if (msg?.isEnding) {
+ this.triggerFinalEnding();
+ } else if (msg?.choices) {
+ this._enterChoosing(msg.choices);
+ } else {
+ this._proceedToNext();
+ }
+ return;
+ }
+
+ if (s === 'responding' || s === 'reacting') {
+ this._skipRequested = true;
+ this._interruptVoice();
+ SFX_UI.skip();
+ Haptic.confirm();
+ return;
+ }
+
+ if (s === 'choosing') {
+ this._skipChoice();
+ return;
+ }
+ }
+
+ _onSwipe(dir) {
+ const s = this.state;
+
+ if (s === 'paused') return;
+
+ if (s === 'ringing') {
+ if (dir === 'left') this._rejectCall(this.currentMsg);
+ else if (dir === 'right') this._answerCall(this.currentMsg);
+ return;
+ }
+
+ if (s === 'notification') {
+ if (dir === 'left') {
+ this._clearReminder();
+ SFX_UI.skip();
+ Haptic.confirm();
+ this.profile.add({ avoidance: 1 });
+ this._proceedToNext();
+ }
+ return;
+ }
+
+ if (s === 'choosing') {
+ this._handleChoice(dir);
+ return;
+ }
+ }
+
+ // ── 长按:重播 / 状态广播 ──
+ _onLongPress() {
+ const s = this.state;
+
+ if (s === 'paused' || s === 'ending') return;
+
+ Haptic.confirm();
+
+ // playing/responding/reacting/choosing → 重播当前消息音频
+ if ((s === 'playing' || s === 'responding' || s === 'reacting' || s === 'choosing') && this.currentMsg) {
+ this._interruptVoice();
+ if (this.currentMsg.audio?.length > 0) {
+ this._playVoiceAsync(this.currentMsg.audio);
+ } else if (this.currentMsg.type === 'wechat_text' && this.currentMsg.text) {
+ TTS.speak(this.currentMsg.text);
+ }
+ return;
+ }
+
+ // idle/notification → 广播未读数
+ TTS.speak(`当前未读${this.vars.UNREAD}条消息`);
+ }
+
+ // ── 暂停 ──
+ _onPause() {
+ const s = this.state;
+ if (s === 'ending' || s === 'paused') return;
+
+ this._pausedState = s;
+ this._interruptVoice();
+ this._clearReminder();
+ Haptic.stop();
+ this.state = 'paused';
+ Snd.playVoice([`${T}/v2_sys_paused_wav_v1.mp3`]);
+ }
+
+ // ── 恢复 ──
+ _onResume() {
+ Snd.stopVoice();
+ const prev = this._pausedState || 'idle';
+ this._pausedState = null;
+
+ // 语音无法断点续播,根据暂停前状态决定恢复行为
+ if (prev === 'choosing' && this._currentChoices) {
+ this.state = 'choosing';
+ SFX_UI.choiceReady();
+ Haptic.confirm();
+ this._startReminder();
+ } else if (prev === 'notification' || prev === 'ringing') {
+ this.state = prev;
+ this._startReminder();
+ } else {
+ // playing/responding/reacting → 无法续播,推进到下一条或回到选择
+ if (this._currentChoices) {
+ this._enterChoosing(this._currentChoices);
+ } else {
+ this._proceedToNext();
+ }
+ }
+ }
+
+ // ── 退出 ──
+ _onExit() {
+ if (this.state !== 'paused') return;
+
+ this._endingTriggered = true;
+ this._interruptVoice();
+ // 退出不需要特殊音频,直接返回开始页
+ setTimeout(() => {
+ const startEl = document.getElementById('start');
+ if (startEl) {
+ startEl.style.opacity = '1';
+ startEl.style.display = 'flex';
+ }
+ }, 2000);
+ }
+
+ // ══════════════════════════════════════════
+ // 结局系统
+ // ══════════════════════════════════════════
+
+ async triggerFinalEnding() {
+ if (this._endingTriggered) return;
+
+ const v = this.vars;
+
+ // 结局E(隐藏)— 优先级 1
+ if (v.ZHOUNAN_DEPTH >= 2 && v.ZHOUNAN_SHARE &&
+ (this.profile.label === 'engagement' || this.profile.label === 'nostalgia')) {
+ return this._triggerEnding('E');
+ }
+
+ // 结局A:妈妈连接 + 自己录音 — 优先级 2
+ if (v.MOM_LINK && v.SELF_RECORD) {
+ return this._triggerEnding('A');
+ }
+
+ // 结局B:拒了阿哲 + 回了周南 — 优先级 3
+ if (v.HE_BACK === false && v.ZHOUNAN_DEPTH >= 1) {
+ return this._triggerEnding('B');
+ }
+
+ // 结局C:全部已读但极少回复 — 优先级 4
+ const read = Object.values(this.msgState).filter(s => s !== 'unread').length;
+ if (read >= MESSAGES.length - 2 && v.REPLY_COUNT <= 2) {
+ return this._triggerEnding('C');
+ }
+
+ // 结局D(兜底)— 优先级 5
+ this._triggerEnding('D');
+ }
+
+ async _triggerEnding(type) {
+ if (this._endingTriggered) return;
+ this._endingTriggered = true;
+
+ this.state = 'ending';
+ this._clearReminder();
+ this._interruptVoice();
+ Snd.stopRingtone();
+ Snd.stopBGM(3000);
+ await sleep(2000);
+
+ dbg(`ending: ${type}`);
+
+ const fn = {
+ A: () => this._endingA(),
+ B: () => this._endingB(),
+ C: () => this._endingC(),
+ D: () => this._endingD(),
+ E: () => this._endingE(),
+ }[type];
+ if (fn) await fn();
+
+ // 结局后播报心理支持热线
+ await sleep(3000);
+ await this._playVoiceAsync([`${T}/v2_sys_hotline_wav_v1.mp3`]);
+ }
+
+ async _endingA() {
+ Snd.startAmbience(`${AMB}/amb_kitchen_morning_v1.mp3`, { vol: 0.3 });
+ await sleep(2000);
+ Snd.playVoice([`${SFX}/sfx_kettle_whistle_v1.mp3`]);
+ await sleep(4000);
+ SFX_UI.ending();
+ this._showCaption('明天再说。');
+ await this._playVoiceAsync([`${T}/v2_ending_a_linxia_wav_v1.mp3`]);
+ }
+
+ async _endingB() {
+ await sleep(2000);
+ SFX_UI.ending();
+ await sleep(1000);
+ this._showCaption('寄出去了。');
+ await this._playVoiceAsync([`${T}/v2_ending_b_linxia_wav_v1.mp3`]);
+ }
+
+ async _endingC() {
+ await sleep(3000);
+ SFX_UI.ending();
+ await sleep(1000);
+ this._showCaption('今晚就这样。');
+ await this._playVoiceAsync([`${T}/v2_ending_c_linxia_wav_v1.mp3`]);
+ }
+
+ async _endingD() {
+ Haptic.system();
+ await sleep(2000);
+ Haptic.ring();
+ await sleep(4000);
+ Snd.startAmbience(`${AMB}/amb_dawn_birds_v1.mp3`, { vol: 0.4 });
+ await sleep(4000);
+ this._showCaption('早上好。');
+ await this._playVoiceAsync([`${T}/v2_ending_d_linxia_wav_v1.mp3`]);
+ }
+
+ async _endingE() {
+ Haptic.message();
+ SFX_UI.notify();
+ await sleep(1500);
+ // 周南秒回:"那我下次出差来北京,请你吃饭好不好。"
+ await this._playVoiceAsync([`${T}/v2_ending_e_zhounan_wav_v1.mp3`]);
+ await sleep(2000);
+ SFX_UI.ending();
+ await sleep(1000);
+ this._showCaption('好。');
+ await this._playVoiceAsync([`${T}/v2_ending_e_linxia_wav_v1.mp3`]);
+ }
+
+ _showCaption(text) {
+ const el = document.getElementById('caption');
+ el.textContent = text;
+ el.style.display = 'flex';
+ }
+}
diff --git a/game1/js/gestures.js b/game1/js/gestures.js
new file mode 100644
index 0000000..8185a57
--- /dev/null
+++ b/game1/js/gestures.js
@@ -0,0 +1,262 @@
+// gestures.js — 触控手势检测器
+//
+// 支持 9 种手势:
+// 单击 · 双击 · 长按(1s) · 左滑 · 右滑 · 上滑 · 下滑
+// 两指长按(1s) · 三指点击
+
+class GestureDetector {
+ constructor(el) {
+ this._el = el;
+ this._handlers = {};
+
+ this._startX = 0;
+ this._startY = 0;
+ this._startTime = 0;
+ this._tapTimer = null;
+ this._tapCount = 0;
+
+ // 长按检测
+ this._longPressTimer = null;
+ this._longPressTriggered = false;
+
+ // 多指检测
+ this._maxTouches = 0; // 本次触摸中同时出现的最大手指数
+ this._multiTouchLPTimer = null; // 两指长按计时器
+ this._multiTouchHandled = false;
+
+ this._bindTouch();
+ this._bindMouse();
+ this._bindKeyboard();
+ }
+
+ on(evt, fn) { this._handlers[evt] = fn; return this; }
+
+ _emit(evt) {
+ dbg(evt);
+ const fn = this._handlers[evt];
+ if (fn) fn();
+ }
+
+ // ── 触摸事件 ──
+ _bindTouch() {
+ const el = this._el;
+
+ el.addEventListener('touchstart', e => {
+ e.preventDefault();
+ const count = e.touches.length;
+
+ // 记录本次触摸出现过的最大手指数
+ if (count > this._maxTouches) this._maxTouches = count;
+
+ if (count === 1) {
+ // 单指:记录起点 + 启动长按计时
+ const t = e.touches[0];
+ this._startX = t.clientX;
+ this._startY = t.clientY;
+ this._startTime = Date.now();
+ this._longPressTriggered = false;
+
+ this._longPressTimer = setTimeout(() => {
+ this._longPressTriggered = true;
+ this._longPressTimer = null;
+ this._emit('longpress');
+ }, 1000);
+
+ } else if (count === 2) {
+ // 两指:取消单指长按,启动两指长按计时
+ this._cancelLongPress();
+ this._multiTouchLPTimer = setTimeout(() => {
+ this._multiTouchHandled = true;
+ this._multiTouchLPTimer = null;
+ this._emit('twofinger_longpress');
+ }, 1000);
+
+ } else if (count >= 3) {
+ // 三指+:取消所有计时
+ this._cancelLongPress();
+ this._cancelMultiTouchLP();
+ }
+ }, { passive: false });
+
+ el.addEventListener('touchmove', e => {
+ e.preventDefault();
+ // 移动超 15px 取消长按
+ if (this._longPressTimer && e.touches.length === 1) {
+ const t = e.touches[0];
+ const dx = t.clientX - this._startX;
+ const dy = t.clientY - this._startY;
+ if (Math.sqrt(dx * dx + dy * dy) > 15) {
+ this._cancelLongPress();
+ }
+ }
+ // 两指移动也取消两指长按
+ if (this._multiTouchLPTimer) {
+ this._cancelMultiTouchLP();
+ }
+ }, { passive: false });
+
+ el.addEventListener('touchend', e => {
+ e.preventDefault();
+
+ // 还有手指在屏幕上,等全部抬起再处理
+ if (e.touches.length > 0) return;
+
+ // 全部手指抬起 — 根据 maxTouches 判定手势类型
+ const max = this._maxTouches;
+
+ // 清理计时器
+ this._cancelLongPress();
+ this._cancelMultiTouchLP();
+
+ // 已在计时器中处理过(长按 / 两指长按)
+ if (this._longPressTriggered || this._multiTouchHandled) {
+ this._resetMultiTouch();
+ return;
+ }
+
+ // 三指点击
+ if (max >= 3) {
+ this._resetMultiTouch();
+ this._emit('threefinger_tap');
+ return;
+ }
+
+ // 两指快速触摸(< 1s)→ 忽略
+ if (max === 2) {
+ this._resetMultiTouch();
+ return;
+ }
+
+ // 单指:正常处理(滑动 / 点击)
+ this._resetMultiTouch();
+ const t = e.changedTouches[0];
+ this._process(t.clientX, t.clientY);
+ }, { passive: false });
+
+ el.addEventListener('touchcancel', () => {
+ this._cancelLongPress();
+ this._cancelMultiTouchLP();
+ this._resetMultiTouch();
+ this._reset();
+ }, { passive: false });
+ }
+
+ // ── 鼠标事件(桌面调试)──
+ _bindMouse() {
+ this._el.addEventListener('mousedown', e => {
+ this._startX = e.clientX;
+ this._startY = e.clientY;
+ this._startTime = Date.now();
+ this._longPressTriggered = false;
+
+ this._longPressTimer = setTimeout(() => {
+ this._longPressTriggered = true;
+ this._longPressTimer = null;
+ this._emit('longpress');
+ }, 1000);
+ });
+ this._el.addEventListener('mouseup', e => {
+ this._cancelLongPress();
+ if (this._longPressTriggered) {
+ this._longPressTriggered = false;
+ return;
+ }
+ this._process(e.clientX, e.clientY);
+ });
+ this._el.addEventListener('mousemove', e => {
+ if (this._longPressTimer) {
+ const dx = e.clientX - this._startX;
+ const dy = e.clientY - this._startY;
+ if (Math.sqrt(dx * dx + dy * dy) > 15) {
+ this._cancelLongPress();
+ }
+ }
+ });
+ }
+
+ // ── 键盘事件(桌面调试)──
+ _bindKeyboard() {
+ document.addEventListener('keydown', e => {
+ switch (e.code) {
+ case 'Space': e.preventDefault(); this._emit('singletap'); break;
+ case 'Enter': e.preventDefault(); this._emit('doubletap'); break;
+ case 'ArrowLeft': e.preventDefault(); this._emit('swipe_left'); break;
+ case 'ArrowRight': e.preventDefault(); this._emit('swipe_right'); break;
+ case 'ArrowUp': e.preventDefault(); this._emit('swipe_up'); break;
+ case 'ArrowDown': e.preventDefault(); this._emit('swipe_down'); break;
+ case 'KeyL': e.preventDefault(); this._emit('longpress'); break;
+ case 'KeyP': e.preventDefault(); this._emit('twofinger_longpress'); break;
+ case 'KeyQ': e.preventDefault(); this._emit('threefinger_tap'); break;
+ }
+ });
+ }
+
+ // ── 手势判定(单指) ──
+ _process(endX, endY) {
+ const dx = endX - this._startX;
+ const dy = endY - this._startY;
+ const dist = Math.sqrt(dx * dx + dy * dy);
+ const dt = Date.now() - this._startTime;
+
+ // 滑动:距离 > 35px
+ if (dist > 35) {
+ const angle = Math.abs(Math.atan2(dy, dx) * 180 / Math.PI);
+ let dir = null;
+
+ if (angle < 45) dir = 'right';
+ else if (angle > 135) dir = 'left';
+ else if (dy < 0 && Math.abs(dy) > Math.abs(dx)) dir = 'up';
+ else if (dy > 0 && Math.abs(dy) > Math.abs(dx)) dir = 'down';
+
+ if (dir) {
+ this._tapCount = 0;
+ clearTimeout(this._tapTimer);
+ this._emit(`swipe_${dir}`);
+ return;
+ }
+ }
+
+ // 点击:距离 < 20px,时间 < 500ms
+ if (dist < 20 && dt < 500) {
+ this._tapCount++;
+ if (this._tapCount === 1) {
+ this._tapTimer = setTimeout(() => {
+ this._tapCount = 0;
+ this._emit('singletap');
+ }, 250);
+ } else if (this._tapCount >= 2) {
+ clearTimeout(this._tapTimer);
+ this._tapCount = 0;
+ this._emit('doubletap');
+ }
+ }
+ }
+
+ // ── 工具方法 ──
+
+ _cancelLongPress() {
+ if (this._longPressTimer) {
+ clearTimeout(this._longPressTimer);
+ this._longPressTimer = null;
+ }
+ }
+
+ _cancelMultiTouchLP() {
+ if (this._multiTouchLPTimer) {
+ clearTimeout(this._multiTouchLPTimer);
+ this._multiTouchLPTimer = null;
+ }
+ }
+
+ _resetMultiTouch() {
+ this._maxTouches = 0;
+ this._multiTouchHandled = false;
+ }
+
+ _reset() {
+ clearTimeout(this._tapTimer);
+ this._tapCount = 0;
+ this._longPressTriggered = false;
+ this._resetMultiTouch();
+ }
+}
diff --git a/game1/js/haptics.js b/game1/js/haptics.js
new file mode 100644
index 0000000..02bbbce
--- /dev/null
+++ b/game1/js/haptics.js
@@ -0,0 +1,35 @@
+// haptics.js — 震动反馈(Android Chrome 支持,iOS 降级为无震动)
+
+const Haptic = {
+ _ok: typeof navigator !== 'undefined' && !!navigator.vibrate,
+
+ _v(pattern) {
+ if (this._ok) navigator.vibrate(pattern);
+ },
+
+ // 来电震动(长震 + 停顿,循环由来电循环调用)
+ ring() { this._v([400, 200, 400]); },
+
+ // 微信消息(两短震)
+ message() { this._v([80, 60, 80]); },
+
+ // 确认操作
+ confirm() { this._v(60); },
+
+ // 拒绝 / 挂断
+ reject() { this._v([100, 50, 100, 50, 100]); },
+
+ // 进入细回复模式
+ detailMode() { this._v([30, 30, 60]); },
+
+ // 选项选中
+ select() { this._v(80); },
+
+ // 系统通知
+ system() { this._v([200, 100, 100, 100, 200]); },
+
+ // 错误 / 无效手势
+ error() { this._v([50, 30, 50]); },
+
+ stop() { if (this._ok) navigator.vibrate(0); },
+};
diff --git a/game1/js/main.js b/game1/js/main.js
new file mode 100644
index 0000000..fbd60bc
--- /dev/null
+++ b/game1/js/main.js
@@ -0,0 +1,55 @@
+// main.js — 游戏入口
+
+let engine = null;
+let gestures = null;
+
+async function main() {
+ TTS.init();
+
+ // 初始化音频(必须在用户手势后)
+ await Snd.init();
+
+ // 请求屏幕常亮
+ try {
+ if ('wakeLock' in navigator) {
+ window._wakeLock = await navigator.wakeLock.request('screen');
+ }
+ } catch (_) {}
+
+ // 绑定手势
+ const gameEl = document.getElementById('game');
+ gestures = new GestureDetector(gameEl);
+
+ // 创建并启动引擎
+ engine = new GameEngine();
+ engine.init(gestures);
+ await engine.start();
+
+ if (DEV) {
+ document.getElementById('dbg').style.display = 'block';
+ document.title = '林夏 [DEV]';
+ }
+}
+
+// 触摸启动(手机)
+document.getElementById('start').addEventListener('touchend', async function onStart(e) {
+ e.preventDefault();
+ this.removeEventListener('touchend', onStart);
+ this.style.transition = 'opacity 0.8s';
+ this.style.opacity = '0';
+ setTimeout(() => { this.style.display = 'none'; }, 800);
+ await main();
+}, { once: true });
+
+// 鼠标点击(电脑调试)
+document.getElementById('start').addEventListener('click', async function onClick() {
+ this.removeEventListener('click', onClick);
+ this.style.transition = 'opacity 0.8s';
+ this.style.opacity = '0';
+ setTimeout(() => { this.style.display = 'none'; }, 800);
+ await main();
+}, { once: true });
+
+// 防止页面滚动/缩放
+document.addEventListener('touchmove', e => e.preventDefault(), { passive: false });
+document.addEventListener('gesturestart', e => e.preventDefault());
diff --git a/game1/js/profile.js b/game1/js/profile.js
new file mode 100644
index 0000000..c5d6024
--- /dev/null
+++ b/game1/js/profile.js
@@ -0,0 +1,39 @@
+// profile.js — 行为画像追踪器(avoidance / engagement / nostalgia)
+
+class ProfileTracker {
+ constructor() {
+ this.scores = { avoidance: 0, engagement: 0, nostalgia: 0 };
+ }
+
+ add(effects) {
+ if (!effects) return;
+ for (const k of ['avoidance', 'engagement', 'nostalgia']) {
+ if (effects[k]) this.scores[k] += effects[k];
+ }
+ }
+
+ // 漏接电话
+ onMissedCall() { this.add({ avoidance: 2 }); }
+ // 单点回"嗯"
+ onSimpleReply() { this.add({ avoidance: 1 }); }
+ // 双指上滑已读不回
+ onReadNoReply() { this.add({ avoidance: 2 }); }
+ // 进入细回复
+ onDetailReply() { this.add({ engagement: 2 }); }
+ // 细回复选"主动追问"(engagement 方向)
+ onEngage() { this.add({ engagement: 2 }); }
+ // 细回复选"分享自己"
+ onShare() { this.add({ engagement: 1, nostalgia: 1 }); }
+ // 反复重听同一条
+ onReplay() { this.add({ nostalgia: 2 }); }
+ // 听完长语音未回应
+ onListenNoReact(){ this.add({ avoidance: 1, nostalgia: 1 }); }
+
+ // 获取最终画像标签
+ get label() {
+ const s = this.scores;
+ if (s.engagement >= s.avoidance && s.engagement >= s.nostalgia) return 'engagement';
+ if (s.nostalgia >= s.avoidance) return 'nostalgia';
+ return 'avoidance';
+ }
+}
diff --git a/game1/js/story.js b/game1/js/story.js
new file mode 100644
index 0000000..de63bc0
--- /dev/null
+++ b/game1/js/story.js
@@ -0,0 +1,674 @@
+// story.js — 消息数据 v2(CosyVoice2 语音 + 多轮对话)
+//
+// 选择方向的情感含义(全局一致):
+// 单击(tap) → 简单回应
+// 双击 → 不回复 / 跳过
+// 右滑(right) → 温暖 / 接受 / 开放
+// 左滑(left) → 冷淡 / 拒绝 / 疏远
+// 上滑(up) → 好奇 / 主动 / 追问
+// 下滑(down) → 沉默 / 退缩
+//
+// 每个 choice 结构:
+// linxia: 林夏回应的音频路径(null/缺省 = 沉默)
+// npcReact: NPC反应音频数组(缺省 = 无反应)
+// isSilence: 沉默选项标记
+// state: 剧情状态变更
+// profile: 画像分数变更
+// followUp: 可选的下一轮对话 { choices: { ... } }
+
+const A = 'mp3';
+const T = `${A}/tts`; // CosyVoice2 生成的所有语音
+const MUS = `${A}/music`;
+const SFX = `${A}/sfx`;
+const AMB = `${A}/ambience`;
+
+// ── 快捷函数 ──
+const npc = f => `${T}/${f}`; // NPC Round 1 台词
+const lx = f => `${T}/${f}`; // 林夏回应
+const rx = f => `${T}/${f}`; // NPC 反应
+
+const MESSAGES = [
+
+ // ══════════════════════════════════════════
+ // 01 · 妈妈 · 电话
+ // ══════════════════════════════════════════
+ {
+ id: 1, type: 'call',
+ sender: 'mom', senderName: '妈妈',
+ ringtone: `${MUS}/lv11_ring_mom_v1.mp3`,
+ audio: [npc('lv11_2103_mom_call_01_wav_v1.mp3')],
+ autoReply: lx('v2_msg01_linxia_auto_wav_v1.mp3'),
+ stateOnAnswer: { MOM_LINK: true },
+ profileOnMiss: { avoidance: 2 },
+ profileOnAnswer: { engagement: 1 },
+ },
+
+ // ══════════════════════════════════════════
+ // 02 · 小美 · 语音 ×3(教学关)
+ // ══════════════════════════════════════════
+ {
+ id: 2, type: 'wechat_voice',
+ sender: 'xiaomei', senderName: '小美',
+ audio: [
+ npc('lv11_2115_xiaomei_voice_01_wav_v1.mp3'),
+ npc('lv11_2115_xiaomei_voice_02_wav_v1.mp3'),
+ npc('lv11_2115_xiaomei_voice_03_wav_v1.mp3'),
+ ],
+ choices: {
+ right: {
+ linxia: lx('v2_msg02_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg02_react_warm_wav_v1.mp3')],
+ profile: { engagement: 2 },
+ },
+ left: {
+ linxia: lx('v2_msg02_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg02_react_cold_wav_v1.mp3')],
+ profile: { avoidance: 1 },
+ },
+ up: {
+ linxia: lx('v2_msg02_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg02_react_curious_wav_v1.mp3')],
+ profile: { engagement: 1 },
+ followUp: {
+ choices: {
+ right: {
+ linxia: lx('v2_msg02_r3_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg02_r3_react_wav_v1.mp3')],
+ profile: { nostalgia: 1 },
+ },
+ },
+ },
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg02_react_silent_wav_v1.mp3')],
+ profile: { avoidance: 2 },
+ },
+ tap: {
+ linxia: lx('v2_msg02_linxia_tap_wav_v1.mp3'),
+ npcReact: [rx('v2_msg02_react_tap_wav_v1.mp3')],
+ },
+ },
+ },
+
+ // ══════════════════════════════════════════
+ // 03 · 外卖小哥 · 电话
+ // ══════════════════════════════════════════
+ {
+ id: 3, type: 'call',
+ sender: 'delivery', senderName: '外卖小哥',
+ ringtone: `${MUS}/lv11_ring_delivery_v1.mp3`,
+ audio: [npc('lv11_2120_delivery_call_01_wav_v1.mp3')],
+ autoReply: lx('v2_msg03_linxia_auto_wav_v1.mp3'),
+ profileOnMiss: { avoidance: 1 },
+ profileOnAnswer: { engagement: 1 },
+ },
+
+ // ══════════════════════════════════════════
+ // 04 · 阿哲 · 语音(前男友要东西)
+ // ══════════════════════════════════════════
+ {
+ id: 4, type: 'wechat_voice',
+ sender: 'azhe', senderName: '阿哲',
+ audio: [npc('lv11_2130_azhe_voice_01_wav_v1.mp3')],
+ choices: {
+ right: {
+ linxia: lx('v2_msg04_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg04_react_warm_wav_v1.mp3')],
+ profile: { engagement: 2 },
+ followUp: {
+ // 阿哲问"你呢?"后
+ choices: {
+ right: {
+ linxia: lx('v2_msg04_r3w_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg04_r3w_react_warm_wav_v1.mp3')],
+ },
+ left: {
+ linxia: lx('v2_msg04_r3w_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg04_r3w_react_cold_wav_v1.mp3')],
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg04_r3w_react_silent_wav_v1.mp3')],
+ },
+ },
+ },
+ },
+ left: {
+ linxia: lx('v2_msg04_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg04_react_cold_wav_v1.mp3')],
+ state: { HE_BACK: false },
+ profile: { avoidance: 1 },
+ },
+ up: {
+ linxia: lx('v2_msg04_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg04_react_curious_wav_v1.mp3')],
+ profile: { engagement: 2 },
+ followUp: {
+ // 阿哲说"不太方便"后
+ choices: {
+ up: {
+ linxia: lx('v2_msg04_r3c_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg04_r3c_react_curious_wav_v1.mp3')],
+ state: { HE_BACK: false },
+ },
+ right: {
+ linxia: lx('v2_msg04_r3c_linxia_warm_wav_v1.mp3'),
+ },
+ },
+ },
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg04_react_silent_wav_v1.mp3')],
+ state: { HE_BACK: null },
+ profile: { avoidance: 2 },
+ },
+ tap: {
+ linxia: lx('v2_msg04_linxia_tap_wav_v1.mp3'),
+ npcReact: [rx('v2_msg04_react_tap_wav_v1.mp3')],
+ state: { HE_BACK: false },
+ profile: { engagement: 1 },
+ },
+ },
+ },
+
+ // ══════════════════════════════════════════
+ // 05 · HR王姐 · 语音
+ // ══════════════════════════════════════════
+ {
+ id: 5, type: 'wechat_voice',
+ sender: 'hr', senderName: 'HR王姐',
+ audio: [npc('lv11_2145_hr_voice_01_wav_v1.mp3')],
+ choices: {
+ right: {
+ linxia: lx('v2_msg05_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg05_react_warm_wav_v1.mp3')],
+ profile: { engagement: 1 },
+ },
+ left: {
+ linxia: lx('v2_msg05_linxia_cold_wav_v1.mp3'),
+ },
+ up: {
+ linxia: lx('v2_msg05_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg05_react_curious_wav_v1.mp3')],
+ profile: { engagement: 2 },
+ },
+ down: {
+ isSilence: true,
+ profile: { avoidance: 1 },
+ },
+ tap: {
+ linxia: lx('v2_msg05_linxia_tap_wav_v1.mp3'),
+ },
+ },
+ },
+
+ // ══════════════════════════════════════════
+ // 06 · 安安 · 语音(酒吧邀约)
+ // ══════════════════════════════════════════
+ {
+ id: 6, type: 'wechat_voice',
+ sender: 'anan', senderName: '安安',
+ audio: [npc('lv11_2200_anan_voice_01_wav_v1.mp3')],
+ choices: {
+ right: {
+ linxia: lx('v2_msg06_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg06_react_warm_wav_v1.mp3')],
+ profile: { engagement: 2 },
+ },
+ left: {
+ linxia: lx('v2_msg06_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg06_react_cold_wav_v1.mp3')],
+ },
+ up: {
+ linxia: lx('v2_msg06_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg06_react_curious_wav_v1.mp3')],
+ profile: { engagement: 1 },
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg06_react_silent_wav_v1.mp3')],
+ profile: { avoidance: 1 },
+ },
+ tap: {
+ linxia: lx('v2_msg06_linxia_tap_wav_v1.mp3'),
+ npcReact: [rx('v2_msg06_react_tap_wav_v1.mp3')],
+ },
+ },
+ },
+
+ // ══════════════════════════════════════════
+ // 07 · 寝室群 · 5条语音
+ // ══════════════════════════════════════════
+ {
+ id: 7, type: 'wechat_voice',
+ sender: 'dorm', senderName: '寝室群',
+ audio: [
+ npc('lv11_2215_dorm_a_voice_01_wav_v1.mp3'),
+ npc('lv11_2215_dorm_b_voice_01_wav_v1.mp3'),
+ npc('lv11_2215_dorm_c_voice_01_wav_v1.mp3'),
+ npc('lv11_2215_dorm_a_voice_02_wav_v1.mp3'),
+ npc('lv11_2215_dorm_b_voice_02_wav_v1.mp3'),
+ ],
+ choices: {
+ right: {
+ linxia: lx('v2_msg07_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg07_react_warm_wav_v1.mp3')],
+ state: { GROUP_REPLY: true },
+ profile: { engagement: 2 },
+ followUp: {
+ // 室友A问"你最近怎么样"后
+ choices: {
+ right: {
+ linxia: lx('v2_msg07_r3_linxia_warm_wav_v1.mp3'),
+ profile: { engagement: 1 },
+ },
+ left: {
+ linxia: lx('v2_msg07_r3_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg07_r3_react_cold_wav_v1.mp3')],
+ },
+ },
+ },
+ },
+ left: {
+ linxia: lx('v2_msg07_linxia_cold_wav_v1.mp3'),
+ profile: { avoidance: 1 },
+ },
+ up: {
+ linxia: lx('v2_msg07_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg07_react_curious_wav_v1.mp3')],
+ state: { GROUP_REPLY: true },
+ profile: { engagement: 2 },
+ },
+ down: {
+ isSilence: true,
+ profile: { avoidance: 2 },
+ },
+ tap: {
+ linxia: lx('v2_linxia_generic_hmm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg07_react_tap_wav_v1.mp3')],
+ state: { GROUP_REPLY: true },
+ },
+ },
+ },
+
+ // ══════════════════════════════════════════
+ // 08 · 未知号码 · 电话(悬疑钩子)
+ // ══════════════════════════════════════════
+ {
+ id: 8, type: 'call',
+ sender: 'unknown', senderName: '未知号码',
+ ringtone: `${MUS}/lv11_ring_unknown_v1.mp3`,
+ audio: [`${SFX}/sfx_breathing_unknown_v1.mp3`],
+ autoHangup: true,
+ sfxAfter: `${SFX}/sfx_heartbeat_fast_v1.mp3`,
+ profileOnMiss: { avoidance: 1 },
+ profileOnAnswer: { nostalgia: 1 },
+ },
+
+ // ══════════════════════════════════════════
+ // 09 · 小美(醉)· 4段语音
+ // ══════════════════════════════════════════
+ {
+ id: 9, type: 'wechat_voice',
+ sender: 'xiaomei', senderName: '小美',
+ audio: [
+ npc('lv11_2245_xiaomei_voice_01_seg1_wav_v1.mp3'),
+ npc('lv11_2245_xiaomei_voice_01_seg2_wav_v1.mp3'),
+ npc('lv11_2245_xiaomei_voice_01_seg3_wav_v1.mp3'),
+ npc('lv11_2245_xiaomei_voice_01_seg4_wav_v1.mp3'),
+ ],
+ bgm: `${MUS}/lv11_bgm_m2_suspense_v1.mp3`,
+ choices: {
+ right: {
+ linxia: lx('v2_msg09_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg09_react_warm_wav_v1.mp3')],
+ profile: { engagement: 2 },
+ },
+ left: {
+ linxia: lx('v2_msg09_linxia_cold_wav_v1.mp3'),
+ profile: { avoidance: 1 },
+ },
+ up: {
+ linxia: lx('v2_msg09_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg09_react_curious_wav_v1.mp3')],
+ profile: { engagement: 2 },
+ followUp: {
+ // 小美说"怕你恨我"后
+ choices: {
+ right: {
+ linxia: lx('v2_msg09_r3_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg09_r3_react_warm_wav_v1.mp3')],
+ },
+ left: {
+ linxia: lx('v2_msg09_r3_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg09_r3_react_cold_wav_v1.mp3')],
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg09_r3_react_silent_q_wav_v1.mp3')],
+ },
+ },
+ },
+ },
+ down: {
+ isSilence: true,
+ profile: { avoidance: 2, nostalgia: 1 },
+ },
+ tap: {
+ linxia: lx('v2_msg09_linxia_tap_wav_v1.mp3'),
+ npcReact: [rx('v2_msg09_react_tap_wav_v1.mp3')],
+ profile: { nostalgia: 1 },
+ },
+ },
+ },
+
+ // ══════════════════════════════════════════
+ // 10 · 妈妈 · 文字消息(多层嵌套对话)
+ // ══════════════════════════════════════════
+ {
+ id: 10, type: 'wechat_text',
+ sender: 'mom', senderName: '妈妈',
+ text: '睡了吗?',
+ choices: {
+ right: {
+ linxia: lx('v2_msg10_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg10_react_warm_wav_v1.mp3')],
+ state: { MOM_LINK: true },
+ profile: { engagement: 2 },
+ followUp: {
+ // 妈妈问"今天怎么样"后
+ choices: {
+ right: {
+ linxia: lx('v2_msg10_rw_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg10_rw_react_warm_wav_v1.mp3')],
+ },
+ up: {
+ linxia: lx('v2_msg10_rw_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg10_rw_react_curious_wav_v1.mp3')],
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg10_rw_react_silent_wav_v1.mp3')],
+ profile: { avoidance: 1 },
+ },
+ },
+ },
+ },
+ left: {
+ linxia: lx('v2_msg10_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg10_react_cold_wav_v1.mp3')],
+ state: { MOM_LINK: true },
+ profile: { avoidance: 1 },
+ },
+ up: {
+ linxia: lx('v2_msg10_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg10_call_mom_wav_v1.mp3')],
+ state: { MOM_LINK: true },
+ profile: { engagement: 3 },
+ followUp: {
+ // 妈妈打来电话说"怎么了夏夏?"后
+ choices: {
+ right: {
+ linxia: lx('v2_msg10_call_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg10_call_react_warm_wav_v1.mp3')],
+ },
+ up: {
+ linxia: lx('v2_msg10_call_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg10_call_react_curious_wav_v1.mp3')],
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg10_call_react_down_wav_v1.mp3')],
+ profile: { avoidance: 1 },
+ },
+ },
+ },
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg10_react_silent_wav_v1.mp3')],
+ profile: { avoidance: 2 },
+ },
+ tap: {
+ linxia: lx('v2_msg10_linxia_tap_wav_v1.mp3'),
+ npcReact: [rx('v2_msg10_react_tap_wav_v1.mp3')],
+ state: { MOM_LINK: true },
+ },
+ },
+ },
+
+ // ══════════════════════════════════════════
+ // 11 · 阿哲 · 电话(关键分支:来不来)
+ // ══════════════════════════════════════════
+ {
+ id: 11, type: 'call',
+ sender: 'azhe', senderName: '阿哲',
+ ringtone: `${MUS}/lv11_ring_azhe_v1.mp3`,
+ audio: [npc('lv11_2315_azhe_call_01_wav_v1.mp3')],
+ profileOnMiss: { avoidance: 2 },
+ profileOnAnswer: { engagement: 1 },
+ choices: {
+ right: {
+ linxia: lx('v2_msg11_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg11_react_warm_wav_v1.mp3')],
+ state: { HE_BACK: true },
+ },
+ left: {
+ linxia: lx('v2_msg11_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg11_react_cold_wav_v1.mp3')],
+ state: { HE_BACK: false },
+ },
+ up: {
+ linxia: lx('v2_msg11_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg11_react_curious_wav_v1.mp3')],
+ followUp: {
+ choices: {
+ right: {
+ linxia: lx('v2_msg11_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg11_react_warm_wav_v1.mp3')],
+ state: { HE_BACK: true },
+ },
+ left: {
+ linxia: lx('v2_msg11_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg11_react_cold_wav_v1.mp3')],
+ state: { HE_BACK: false },
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg11_r3_react_silent_wav_v1.mp3')],
+ state: { HE_BACK: false },
+ profile: { avoidance: 1 },
+ },
+ },
+ },
+ },
+ down: {
+ isSilence: true,
+ state: { HE_BACK: false },
+ },
+ tap: {
+ linxia: lx('v2_msg11_linxia_tap_wav_v1.mp3'),
+ npcReact: [rx('v2_msg11_react_tap_wav_v1.mp3')],
+ state: { HE_BACK: false },
+ },
+ },
+ },
+
+ // ══════════════════════════════════════════
+ // 12 · 周南 · 语音(老同学)
+ // ══════════════════════════════════════════
+ {
+ id: 12, type: 'wechat_voice',
+ sender: 'zhounan', senderName: '周南',
+ audio: [npc('lv11_2330_zhounan_voice_01_wav_v1.mp3')],
+ choices: {
+ right: {
+ linxia: lx('v2_msg12_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg12_react_warm_wav_v1.mp3')],
+ state: { ZHOUNAN_DEPTH: 1 },
+ profile: { engagement: 2, nostalgia: 1 },
+ },
+ left: {
+ linxia: lx('v2_msg12_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg12_react_cold_wav_v1.mp3')],
+ state: { ZHOUNAN_DEPTH: 1 },
+ profile: { nostalgia: 1 },
+ },
+ up: {
+ linxia: lx('v2_msg12_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg12_react_curious_wav_v1.mp3')],
+ state: { ZHOUNAN_DEPTH: 1 },
+ profile: { nostalgia: 2 },
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg12_react_silent_wav_v1.mp3')],
+ profile: { nostalgia: 1 },
+ },
+ tap: {
+ linxia: lx('v2_msg12_linxia_tap_wav_v1.mp3'),
+ npcReact: [rx('v2_msg12_react_tap_wav_v1.mp3')],
+ state: { ZHOUNAN_DEPTH: 1 },
+ },
+ },
+ },
+
+ // ══════════════════════════════════════════
+ // 13 · 周南 · 4条长语音(他的十年)
+ // ══════════════════════════════════════════
+ {
+ id: 13, type: 'wechat_voice',
+ sender: 'zhounan', senderName: '周南',
+ audio: [
+ npc('lv11_2345_zhounan_voice_01_wav_v1.mp3'),
+ npc('lv11_2345_zhounan_voice_02_wav_v1.mp3'),
+ npc('lv11_2345_zhounan_voice_03_wav_v1.mp3'),
+ npc('lv11_2345_zhounan_voice_04_wav_v1.mp3'),
+ ],
+ bgm: `${MUS}/lv11_bgm_m3_zhounan_v1.mp3`,
+ choices: {
+ right: {
+ linxia: lx('v2_msg13_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg13_react_warm_wav_v1.mp3')],
+ state: { ZHOUNAN_DEPTH_ADD: 1 },
+ profile: { engagement: 2 },
+ followUp: {
+ // 周南问"你在北京过得好吗"后
+ choices: {
+ right: {
+ linxia: lx('v2_msg13_r3_linxia_warm_wav_v1.mp3'),
+ npcReact: [rx('v2_msg13_r3_react_warm_wav_v1.mp3')],
+ state: { ZHOUNAN_SHARE: true },
+ },
+ left: {
+ linxia: lx('v2_msg13_r3_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg13_r3_react_cold_wav_v1.mp3')],
+ },
+ up: {
+ linxia: lx('v2_msg13_r3_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg13_r3_react_curious_wav_v1.mp3')],
+ },
+ },
+ },
+ },
+ left: {
+ linxia: lx('v2_msg13_linxia_cold_wav_v1.mp3'),
+ npcReact: [rx('v2_msg13_react_cold_wav_v1.mp3')],
+ state: { ZHOUNAN_DEPTH_ADD: 1 },
+ profile: { nostalgia: 1 },
+ },
+ up: {
+ linxia: lx('v2_msg13_linxia_curious_wav_v1.mp3'),
+ npcReact: [rx('v2_msg13_react_curious_wav_v1.mp3')],
+ state: { ZHOUNAN_DEPTH_ADD: 1, ZHOUNAN_SHARE: true },
+ profile: { nostalgia: 2, engagement: 1 },
+ },
+ down: {
+ isSilence: true,
+ npcReact: [rx('v2_msg13_react_silent_wav_v1.mp3')],
+ profile: { avoidance: 1 },
+ },
+ tap: {
+ linxia: lx('v2_msg13_linxia_tap_wav_v1.mp3'),
+ npcReact: [rx('v2_msg13_react_tap_wav_v1.mp3')],
+ state: { ZHOUNAN_DEPTH_ADD: 1 },
+ },
+ },
+ },
+
+ // ══════════════════════════════════════════
+ // 14 · 妈妈 · 第二次电话
+ // ══════════════════════════════════════════
+ {
+ id: 14, type: 'call',
+ sender: 'mom', senderName: '妈妈',
+ ringtone: `${MUS}/lv11_ring_mom_v1.mp3`,
+ audio: [npc('lv11_0015_mom_call_01_wav_v1.mp3')],
+ autoReply: lx('v2_msg14_linxia_auto_wav_v1.mp3'),
+ autoReact: {
+ condition: 'MOM_LINK',
+ true: rx('v2_msg14_react_linked_wav_v1.mp3'),
+ false: rx('v2_msg14_react_unlinked_wav_v1.mp3'),
+ },
+ autoReactPreamble: rx('v2_msg14_mom_ask_wav_v1.mp3'),
+ stateOnAnswer: { MOM_LINK: true },
+ profileOnMiss: { avoidance: 2 },
+ profileOnAnswer: { engagement: 2 },
+ },
+
+ // ══════════════════════════════════════════
+ // 15 · 时光胶囊 · 系统通知
+ // ══════════════════════════════════════════
+ {
+ id: 15, type: 'system_notification',
+ sender: 'self', senderName: '时光胶囊',
+ systemAudio: npc('lv11_sys_memo_01_wav_v1.mp3'),
+ audioByProfile: {
+ avoidance: npc('lv11_0030_self3y_voice_02_wav_v1.mp3'),
+ engagement: npc('lv11_0030_self3y_voice_01_wav_v1.mp3'),
+ nostalgia: npc('lv11_0030_self3y_voice_03_wav_v1.mp3'),
+ },
+ stateOnPlay: { SELF_RECORD: true },
+ profileOnPlay: { nostalgia: 2 },
+ profileOnSkip: { avoidance: 2 },
+ },
+
+ // ══════════════════════════════════════════
+ // 16 · 妈妈 · 长语音 ×8(结局前奏)
+ // ══════════════════════════════════════════
+ {
+ id: 16, type: 'wechat_voice',
+ sender: 'mom', senderName: '妈妈',
+ audio: [
+ npc('lv11_0200_mom_voice_01_seg1_wav_v1.mp3'),
+ npc('lv11_0200_mom_voice_01_seg2_wav_v1.mp3'),
+ npc('lv11_0200_mom_voice_01_seg3_wav_v1.mp3'),
+ npc('lv11_0200_mom_voice_01_seg4_wav_v1.mp3'),
+ npc('lv11_0200_mom_voice_01_seg5_wav_v1.mp3'),
+ npc('lv11_0200_mom_voice_01_seg6_wav_v1.mp3'),
+ npc('lv11_0200_mom_voice_01_seg7_wav_v1.mp3'),
+ npc('lv11_0200_mom_voice_01_seg8_wav_v1.mp3'),
+ ],
+ bgm: `${MUS}/lv11_bgm_m4_ending_v1.mp3`,
+ isEnding: true,
+ requiresMomLink: true,
+ },
+];
+
+// 角色信息
+const SENDERS = {
+ mom: { name: '妈妈' },
+ azhe: { name: '阿哲' },
+ xiaomei: { name: '小美' },
+ delivery: { name: '外卖小哥' },
+ hr: { name: 'HR王姐' },
+ anan: { name: '安安' },
+ dorm: { name: '寝室群' },
+ unknown: { name: '未知号码' },
+ zhounan: { name: '周南' },
+ self: { name: '时光胶囊' },
+};
+
+const AMBIENCE_DEFAULT = `${AMB}/amb_apartment_night_01_v1.mp3`;
diff --git a/game1/js/utils.js b/game1/js/utils.js
new file mode 100644
index 0000000..29a1f93
--- /dev/null
+++ b/game1/js/utils.js
@@ -0,0 +1,38 @@
+// utils.js — 工具函数
+
+const DEV = new URLSearchParams(location.search).has('dev');
+
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+function dbg(msg) {
+ const el = document.getElementById('dbg');
+ if (el && el.style.display !== 'none') el.textContent = msg;
+}
+
+// TTS — 仅用于朗读文字消息内容,不用于任何 UI 反馈
+const TTS = {
+ _voices: [],
+ init() {
+ if (!window.speechSynthesis) return;
+ const load = () => { this._voices = speechSynthesis.getVoices(); };
+ load();
+ speechSynthesis.onvoiceschanged = load;
+ },
+ _pick() {
+ return this._voices.find(v => v.lang.startsWith('zh') || v.name.includes('Chinese'))
+ || this._voices[0] || null;
+ },
+ speak(text, { rate = 0.85, volume = 0.8 } = {}) {
+ return new Promise(resolve => {
+ if (!window.speechSynthesis) { resolve(); return; }
+ speechSynthesis.cancel();
+ const u = new SpeechSynthesisUtterance(text);
+ u.lang = 'zh-CN'; u.rate = rate; u.volume = volume;
+ const v = this._pick();
+ if (v) u.voice = v;
+ u.onend = resolve; u.onerror = resolve;
+ speechSynthesis.speak(u);
+ });
+ },
+ stop() { if (window.speechSynthesis) speechSynthesis.cancel(); },
+};
diff --git a/game1/manifest.json b/game1/manifest.json
new file mode 100644
index 0000000..af6abcf
--- /dev/null
+++ b/game1/manifest.json
@@ -0,0 +1,13 @@
+{
+ "name": "林夏",
+ "short_name": "林夏",
+ "description": "一款全黑屏的情感叙事游戏。今晚16条消息,你接谁不接谁,决定明天的自己。",
+ "start_url": "/game/",
+ "display": "fullscreen",
+ "orientation": "portrait",
+ "background_color": "#000000",
+ "theme_color": "#000000",
+ "icons": [
+ { "src": "icon.png", "sizes": "192x192", "type": "image/png" }
+ ]
+}
diff --git a/game1/mp3/ambience/amb_apartment_night_01_v1.mp3 b/game1/mp3/ambience/amb_apartment_night_01_v1.mp3
new file mode 100644
index 0000000..a9f24a6
Binary files /dev/null and b/game1/mp3/ambience/amb_apartment_night_01_v1.mp3 differ
diff --git a/game1/mp3/ambience/amb_apartment_night_02_v1.mp3 b/game1/mp3/ambience/amb_apartment_night_02_v1.mp3
new file mode 100644
index 0000000..53cbc0c
Binary files /dev/null and b/game1/mp3/ambience/amb_apartment_night_02_v1.mp3 differ
diff --git a/game1/mp3/ambience/amb_bar_loud_01_v1.mp3 b/game1/mp3/ambience/amb_bar_loud_01_v1.mp3
new file mode 100644
index 0000000..91dc7fa
Binary files /dev/null and b/game1/mp3/ambience/amb_bar_loud_01_v1.mp3 differ
diff --git a/game1/mp3/ambience/amb_dawn_birds_v1.mp3 b/game1/mp3/ambience/amb_dawn_birds_v1.mp3
new file mode 100644
index 0000000..a128b9a
Binary files /dev/null and b/game1/mp3/ambience/amb_dawn_birds_v1.mp3 differ
diff --git a/game1/mp3/ambience/amb_kitchen_morning_v1.mp3 b/game1/mp3/ambience/amb_kitchen_morning_v1.mp3
new file mode 100644
index 0000000..b078374
Binary files /dev/null and b/game1/mp3/ambience/amb_kitchen_morning_v1.mp3 differ
diff --git a/game1/mp3/ambience/amb_rain_window_01_v1.mp3 b/game1/mp3/ambience/amb_rain_window_01_v1.mp3
new file mode 100644
index 0000000..473affa
Binary files /dev/null and b/game1/mp3/ambience/amb_rain_window_01_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_bgm_m1_main_loop_v1.mp3 b/game1/mp3/music/lv11_bgm_m1_main_loop_v1.mp3
new file mode 100644
index 0000000..3d01a59
Binary files /dev/null and b/game1/mp3/music/lv11_bgm_m1_main_loop_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_bgm_m2_suspense_v1.mp3 b/game1/mp3/music/lv11_bgm_m2_suspense_v1.mp3
new file mode 100644
index 0000000..cd469bc
Binary files /dev/null and b/game1/mp3/music/lv11_bgm_m2_suspense_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_bgm_m3_zhounan_v1.mp3 b/game1/mp3/music/lv11_bgm_m3_zhounan_v1.mp3
new file mode 100644
index 0000000..81b2d1e
Binary files /dev/null and b/game1/mp3/music/lv11_bgm_m3_zhounan_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_bgm_m4_ending_v1.mp3 b/game1/mp3/music/lv11_bgm_m4_ending_v1.mp3
new file mode 100644
index 0000000..0fa430b
Binary files /dev/null and b/game1/mp3/music/lv11_bgm_m4_ending_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_ring_anan_v1.mp3 b/game1/mp3/music/lv11_ring_anan_v1.mp3
new file mode 100644
index 0000000..4d325a2
Binary files /dev/null and b/game1/mp3/music/lv11_ring_anan_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_ring_azhe_v1.mp3 b/game1/mp3/music/lv11_ring_azhe_v1.mp3
new file mode 100644
index 0000000..8985486
Binary files /dev/null and b/game1/mp3/music/lv11_ring_azhe_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_ring_delivery_v1.mp3 b/game1/mp3/music/lv11_ring_delivery_v1.mp3
new file mode 100644
index 0000000..7b4b214
Binary files /dev/null and b/game1/mp3/music/lv11_ring_delivery_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_ring_dorm_group_v1.mp3 b/game1/mp3/music/lv11_ring_dorm_group_v1.mp3
new file mode 100644
index 0000000..ff2b3f7
Binary files /dev/null and b/game1/mp3/music/lv11_ring_dorm_group_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_ring_hr_v1.mp3 b/game1/mp3/music/lv11_ring_hr_v1.mp3
new file mode 100644
index 0000000..6ffe655
Binary files /dev/null and b/game1/mp3/music/lv11_ring_hr_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_ring_mom_v1.mp3 b/game1/mp3/music/lv11_ring_mom_v1.mp3
new file mode 100644
index 0000000..87fc941
Binary files /dev/null and b/game1/mp3/music/lv11_ring_mom_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_ring_unknown_v1.mp3 b/game1/mp3/music/lv11_ring_unknown_v1.mp3
new file mode 100644
index 0000000..6a8b1fd
Binary files /dev/null and b/game1/mp3/music/lv11_ring_unknown_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_ring_xiaomei_v1.mp3 b/game1/mp3/music/lv11_ring_xiaomei_v1.mp3
new file mode 100644
index 0000000..1de1dd1
Binary files /dev/null and b/game1/mp3/music/lv11_ring_xiaomei_v1.mp3 differ
diff --git a/game1/mp3/music/lv11_ring_zhounan_v1.mp3 b/game1/mp3/music/lv11_ring_zhounan_v1.mp3
new file mode 100644
index 0000000..3764934
Binary files /dev/null and b/game1/mp3/music/lv11_ring_zhounan_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_breathing_unknown_v1.mp3 b/game1/mp3/sfx/sfx_breathing_unknown_v1.mp3
new file mode 100644
index 0000000..dbd3e48
Binary files /dev/null and b/game1/mp3/sfx/sfx_breathing_unknown_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_cat_yowl_v1.mp3 b/game1/mp3/sfx/sfx_cat_yowl_v1.mp3
new file mode 100644
index 0000000..1511547
Binary files /dev/null and b/game1/mp3/sfx/sfx_cat_yowl_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_deep_breath_v1.mp3 b/game1/mp3/sfx/sfx_deep_breath_v1.mp3
new file mode 100644
index 0000000..b94989d
Binary files /dev/null and b/game1/mp3/sfx/sfx_deep_breath_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_door_knock_firm_v1.mp3 b/game1/mp3/sfx/sfx_door_knock_firm_v1.mp3
new file mode 100644
index 0000000..9941247
Binary files /dev/null and b/game1/mp3/sfx/sfx_door_knock_firm_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_door_knock_gentle_v1.mp3 b/game1/mp3/sfx/sfx_door_knock_gentle_v1.mp3
new file mode 100644
index 0000000..119c09e
Binary files /dev/null and b/game1/mp3/sfx/sfx_door_knock_gentle_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_ending_chime_v1.mp3 b/game1/mp3/sfx/sfx_ending_chime_v1.mp3
new file mode 100644
index 0000000..6395e47
Binary files /dev/null and b/game1/mp3/sfx/sfx_ending_chime_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_glass_shatter_short_v1.mp3 b/game1/mp3/sfx/sfx_glass_shatter_short_v1.mp3
new file mode 100644
index 0000000..edaf152
Binary files /dev/null and b/game1/mp3/sfx/sfx_glass_shatter_short_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_heartbeat_fast_v1.mp3 b/game1/mp3/sfx/sfx_heartbeat_fast_v1.mp3
new file mode 100644
index 0000000..5a822ec
Binary files /dev/null and b/game1/mp3/sfx/sfx_heartbeat_fast_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_kettle_whistle_v1.mp3 b/game1/mp3/sfx/sfx_kettle_whistle_v1.mp3
new file mode 100644
index 0000000..83b8ca8
Binary files /dev/null and b/game1/mp3/sfx/sfx_kettle_whistle_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_keyboard_fast_v1.mp3 b/game1/mp3/sfx/sfx_keyboard_fast_v1.mp3
new file mode 100644
index 0000000..899c876
Binary files /dev/null and b/game1/mp3/sfx/sfx_keyboard_fast_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_keyboard_slow_v1.mp3 b/game1/mp3/sfx/sfx_keyboard_slow_v1.mp3
new file mode 100644
index 0000000..51a6e48
Binary files /dev/null and b/game1/mp3/sfx/sfx_keyboard_slow_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_night_bus_v1.mp3 b/game1/mp3/sfx/sfx_night_bus_v1.mp3
new file mode 100644
index 0000000..0ad7bc4
Binary files /dev/null and b/game1/mp3/sfx/sfx_night_bus_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_paper_turning_v1.mp3 b/game1/mp3/sfx/sfx_paper_turning_v1.mp3
new file mode 100644
index 0000000..a2ab4e1
Binary files /dev/null and b/game1/mp3/sfx/sfx_paper_turning_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_phone_vibrate_v1.mp3 b/game1/mp3/sfx/sfx_phone_vibrate_v1.mp3
new file mode 100644
index 0000000..bd58ac7
Binary files /dev/null and b/game1/mp3/sfx/sfx_phone_vibrate_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_rain_heavier_v1.mp3 b/game1/mp3/sfx/sfx_rain_heavier_v1.mp3
new file mode 100644
index 0000000..12c93d3
Binary files /dev/null and b/game1/mp3/sfx/sfx_rain_heavier_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_rain_lighter_v1.mp3 b/game1/mp3/sfx/sfx_rain_lighter_v1.mp3
new file mode 100644
index 0000000..650f9a3
Binary files /dev/null and b/game1/mp3/sfx/sfx_rain_lighter_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_transition_chapter_v1.mp3 b/game1/mp3/sfx/sfx_transition_chapter_v1.mp3
new file mode 100644
index 0000000..f8b9666
Binary files /dev/null and b/game1/mp3/sfx/sfx_transition_chapter_v1.mp3 differ
diff --git a/game1/mp3/sfx/sfx_water_boiling_v1.mp3 b/game1/mp3/sfx/sfx_water_boiling_v1.mp3
new file mode 100644
index 0000000..dfd4193
Binary files /dev/null and b/game1/mp3/sfx/sfx_water_boiling_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0015_mom_call_01_wav_v1.mp3 b/game1/mp3/tts/lv11_0015_mom_call_01_wav_v1.mp3
new file mode 100644
index 0000000..ab955a8
Binary files /dev/null and b/game1/mp3/tts/lv11_0015_mom_call_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0030_self3y_voice_01_wav_v1.mp3 b/game1/mp3/tts/lv11_0030_self3y_voice_01_wav_v1.mp3
new file mode 100644
index 0000000..f3b5631
Binary files /dev/null and b/game1/mp3/tts/lv11_0030_self3y_voice_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0030_self3y_voice_02_wav_v1.mp3 b/game1/mp3/tts/lv11_0030_self3y_voice_02_wav_v1.mp3
new file mode 100644
index 0000000..c760746
Binary files /dev/null and b/game1/mp3/tts/lv11_0030_self3y_voice_02_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0030_self3y_voice_03_wav_v1.mp3 b/game1/mp3/tts/lv11_0030_self3y_voice_03_wav_v1.mp3
new file mode 100644
index 0000000..c16c063
Binary files /dev/null and b/game1/mp3/tts/lv11_0030_self3y_voice_03_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0200_mom_voice_01_seg1_wav_v1.mp3 b/game1/mp3/tts/lv11_0200_mom_voice_01_seg1_wav_v1.mp3
new file mode 100644
index 0000000..fbc7b1c
Binary files /dev/null and b/game1/mp3/tts/lv11_0200_mom_voice_01_seg1_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0200_mom_voice_01_seg2_wav_v1.mp3 b/game1/mp3/tts/lv11_0200_mom_voice_01_seg2_wav_v1.mp3
new file mode 100644
index 0000000..dce6d7a
Binary files /dev/null and b/game1/mp3/tts/lv11_0200_mom_voice_01_seg2_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0200_mom_voice_01_seg3_wav_v1.mp3 b/game1/mp3/tts/lv11_0200_mom_voice_01_seg3_wav_v1.mp3
new file mode 100644
index 0000000..37672ac
Binary files /dev/null and b/game1/mp3/tts/lv11_0200_mom_voice_01_seg3_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0200_mom_voice_01_seg4_wav_v1.mp3 b/game1/mp3/tts/lv11_0200_mom_voice_01_seg4_wav_v1.mp3
new file mode 100644
index 0000000..1900d33
Binary files /dev/null and b/game1/mp3/tts/lv11_0200_mom_voice_01_seg4_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0200_mom_voice_01_seg5_wav_v1.mp3 b/game1/mp3/tts/lv11_0200_mom_voice_01_seg5_wav_v1.mp3
new file mode 100644
index 0000000..86a666e
Binary files /dev/null and b/game1/mp3/tts/lv11_0200_mom_voice_01_seg5_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0200_mom_voice_01_seg6_wav_v1.mp3 b/game1/mp3/tts/lv11_0200_mom_voice_01_seg6_wav_v1.mp3
new file mode 100644
index 0000000..79e6ee5
Binary files /dev/null and b/game1/mp3/tts/lv11_0200_mom_voice_01_seg6_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0200_mom_voice_01_seg7_wav_v1.mp3 b/game1/mp3/tts/lv11_0200_mom_voice_01_seg7_wav_v1.mp3
new file mode 100644
index 0000000..829ab95
Binary files /dev/null and b/game1/mp3/tts/lv11_0200_mom_voice_01_seg7_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_0200_mom_voice_01_seg8_wav_v1.mp3 b/game1/mp3/tts/lv11_0200_mom_voice_01_seg8_wav_v1.mp3
new file mode 100644
index 0000000..f45e7b8
Binary files /dev/null and b/game1/mp3/tts/lv11_0200_mom_voice_01_seg8_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2103_mom_call_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2103_mom_call_01_wav_v1.mp3
new file mode 100644
index 0000000..ba73fc7
Binary files /dev/null and b/game1/mp3/tts/lv11_2103_mom_call_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2115_xiaomei_voice_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2115_xiaomei_voice_01_wav_v1.mp3
new file mode 100644
index 0000000..96478de
Binary files /dev/null and b/game1/mp3/tts/lv11_2115_xiaomei_voice_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2115_xiaomei_voice_02_wav_v1.mp3 b/game1/mp3/tts/lv11_2115_xiaomei_voice_02_wav_v1.mp3
new file mode 100644
index 0000000..62681e3
Binary files /dev/null and b/game1/mp3/tts/lv11_2115_xiaomei_voice_02_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2115_xiaomei_voice_03_wav_v1.mp3 b/game1/mp3/tts/lv11_2115_xiaomei_voice_03_wav_v1.mp3
new file mode 100644
index 0000000..9405253
Binary files /dev/null and b/game1/mp3/tts/lv11_2115_xiaomei_voice_03_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2120_delivery_call_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2120_delivery_call_01_wav_v1.mp3
new file mode 100644
index 0000000..984a5cd
Binary files /dev/null and b/game1/mp3/tts/lv11_2120_delivery_call_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2130_azhe_voice_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2130_azhe_voice_01_wav_v1.mp3
new file mode 100644
index 0000000..300a91d
Binary files /dev/null and b/game1/mp3/tts/lv11_2130_azhe_voice_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2145_hr_voice_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2145_hr_voice_01_wav_v1.mp3
new file mode 100644
index 0000000..eaca2e9
Binary files /dev/null and b/game1/mp3/tts/lv11_2145_hr_voice_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2200_anan_voice_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2200_anan_voice_01_wav_v1.mp3
new file mode 100644
index 0000000..d7d1f86
Binary files /dev/null and b/game1/mp3/tts/lv11_2200_anan_voice_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2215_dorm_a_voice_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2215_dorm_a_voice_01_wav_v1.mp3
new file mode 100644
index 0000000..fc607c0
Binary files /dev/null and b/game1/mp3/tts/lv11_2215_dorm_a_voice_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2215_dorm_a_voice_02_wav_v1.mp3 b/game1/mp3/tts/lv11_2215_dorm_a_voice_02_wav_v1.mp3
new file mode 100644
index 0000000..9aad740
Binary files /dev/null and b/game1/mp3/tts/lv11_2215_dorm_a_voice_02_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2215_dorm_b_voice_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2215_dorm_b_voice_01_wav_v1.mp3
new file mode 100644
index 0000000..dd53219
Binary files /dev/null and b/game1/mp3/tts/lv11_2215_dorm_b_voice_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2215_dorm_b_voice_02_wav_v1.mp3 b/game1/mp3/tts/lv11_2215_dorm_b_voice_02_wav_v1.mp3
new file mode 100644
index 0000000..c85edf8
Binary files /dev/null and b/game1/mp3/tts/lv11_2215_dorm_b_voice_02_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2215_dorm_c_voice_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2215_dorm_c_voice_01_wav_v1.mp3
new file mode 100644
index 0000000..6c08f4a
Binary files /dev/null and b/game1/mp3/tts/lv11_2215_dorm_c_voice_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg1_wav_v1.mp3 b/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg1_wav_v1.mp3
new file mode 100644
index 0000000..c4df688
Binary files /dev/null and b/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg1_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg2_wav_v1.mp3 b/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg2_wav_v1.mp3
new file mode 100644
index 0000000..15b76e6
Binary files /dev/null and b/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg2_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg3_wav_v1.mp3 b/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg3_wav_v1.mp3
new file mode 100644
index 0000000..0cab5ec
Binary files /dev/null and b/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg3_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg4_wav_v1.mp3 b/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg4_wav_v1.mp3
new file mode 100644
index 0000000..283603d
Binary files /dev/null and b/game1/mp3/tts/lv11_2245_xiaomei_voice_01_seg4_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2300_mom_text_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2300_mom_text_01_wav_v1.mp3
new file mode 100644
index 0000000..0a001d7
Binary files /dev/null and b/game1/mp3/tts/lv11_2300_mom_text_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2315_azhe_call_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2315_azhe_call_01_wav_v1.mp3
new file mode 100644
index 0000000..bf2a0da
Binary files /dev/null and b/game1/mp3/tts/lv11_2315_azhe_call_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2330_zhounan_voice_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2330_zhounan_voice_01_wav_v1.mp3
new file mode 100644
index 0000000..19520c7
Binary files /dev/null and b/game1/mp3/tts/lv11_2330_zhounan_voice_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2345_zhounan_voice_01_wav_v1.mp3 b/game1/mp3/tts/lv11_2345_zhounan_voice_01_wav_v1.mp3
new file mode 100644
index 0000000..3725075
Binary files /dev/null and b/game1/mp3/tts/lv11_2345_zhounan_voice_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2345_zhounan_voice_02_wav_v1.mp3 b/game1/mp3/tts/lv11_2345_zhounan_voice_02_wav_v1.mp3
new file mode 100644
index 0000000..2f56c42
Binary files /dev/null and b/game1/mp3/tts/lv11_2345_zhounan_voice_02_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2345_zhounan_voice_03_wav_v1.mp3 b/game1/mp3/tts/lv11_2345_zhounan_voice_03_wav_v1.mp3
new file mode 100644
index 0000000..4550edf
Binary files /dev/null and b/game1/mp3/tts/lv11_2345_zhounan_voice_03_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_2345_zhounan_voice_04_wav_v1.mp3 b/game1/mp3/tts/lv11_2345_zhounan_voice_04_wav_v1.mp3
new file mode 100644
index 0000000..a654c35
Binary files /dev/null and b/game1/mp3/tts/lv11_2345_zhounan_voice_04_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/lv11_sys_memo_01_wav_v1.mp3 b/game1/mp3/tts/lv11_sys_memo_01_wav_v1.mp3
new file mode 100644
index 0000000..22f07f8
Binary files /dev/null and b/game1/mp3/tts/lv11_sys_memo_01_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_ending_a_linxia_wav_v1.mp3 b/game1/mp3/tts/v2_ending_a_linxia_wav_v1.mp3
new file mode 100644
index 0000000..bedb00d
Binary files /dev/null and b/game1/mp3/tts/v2_ending_a_linxia_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_ending_b_linxia_wav_v1.mp3 b/game1/mp3/tts/v2_ending_b_linxia_wav_v1.mp3
new file mode 100644
index 0000000..25931dc
Binary files /dev/null and b/game1/mp3/tts/v2_ending_b_linxia_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_ending_c_linxia_wav_v1.mp3 b/game1/mp3/tts/v2_ending_c_linxia_wav_v1.mp3
new file mode 100644
index 0000000..b264580
Binary files /dev/null and b/game1/mp3/tts/v2_ending_c_linxia_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_ending_d_linxia_wav_v1.mp3 b/game1/mp3/tts/v2_ending_d_linxia_wav_v1.mp3
new file mode 100644
index 0000000..1a1a906
Binary files /dev/null and b/game1/mp3/tts/v2_ending_d_linxia_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_ending_e_linxia_wav_v1.mp3 b/game1/mp3/tts/v2_ending_e_linxia_wav_v1.mp3
new file mode 100644
index 0000000..4157e7b
Binary files /dev/null and b/game1/mp3/tts/v2_ending_e_linxia_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_ending_e_zhounan_wav_v1.mp3 b/game1/mp3/tts/v2_ending_e_zhounan_wav_v1.mp3
new file mode 100644
index 0000000..b3ce3fd
Binary files /dev/null and b/game1/mp3/tts/v2_ending_e_zhounan_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_linxia_generic_cold_fine_wav_v1.mp3 b/game1/mp3/tts/v2_linxia_generic_cold_fine_wav_v1.mp3
new file mode 100644
index 0000000..9b5d455
Binary files /dev/null and b/game1/mp3/tts/v2_linxia_generic_cold_fine_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_linxia_generic_cold_know_wav_v1.mp3 b/game1/mp3/tts/v2_linxia_generic_cold_know_wav_v1.mp3
new file mode 100644
index 0000000..eab1222
Binary files /dev/null and b/game1/mp3/tts/v2_linxia_generic_cold_know_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_linxia_generic_curious_wav_v1.mp3 b/game1/mp3/tts/v2_linxia_generic_curious_wav_v1.mp3
new file mode 100644
index 0000000..4a4ff71
Binary files /dev/null and b/game1/mp3/tts/v2_linxia_generic_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_linxia_generic_hmm_wav_v1.mp3 b/game1/mp3/tts/v2_linxia_generic_hmm_wav_v1.mp3
new file mode 100644
index 0000000..ae87492
Binary files /dev/null and b/game1/mp3/tts/v2_linxia_generic_hmm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_linxia_generic_ok_wav_v1.mp3 b/game1/mp3/tts/v2_linxia_generic_ok_wav_v1.mp3
new file mode 100644
index 0000000..f5f5a60
Binary files /dev/null and b/game1/mp3/tts/v2_linxia_generic_ok_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_linxia_generic_then_wav_v1.mp3 b/game1/mp3/tts/v2_linxia_generic_then_wav_v1.mp3
new file mode 100644
index 0000000..112b1f0
Binary files /dev/null and b/game1/mp3/tts/v2_linxia_generic_then_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_linxia_generic_warm_ok_wav_v1.mp3 b/game1/mp3/tts/v2_linxia_generic_warm_ok_wav_v1.mp3
new file mode 100644
index 0000000..1ef930c
Binary files /dev/null and b/game1/mp3/tts/v2_linxia_generic_warm_ok_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg01_linxia_auto_wav_v1.mp3 b/game1/mp3/tts/v2_msg01_linxia_auto_wav_v1.mp3
new file mode 100644
index 0000000..ee1e95e
Binary files /dev/null and b/game1/mp3/tts/v2_msg01_linxia_auto_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..b255a8a
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..c6eb5c8
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_linxia_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_linxia_tap_wav_v1.mp3
new file mode 100644
index 0000000..10f1acd
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_linxia_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..bdd1d6e
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_r3_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_r3_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..ba69bbf
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_r3_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_r3_react_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_r3_react_wav_v1.mp3
new file mode 100644
index 0000000..8e4cf0e
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_r3_react_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..d57ab7b
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..0687871
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_react_silent_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_react_silent_wav_v1.mp3
new file mode 100644
index 0000000..7ac3711
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_react_silent_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_react_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_react_tap_wav_v1.mp3
new file mode 100644
index 0000000..d4e91f8
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_react_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg02_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg02_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..729809c
Binary files /dev/null and b/game1/mp3/tts/v2_msg02_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg03_linxia_auto_wav_v1.mp3 b/game1/mp3/tts/v2_msg03_linxia_auto_wav_v1.mp3
new file mode 100644
index 0000000..12216d0
Binary files /dev/null and b/game1/mp3/tts/v2_msg03_linxia_auto_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..3f25573
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..44051d2
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_linxia_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_linxia_tap_wav_v1.mp3
new file mode 100644
index 0000000..2f98073
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_linxia_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..b9924ef
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_r3c_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_r3c_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..64cbc51
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_r3c_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_r3c_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_r3c_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..1144657
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_r3c_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_r3c_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_r3c_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..7c17a91
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_r3c_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_r3w_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_r3w_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..373e258
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_r3w_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_r3w_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_r3w_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..d469d12
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_r3w_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_r3w_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_r3w_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..bed53f4
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_r3w_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_r3w_react_silent_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_r3w_react_silent_wav_v1.mp3
new file mode 100644
index 0000000..b077ccc
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_r3w_react_silent_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_r3w_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_r3w_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..d63d290
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_r3w_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..40f82a8
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..c8f6570
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_react_silent_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_react_silent_wav_v1.mp3
new file mode 100644
index 0000000..7a16cb3
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_react_silent_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_react_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_react_tap_wav_v1.mp3
new file mode 100644
index 0000000..2e19929
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_react_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg04_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg04_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..9cac590
Binary files /dev/null and b/game1/mp3/tts/v2_msg04_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg05_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg05_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..5be3ef4
Binary files /dev/null and b/game1/mp3/tts/v2_msg05_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg05_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg05_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..42f0c0e
Binary files /dev/null and b/game1/mp3/tts/v2_msg05_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg05_linxia_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg05_linxia_tap_wav_v1.mp3
new file mode 100644
index 0000000..c39b88d
Binary files /dev/null and b/game1/mp3/tts/v2_msg05_linxia_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg05_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg05_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..cae2d59
Binary files /dev/null and b/game1/mp3/tts/v2_msg05_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg05_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg05_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..da8b70a
Binary files /dev/null and b/game1/mp3/tts/v2_msg05_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg05_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg05_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..65d24fe
Binary files /dev/null and b/game1/mp3/tts/v2_msg05_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg06_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg06_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..8f9fa23
Binary files /dev/null and b/game1/mp3/tts/v2_msg06_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg06_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg06_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..4679fe0
Binary files /dev/null and b/game1/mp3/tts/v2_msg06_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg06_linxia_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg06_linxia_tap_wav_v1.mp3
new file mode 100644
index 0000000..36b126a
Binary files /dev/null and b/game1/mp3/tts/v2_msg06_linxia_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg06_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg06_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..986c64d
Binary files /dev/null and b/game1/mp3/tts/v2_msg06_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg06_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg06_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..e49287d
Binary files /dev/null and b/game1/mp3/tts/v2_msg06_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg06_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg06_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..1126cd2
Binary files /dev/null and b/game1/mp3/tts/v2_msg06_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg06_react_silent_wav_v1.mp3 b/game1/mp3/tts/v2_msg06_react_silent_wav_v1.mp3
new file mode 100644
index 0000000..ff65d87
Binary files /dev/null and b/game1/mp3/tts/v2_msg06_react_silent_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg06_react_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg06_react_tap_wav_v1.mp3
new file mode 100644
index 0000000..ab08f60
Binary files /dev/null and b/game1/mp3/tts/v2_msg06_react_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg06_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg06_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..9e630be
Binary files /dev/null and b/game1/mp3/tts/v2_msg06_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg07_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg07_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..ec54bc4
Binary files /dev/null and b/game1/mp3/tts/v2_msg07_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg07_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg07_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..6b0c07f
Binary files /dev/null and b/game1/mp3/tts/v2_msg07_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg07_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg07_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..35cb0f7
Binary files /dev/null and b/game1/mp3/tts/v2_msg07_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg07_r3_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg07_r3_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..7b307a9
Binary files /dev/null and b/game1/mp3/tts/v2_msg07_r3_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg07_r3_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg07_r3_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..ae83f85
Binary files /dev/null and b/game1/mp3/tts/v2_msg07_r3_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg07_r3_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg07_r3_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..b8c987e
Binary files /dev/null and b/game1/mp3/tts/v2_msg07_r3_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg07_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg07_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..24c8a42
Binary files /dev/null and b/game1/mp3/tts/v2_msg07_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg07_react_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg07_react_tap_wav_v1.mp3
new file mode 100644
index 0000000..e9cf2e0
Binary files /dev/null and b/game1/mp3/tts/v2_msg07_react_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg07_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg07_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..203dee9
Binary files /dev/null and b/game1/mp3/tts/v2_msg07_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..f842aed
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..2190c0a
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_linxia_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_linxia_tap_wav_v1.mp3
new file mode 100644
index 0000000..cdf9096
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_linxia_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..6f56481
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_r3_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_r3_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..2cea7d5
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_r3_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_r3_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_r3_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..3e52b25
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_r3_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_r3_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_r3_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..9619b57
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_r3_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_r3_react_silent_q_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_r3_react_silent_q_wav_v1.mp3
new file mode 100644
index 0000000..2be04fc
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_r3_react_silent_q_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_r3_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_r3_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..9ae5ff3
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_r3_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..9e88260
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_react_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_react_tap_wav_v1.mp3
new file mode 100644
index 0000000..4891cf0
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_react_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg09_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg09_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..3d7ce10
Binary files /dev/null and b/game1/mp3/tts/v2_msg09_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_call_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_call_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..c90447c
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_call_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_call_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_call_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..1aa0411
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_call_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_call_mom_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_call_mom_wav_v1.mp3
new file mode 100644
index 0000000..67d61f5
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_call_mom_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_call_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_call_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..6b5a97d
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_call_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_call_react_down_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_call_react_down_wav_v1.mp3
new file mode 100644
index 0000000..b40fde5
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_call_react_down_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_call_react_silent_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_call_react_silent_wav_v1.mp3
new file mode 100644
index 0000000..62d541e
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_call_react_silent_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_call_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_call_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..d3255e7
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_call_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..e4bf056
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..85db7ba
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_linxia_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_linxia_tap_wav_v1.mp3
new file mode 100644
index 0000000..387aedd
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_linxia_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..137f860
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..f0f7e9e
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_react_silent_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_react_silent_wav_v1.mp3
new file mode 100644
index 0000000..4bacdf2
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_react_silent_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_react_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_react_tap_wav_v1.mp3
new file mode 100644
index 0000000..c4e37f9
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_react_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..c1f7617
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_rw_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_rw_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..edf0ed4
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_rw_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_rw_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_rw_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..62213ae
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_rw_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_rw_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_rw_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..40d1059
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_rw_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_rw_react_silent_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_rw_react_silent_wav_v1.mp3
new file mode 100644
index 0000000..36b7f7c
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_rw_react_silent_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg10_rw_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg10_rw_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..5586814
Binary files /dev/null and b/game1/mp3/tts/v2_msg10_rw_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg11_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg11_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..880c397
Binary files /dev/null and b/game1/mp3/tts/v2_msg11_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg11_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg11_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..897ae19
Binary files /dev/null and b/game1/mp3/tts/v2_msg11_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg11_linxia_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg11_linxia_tap_wav_v1.mp3
new file mode 100644
index 0000000..68fae6d
Binary files /dev/null and b/game1/mp3/tts/v2_msg11_linxia_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg11_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg11_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..70fa7f8
Binary files /dev/null and b/game1/mp3/tts/v2_msg11_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg11_r3_react_silent_wav_v1.mp3 b/game1/mp3/tts/v2_msg11_r3_react_silent_wav_v1.mp3
new file mode 100644
index 0000000..6c8af63
Binary files /dev/null and b/game1/mp3/tts/v2_msg11_r3_react_silent_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg11_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg11_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..f28d879
Binary files /dev/null and b/game1/mp3/tts/v2_msg11_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg11_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg11_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..2d70636
Binary files /dev/null and b/game1/mp3/tts/v2_msg11_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg11_react_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg11_react_tap_wav_v1.mp3
new file mode 100644
index 0000000..48ae195
Binary files /dev/null and b/game1/mp3/tts/v2_msg11_react_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg11_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg11_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..b794c8c
Binary files /dev/null and b/game1/mp3/tts/v2_msg11_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg12_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg12_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..ea1aaeb
Binary files /dev/null and b/game1/mp3/tts/v2_msg12_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg12_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg12_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..8239e17
Binary files /dev/null and b/game1/mp3/tts/v2_msg12_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg12_linxia_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg12_linxia_tap_wav_v1.mp3
new file mode 100644
index 0000000..dd888aa
Binary files /dev/null and b/game1/mp3/tts/v2_msg12_linxia_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg12_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg12_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..de8c3d2
Binary files /dev/null and b/game1/mp3/tts/v2_msg12_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg12_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg12_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..1d38cac
Binary files /dev/null and b/game1/mp3/tts/v2_msg12_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg12_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg12_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..fae8afd
Binary files /dev/null and b/game1/mp3/tts/v2_msg12_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg12_react_silent_wav_v1.mp3 b/game1/mp3/tts/v2_msg12_react_silent_wav_v1.mp3
new file mode 100644
index 0000000..c724270
Binary files /dev/null and b/game1/mp3/tts/v2_msg12_react_silent_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg12_react_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg12_react_tap_wav_v1.mp3
new file mode 100644
index 0000000..0501fdd
Binary files /dev/null and b/game1/mp3/tts/v2_msg12_react_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg12_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg12_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..30aa6c2
Binary files /dev/null and b/game1/mp3/tts/v2_msg12_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..eaf929a
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..02693c8
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_linxia_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_linxia_tap_wav_v1.mp3
new file mode 100644
index 0000000..3bd68e5
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_linxia_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..780319b
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_r3_linxia_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_r3_linxia_cold_wav_v1.mp3
new file mode 100644
index 0000000..0981656
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_r3_linxia_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_r3_linxia_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_r3_linxia_curious_wav_v1.mp3
new file mode 100644
index 0000000..c0cfbf3
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_r3_linxia_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_r3_linxia_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_r3_linxia_warm_wav_v1.mp3
new file mode 100644
index 0000000..a9db32f
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_r3_linxia_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_r3_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_r3_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..12a65cf
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_r3_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_r3_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_r3_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..6b2b01a
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_r3_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_r3_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_r3_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..f64314e
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_r3_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_react_cold_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_react_cold_wav_v1.mp3
new file mode 100644
index 0000000..6f90571
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_react_cold_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_react_curious_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_react_curious_wav_v1.mp3
new file mode 100644
index 0000000..1f83e85
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_react_curious_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_react_silent_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_react_silent_wav_v1.mp3
new file mode 100644
index 0000000..326d9fa
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_react_silent_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_react_tap_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_react_tap_wav_v1.mp3
new file mode 100644
index 0000000..98a03e5
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_react_tap_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg13_react_warm_wav_v1.mp3 b/game1/mp3/tts/v2_msg13_react_warm_wav_v1.mp3
new file mode 100644
index 0000000..6bb7685
Binary files /dev/null and b/game1/mp3/tts/v2_msg13_react_warm_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg14_linxia_auto_wav_v1.mp3 b/game1/mp3/tts/v2_msg14_linxia_auto_wav_v1.mp3
new file mode 100644
index 0000000..efff5b3
Binary files /dev/null and b/game1/mp3/tts/v2_msg14_linxia_auto_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg14_mom_ask_wav_v1.mp3 b/game1/mp3/tts/v2_msg14_mom_ask_wav_v1.mp3
new file mode 100644
index 0000000..b498e6f
Binary files /dev/null and b/game1/mp3/tts/v2_msg14_mom_ask_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg14_react_linked_wav_v1.mp3 b/game1/mp3/tts/v2_msg14_react_linked_wav_v1.mp3
new file mode 100644
index 0000000..8f821b9
Binary files /dev/null and b/game1/mp3/tts/v2_msg14_react_linked_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_msg14_react_unlinked_wav_v1.mp3 b/game1/mp3/tts/v2_msg14_react_unlinked_wav_v1.mp3
new file mode 100644
index 0000000..dad36e7
Binary files /dev/null and b/game1/mp3/tts/v2_msg14_react_unlinked_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_sys_content_warning_wav_v1.mp3 b/game1/mp3/tts/v2_sys_content_warning_wav_v1.mp3
new file mode 100644
index 0000000..608bbda
Binary files /dev/null and b/game1/mp3/tts/v2_sys_content_warning_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_sys_hotline_wav_v1.mp3 b/game1/mp3/tts/v2_sys_hotline_wav_v1.mp3
new file mode 100644
index 0000000..def43b8
Binary files /dev/null and b/game1/mp3/tts/v2_sys_hotline_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_sys_paused_wav_v1.mp3 b/game1/mp3/tts/v2_sys_paused_wav_v1.mp3
new file mode 100644
index 0000000..112ae2d
Binary files /dev/null and b/game1/mp3/tts/v2_sys_paused_wav_v1.mp3 differ
diff --git a/game1/mp3/tts/v2_sys_wait_reply_wav_v1.mp3 b/game1/mp3/tts/v2_sys_wait_reply_wav_v1.mp3
new file mode 100644
index 0000000..f3669ca
Binary files /dev/null and b/game1/mp3/tts/v2_sys_wait_reply_wav_v1.mp3 differ
diff --git a/game1/操作说明.md b/game1/操作说明.md
new file mode 100644
index 0000000..9601460
--- /dev/null
+++ b/game1/操作说明.md
@@ -0,0 +1,237 @@
+# 《林夏》操作说明
+
+---
+
+## 一、游戏简介
+
+全黑屏叙事游戏。玩家扮演 24 岁的林夏,用一部碎屏手机度过今晚 21:00–02:00 的 5 个小时。
+
+- **没有视觉界面**,只有声音和触感
+- **全程约 60 分钟**(5 小时游戏时间压缩)
+- **16 条消息**,5 种结局
+- **不需要说话**,不需要麦克风
+
+---
+
+## 二、开始前准备
+
+1. 手机和电脑连接**同一 Wi-Fi**
+2. 打开手机浏览器,输入地址:
+
+ ```
+ http://172.28.107.97:8765/game/
+ ```
+
+3. **关灯,戴上耳机**
+4. 将手机音量调到适中(建议 60–80%)
+5. 轻触屏幕任意位置开始游戏
+
+> **iOS 用户注意**:Safari 不支持震动反馈,操作正常但无震感。建议使用 Android Chrome。
+
+---
+
+## 三、消息类型与接收方式
+
+游戏中有三种消息形态:
+
+### 来电
+- 触发:手机长震 + 角色专属铃声
+- 响 **20–30 秒**后自动漏接
+
+| 操作 | 效果 |
+|------|------|
+| **单点** | 接听 |
+| **左滑** | 拒接 |
+| **不动** | 漏接(对方挂断) |
+
+### 微信语音 / 微信文字
+- 触发:两短震 + 系统提示音(TTS 报出发件人)
+
+| 操作 | 效果 |
+|------|------|
+| **单点** | 播放语音 / 念出文字 |
+| **左滑** | 忽略(保持未读) |
+| **不动** | 保持未读,晚些再听 |
+
+---
+
+## 四、回复消息
+
+**听完语音 / 文字念完后**,进入等待回复状态:
+
+| 操作 | 发送内容 |
+|------|----------|
+| **单点** | 发"嗯。"(最简单的回执) |
+| **双点** | 发标准回复(针对该消息的一句话) |
+| **长按 1 秒** | 进入**细回复**模式 |
+| **左滑** | 已读不回 |
+| **不动** | 维持"已读未回"状态 |
+
+---
+
+## 五、细回复(核心机制)
+
+长按 1 秒后,震动确认进入细回复。系统会依次念出 **4 个候选回复**:
+
+```
+一…… "好,明天寄。"
+二…… "你怎么不自己来拿。"
+三…… "你那边……都好吗。"
+四…… (什么都不说)
+```
+
+**在听到目标选项时,朝对应方向滑动:**
+
+| 滑动方向 | 对应选项 |
+|----------|----------|
+| **左滑** | 一 |
+| **右滑** | 二 |
+| **上滑** | 三 |
+| **下滑** | 四 |
+
+> **没听清?** 单点屏幕可重听所有选项,或长按 1 秒重新播放。
+> 选错了也没关系——每条消息只影响行为画像,不会"game over"。
+
+---
+
+## 六、查看当前状态
+
+**随时下滑**,系统会用语音告诉你当前在哪个页面,以及可以做什么操作。
+
+例如:
+- 主屏幕时下滑 → "主屏幕,3条未读。上滑查看收件箱。"
+- 来电时下滑 → "妈妈来电中。单点接听,左滑拒接。"
+- 听完消息后下滑 → "阿哲的消息已播完。单点回嗯,双点标准回复,长按细回复,左滑已读不回。"
+
+> 不确定自己在哪?下滑就对了。
+
+---
+
+## 七、查看未读列表(收件箱)
+
+在主屏幕**上滑**,进入收件箱模式。
+
+系统会依次念出所有未读消息:
+```
+"收件箱:4条未读。妈妈,来电。阿哲,语音。周南,语音。……"
+```
+
+- **单点** → 跳转至列表第一条
+- **左滑** → 返回主屏幕
+- **等待 8 秒** → 自动关闭收件箱,返回游戏
+
+---
+
+## 八、游戏内时间
+
+游戏将现实的 21:00–02:00(5 小时)压缩为 **60 分钟**真实时间。
+
+| 游戏时间 | 真实等待 |
+|----------|----------|
+| 21:03 | 约 36 秒 |
+| 21:30 | 约 6 分钟 |
+| 22:30 | 约 18 分钟 |
+| 23:45 | 约 33 分钟 |
+| 02:00 | 约 60 分钟 |
+
+消息到来时间固定,**漏接 / 不听是合法选择**,但漏掉的内容不会重发。
+
+---
+
+## 九、结局说明(不含剧透)
+
+共 **5 种结局**,由以下 5 个变量决定:
+
+| 变量 | 触发条件 |
+|------|----------|
+| 与妈妈的连接 | 是否接了妈妈的电话 / 回了文字 |
+| 阿哲是否回来 | 23:15 那通电话的选择 |
+| 群里是否发言 | 寝室群消息的回应 |
+| 与周南的深度 | 回应周南的次数与方式 |
+| 是否听自己 | 00:30 时光胶囊是否播放 |
+
+另有一个**隐藏结局 E**,需要与某位角色深度互动才能触发。
+
+---
+
+## 十、行为画像
+
+游戏全程追踪三个维度(玩家不可见):
+
+| 维度 | 含义 | 触发行为 |
+|------|------|----------|
+| **avoidance**(回避) | 林夏今晚想一个人待着 | 漏接、不回、已读不回 |
+| **engagement**(投入) | 林夏在认真面对每一个人 | 细回复、主动追问 |
+| **nostalgia**(怀旧) | 林夏在这一夜反复回望 | 重听同一条消息、分享自己 |
+
+画像决定 **00:30 时光胶囊**播放哪个版本(明朗 / 迷茫 / 温柔)。
+
+---
+
+## 十一、彩蛋
+
+- **夜间启动**(现实时间 22:00–04:00 之间打开游戏):开场多一句话
+- **消息09**:小美醉语音结束后会有一个撤回事件
+- **消息04**:如果一直不回阿哲,会听到一个小细节
+
+---
+
+## 十二、开发者 / 测试模式
+
+在 URL 末尾加 `?dev` 可开启**10倍速**模式:
+
+```
+http://172.28.107.97:8765/game/?dev
+```
+
+- 所有消息的触发时间缩短为原来的 1/10
+- 全程约 **6 分钟**可打通
+- 适合测试结局分支
+
+---
+
+## 十三、启动服务器
+
+关机重启后需要重新启动 HTTP 服务器:
+
+```bash
+cd /home/xsl/blind
+python3 -m http.server 8765 --bind 0.0.0.0 &
+```
+
+查看当前 IP:
+
+```bash
+hostname -I | awk '{print $1}'
+```
+
+---
+
+## 十四、文件结构
+
+```
+/home/xsl/blind/
+├── game/
+│ ├── index.html 主页面(黑屏)
+│ ├── manifest.json PWA 配置
+│ ├── 操作说明.md 本文件
+│ └── js/
+│ ├── utils.js 工具函数 + 浏览器 TTS
+│ ├── haptics.js 震动反馈
+│ ├── audio.js 音频管理器
+│ ├── gestures.js 单指手势检测
+│ ├── profile.js 行为画像评分
+│ ├── story.js 16条消息数据
+│ ├── engine.js 游戏状态机
+│ └── main.js 入口
+└── audio/
+ └── 00_raw/
+ ├── tts/ 角色语音(68条)
+ ├── music/ BGM + 铃声
+ ├── sfx/ 音效
+ └── ambience/ 环境底噪
+```
+
+---
+
+*"屏幕坏了,但她的一夜还没结束。"*
diff --git a/voice/hr/hr_extra_18_trimmed.wav b/voice/hr/hr_extra_18_trimmed.wav
new file mode 100644
index 0000000..3a5338d
Binary files /dev/null and b/voice/hr/hr_extra_18_trimmed.wav differ