const service = require("../../services/game-service"); const { formatCountdown, formatTime } = require("../../utils/format"); Page({ data: { code: "", room: null, me: null, readyToRender: false, stageLabel: "", countdown: "00:00", survivors: 0, seekers: 0, captureCode: "", captureTargets: [], selectedTargetId: "", submittedCode: "", skillUsed: false, canManage: false, showDemoCode: false, captureSubmitting: false, errorMessage: "", eventViews: [] }, onLoad(options) { this.setData({ code: options.code || "" }); this.tick(); }, onShow() { if (!this.data.code) return; this.startTimer(); this.tick(); }, onHide() { this.stopTimer(); }, onUnload() { this.stopTimer(); }, startTimer() { this.stopTimer(); this.timer = setInterval(() => this.tick(), 1000); }, stopTimer() { if (!this.timer) return; clearInterval(this.timer); this.timer = null; }, tick() { let room = service.getRoom(this.data.code); if (!room) { this.stopTimer(); this.setData({ readyToRender: false, errorMessage: "房间不存在或游戏数据已被清理" }); return; } if (room.status === "preparing" && Date.now() >= room.phaseEndsAt) { room = service.beginPlaying(room.code); wx.vibrateLong(); } if (room.status === "playing") { if (Date.now() >= room.phaseEndsAt) { service.finishGame(room.code, "hiders", null, "timeout"); this.goResult(); return; } if (room.nextClueAt && Date.now() >= room.nextClueAt) { service.revealClue(room.code); wx.vibrateShort({ type: "medium" }); } } if (room.status === "finished") { this.goResult(); return; } this.refresh(); }, refresh() { const room = service.getRoom(this.data.code); if (!room) { this.stopTimer(); this.setData({ readyToRender: false, errorMessage: "房间不存在或游戏数据已被清理" }); return; } const me = room.players.find((player) => player.id === service.USER_ID) || room.players[0]; const seconds = room.status === "paused" ? Math.ceil(room.pausedRemainingMs / 1000) : Math.ceil((room.phaseEndsAt - Date.now()) / 1000); const survivors = room.players.filter( (player) => player.role === "hider" && player.status === "hiding" ).length; const seekers = room.players.filter( (player) => player.role === "seeker" ).length; const captureTargets = room.players.filter( (player) => player.role === "hider" && player.status === "hiding" ); const selectedTargetId = captureTargets.some( (player) => player.id === this.data.selectedTargetId ) ? this.data.selectedTargetId : captureTargets.length ? captureTargets[0].id : ""; const currentSkill = me.role === "seeker" ? "radar" : "silent"; const captureCode = me.role === "seeker" ? service.getCaptureCode(room.code, selectedTargetId) : service.getCaptureCode(room.code, me.id); this.setData({ room, errorMessage: "", me, readyToRender: true, stageLabel: room.status === "preparing" ? "躲藏准备" : room.status === "paused" ? "游戏已暂停" : "追逃进行中", countdown: formatCountdown(seconds), survivors, seekers, captureTargets, selectedTargetId, captureCode, skillUsed: Boolean(me.skillsUsed && me.skillsUsed[currentSkill] > 0), canManage: room.hostId === service.USER_ID || room.code === "731204", showDemoCode: Boolean(room.runtime && room.runtime.demoAccelerated), eventViews: room.events.slice(0, 6).map((event) => ({ ...event, time: formatTime(event.createdAt) })) }); }, useSkill() { if (this.data.skillUsed) { wx.showToast({ title: "本局体验技能已使用", icon: "none" }); return; } const skill = this.data.me.role === "seeker" ? "radar" : "silent"; try { service.useSkill(this.data.code, skill); wx.vibrateShort({ type: "heavy" }); this.refresh(); } catch (error) { wx.showToast({ title: error.message, icon: "none" }); } }, chooseCaptureTarget(event) { this.setData({ selectedTargetId: event.currentTarget.dataset.id, submittedCode: "" }); this.refresh(); }, inputCaptureCode(event) { this.setData({ submittedCode: event.detail.value.replace(/\D/g, "").slice(0, 4) }); }, fillDemoCode() { this.setData({ submittedCode: this.data.captureCode }); }, submitCapture() { if (this.data.captureSubmitting) return; this.setData({ captureSubmitting: true }); try { const room = service.capture( this.data.code, this.data.selectedTargetId, this.data.submittedCode ); wx.vibrateLong(); this.setData({ submittedCode: "" }); if (room.status === "finished") { this.goResult(); } else { this.setData({ captureSubmitting: false }); wx.showToast({ title: "抓捕成功", icon: "success" }); this.refresh(); } } catch (error) { this.setData({ captureSubmitting: false }); wx.showToast({ title: error.message, icon: "none" }); } }, showSafety() { wx.showActionSheet({ itemList: ["我已安全,继续游戏", "联系房主", "退出并前往集合点"], success: (result) => { if (result.tapIndex === 1) { wx.showModal({ title: "联系房主", content: "当前原型暂不提供即时通讯,请通过约定的电话或微信联系房主。", showCancel: false }); } if (result.tapIndex === 2) { wx.showModal({ title: "确认退出?", content: "请先确保自己处于安全位置,并前往约定集合点。", confirmText: "安全退出", confirmColor: "#d9554d", success: (modalResult) => { if (!modalResult.confirm) return; try { const room = service.quitGame(this.data.code); if (room.status === "finished") { this.goResult(); } else { this.stopTimer(); service.clearCurrentRoom(); wx.reLaunch({ url: "/pages/home/home" }); } } catch (error) { wx.showToast({ title: error.message, icon: "none" }); } } }); } } }); }, togglePause() { try { if (this.data.room.status === "paused") { service.resumeGame(this.data.code); } else { service.pauseGame(this.data.code); } this.refresh(); } catch (error) { wx.showToast({ title: error.message, icon: "none" }); } }, finishEarly() { wx.showModal({ title: "提前结束游戏?", content: "系统将根据当前存活情况立即生成本局战报。", confirmText: "结束游戏", confirmColor: "#d9554d", success: (result) => { if (!result.confirm) return; try { service.finishGame(this.data.code, "", service.USER_ID, "host_ended"); this.goResult(); } catch (error) { wx.showToast({ title: error.message, icon: "none" }); } } }); }, goResult() { this.stopTimer(); wx.redirectTo({ url: `/pages/result/result?code=${this.data.code}` }); }, backHome() { this.stopTimer(); service.clearCurrentRoom(); wx.reLaunch({ url: "/pages/home/home" }); } });