新建项目

This commit is contained in:
2026-07-29 10:50:33 +08:00
parent bfd3841cbd
commit 4f69b859a8
49 changed files with 5565 additions and 0 deletions

278
pages/game/game.js Normal file
View File

@@ -0,0 +1,278 @@
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" });
}
});