Commit 94cab0cd authored by 陈冲's avatar 陈冲

fix: 添加了提交分数5秒提示倒计时

parent 02b57a26
......@@ -36,7 +36,7 @@ const ROOM_ENTRY_LIMIT: usize = 20;
///游戏结束排序最大用户数
const ROOM_SCORE_RANK_LIMIT: usize = 10;
const GAME_START_COUNTDOWN_SECS: u64 = 3;
const GAME_RESULT_WAIT_SECS: u64 = 2;
const GAME_RESULT_WAIT_SECS: u64 = 5;
const GAME_RUNNING_SECS: u64 = 60;
//管理员退出5分钟后,关闭所有玩家的连接
const ADMIN_ABSENT_DEACTIVATE_PLAYER_SECS: u64 = 5 * 60;
......@@ -1031,11 +1031,6 @@ async fn handle_room_command(
if game.insert().await {
if !already_top_ranked && (1..=ROOM_SCORE_RANK_LIMIT as i64).contains(&rank) {
CLIENT_TOP_RANKED_MAP.insert(userid.to_string(), true);
if let Ok(mut state) = ROOM_STATE.lock() {
if let Some(player) = state.players.get_mut(userid) {
player.already_top_ranked = true;
}
}
}
let mut payload = json!({
......@@ -1077,19 +1072,32 @@ async fn handle_room_command(
}
}
// 获取房间状态
"room_state" => {
"room_state" | "status" => {
if let Ok(state) = ROOM_STATE.lock() {
let gameId = state.current_game_id.clone().unwrap_or_default();
let count = state.players.len();
let ranking = state.current_ranking_payload();
let status = state.status as i32;
let player_rank = if is_admin {
Value::Null
} else {
state.player_rank(userid).map_or(Value::Null, |rank| json!(rank))
};
let player_score = if is_admin {
Value::Null
} else {
state
.players
.get(userid)
.map_or(Value::Null, |player| json!(player.score))
};
// let snapshot = state.snapshot();
drop(state);
emit_msgpack(
&socket,
evt,
&ok_resp(Some(
json!({"status":status,"list":ranking,"count":count,"gameId":gameId,}),
json!({"status":status,"list":ranking,"count":count,"gameId":gameId,"rank":player_rank,"score":player_score,}),
)),
);
}
......
......@@ -66,6 +66,20 @@ export function $format_str(str: string, num: number) {
let toastElement: HTMLDivElement | undefined;
let toastTimer: ReturnType<typeof window.setTimeout> | undefined;
export function $hideToast() {
if (toastTimer) {
window.clearTimeout(toastTimer);
toastTimer = undefined;
}
if (!toastElement) {
return;
}
toastElement.style.opacity = '0';
toastElement.style.transform = 'translate(-50%, -40%)';
}
export function $toast(message?: string, duration = 2000) {
if (!message) {
return;
......
......@@ -2,7 +2,7 @@ import { ref } from 'vue'
import { useRouter } from 'vue-router'
import type { Socket } from 'socket.io-client'
import { initSocket, onSocketMessage, sendSocketMessage } from '@/commons/ws'
import { $getWechat, $read, $save, $toast, postScreenGameScore } from '@/commons/utils'
import { $getWechat, $hideToast, $read, $save, $toast, postScreenGameScore } from '@/commons/utils'
type SocketPayload = {
msg?: string
......@@ -26,6 +26,7 @@ type GameSocketOptions = {
onRescoreSubmitted?: (recount: number) => void
onRoomClosed?: (data: any) => void
onGameRecover?: (data: any) => void
onStatus?: (data: any) => void
onEvent?: (evt: string, payload: SocketPayload) => void
onRoomBack?: (data: any) => void
}
......@@ -52,28 +53,59 @@ export function joinSharedRoom(nickname: string, token: string, avatar: string,
return sendSocketMessage('room_join', { nickname: nickname, token, avatar, gameId: gameId })
}
const networkDisconnectToastDuration = 30 * 60 * 1000;
export function sendGameMessage(cmd: string, data: any = {}) {
return sendSocketMessage(cmd, data)
const sent = sendSocketMessage(cmd, data)
if (!sent) {
$toast('网络不好,分数无法提交', networkDisconnectToastDuration)
}
return sent
}
export function requestGameStatus() {
return sendSocketMessage('status')
}
export const userJoinStatus = ref(false)
export const userTop10RankStatus = ref(false)
export const isGameSocketConnected = ref(false)
export function useGameSocket(options: GameSocketOptions) {
const router = useRouter()
let socket: Socket | null = null
let offSocketMessage: (() => void) | undefined
let statusTimer: ReturnType<typeof window.setInterval> | undefined
const domain = $read('domain', '');
let resubmitcount = 0;
const stopStatusPolling = () => {
if (statusTimer) {
window.clearInterval(statusTimer)
statusTimer = undefined
}
}
const startStatusPolling = () => {
stopStatusPolling()
requestGameStatus()
statusTimer = window.setInterval(requestGameStatus, 1000)
}
socket = initSocket({
socket_path: '/socket.io',
client_ns: `${domain}/ws`,
auth: options.auth,
onConnect: (socket) => {
isGameSocketConnected.value = true;
$hideToast();
startStatusPolling();
options.onConnect?.();
},
onDisconnect: (_reason: string) => {
// $toast('网络中断,尝试重新连接...')
isGameSocketConnected.value = false;
stopStatusPolling();
$toast('网络连接已断开,请检查网络后重新进入游戏', networkDisconnectToastDuration)
socket = null;
},
onError: (error: any) => {
......@@ -136,6 +168,7 @@ export function useGameSocket(options: GameSocketOptions) {
//是否上过前十榜
userTop10RankStatus.value = data.alreadyTopRanked
$toast('您已进入游戏房间')
userTop10RankStatus.value = data.alreadyTopRanked
userJoinStatus.value = true
break
}
......@@ -148,6 +181,7 @@ export function useGameSocket(options: GameSocketOptions) {
let { rank, remainingSeconds, score } = data;
//返回的时间可能会慢一秒
options.onGameRecover?.({ rank, remainingSeconds, score });
options.onGameRecover?.({ rank, remainingSeconds, score });
break
}
case 'game_start_result': {
......@@ -162,7 +196,6 @@ export function useGameSocket(options: GameSocketOptions) {
break
}
case 'submit_score_save_result': {
//if (!userTop10RankStatus) {//保证每一轮玩家数据完整性, 暂时不需要这个条件
const submittedScore = options.onScoreSubmitted?.(true, data)
const token_origin = $getWechat()?.token_origin
if (token_origin && submittedScore && Number.isFinite(submittedScore.score)) {
......@@ -174,7 +207,13 @@ export function useGameSocket(options: GameSocketOptions) {
rank_no: submittedScore.rank_no,
})
}
//}
break
}
case 'status_result': {
if (data?.gameId && data.gameId !== options.gameId) {
return
}
options.onStatus?.(data)
break
}
case 'room_back_result': {
......@@ -189,6 +228,7 @@ export function useGameSocket(options: GameSocketOptions) {
case 'room_rank_result': {
//如果是游戏开始中再进入会收到这个排行榜消息
options.onShowRank?.(data);
options.onShowRank?.(data);
return;
}
default: {
......@@ -207,6 +247,10 @@ export function useGameSocket(options: GameSocketOptions) {
socket,
userJoinStatus,
userTop10RankStatus,
offSocketMessage
requestGameStatus,
offSocketMessage: () => {
stopStatusPolling()
offSocketMessage?.()
}
}
}
......@@ -34,6 +34,8 @@ const countdownInterval = ref(60)
const isDivDescVisible = ref(false)
const tick = ref(0) //点击次数
const rank = ref(0) //排名
const roomStatus = ref(0)
const hasFinalScoreResult = ref(false)
const currentGuFrame = ref(0)
const isGuPlaying = ref(false)
const token = ref(wechat?.token ?? '')
......@@ -51,6 +53,47 @@ const confirmRefresh = (event: BeforeUnloadEvent) => {
event.returnValue = ''
}
function getServerRank(data: any, fallback?: unknown) {
const value = typeof data === 'object' && data
? data.rank ?? data.rank_no ?? data.rankNo ?? data.game_rank ?? data.gameRank ?? fallback
: data
const nextRank = Number(value)
return Number.isFinite(nextRank) && nextRank > 0 ? nextRank : 0
}
function showFinalScoreIfReady() {
if (roomStatus.value !== 4 || !hasFinalScoreResult.value) return
hasFinalScoreResult.value = false
showScoreView()
}
function markFinalScoreResult() {
hasFinalScoreResult.value = true
showFinalScoreIfReady()
}
function syncFinalStatus(data: any) {
const nextStatus = Number(data?.status)
if (!Number.isFinite(nextStatus)) return
roomStatus.value = nextStatus
if (nextStatus === 3) {
return
}
if (nextStatus !== 4) return
const nextRank = getServerRank(data)
const nextScore = Number(data?.score)
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(nextScore) && nextScore >= 0) {
tick.value = nextScore
}
showFinalScoreIfReady()
}
function setDivDescVisible(visible: boolean) {
isDivDescVisible.value = visible
}
......@@ -62,21 +105,27 @@ function stopGameCountdown() {
}
}
function submitScore(save: boolean = false) {
function submitScore(save: boolean = false, currentTick = tick.value) {
//最后提交保存在数据库中
if (save) {
//item_num 游戏项目(1到6)
//wechat 原始token
sendGameMessage('submit_score_save', {
score: tick.value,
const sent = sendGameMessage('submit_score_save', {
score: currentTick,
wechat: wechat.token,
item_num: 1,
nickname: wechat.nickname,
avatar: wechat.avatar,
rank: rank.value,
})
if (sent && !userTop10RankStatus.value) {
window.setTimeout(() => {
$toast('成绩提交中,请稍候...', 5000)
}, 0)
}
return sent
} else {
sendGameMessage('submit_score', { score: tick.value })
return sendGameMessage('submit_score', { score: currentTick })
}
}
......@@ -92,9 +141,11 @@ function startGameCountdown(seconds = 60) {
countdownInterval.value = 0
gameCountdownTimer = undefined
setDivDescVisible(false)
hasFinalScoreResult.value = false
submitScore(true)
if (!userTop10RankStatus.value) {
$toast('成绩提交中,请稍候...', 2000)
if (userTop10RankStatus.value) {
userJoinStatus.value = false
$toast('一人只能上榜领奖一次,您的福利已到手,把机会留给其他玩家吧', 30000)
}
return
}
......@@ -125,6 +176,8 @@ function resetToLoadingView() {
}
tick.value = 0
rank.value = 0
roomStatus.value = 0
hasFinalScoreResult.value = false
countdownInterval.value = 60
currentGuFrame.value = 0
isGuPlaying.value = false
......@@ -137,6 +190,8 @@ function startGameView() {
stopGameCountdown()
tick.value = 0
currentGuFrame.value = 0
roomStatus.value = 2
hasFinalScoreResult.value = false
currentView.value = 'playing'
startGameCountdown()
}
......@@ -159,8 +214,9 @@ const touchHandler = () => {
return
}
tick.value += 5
submitScore()
const nextTick = tick.value + 5
if (!submitScore(false, nextTick)) return
tick.value = nextTick
playGame1Music();
isGuPlaying.value = true
......@@ -208,39 +264,42 @@ if (wechat) {
onScoreSubmitted: (is_save, data) => {
if (!is_save) {//非保存状态下
const nextScore = Number(typeof data === 'object' ? data?.score : NaN)
const nextRank = Number(typeof data === 'object' ? data?.rank : data)
const nextRank = getServerRank(data)
const remainingSeconds = Number(typeof data === 'object' ? data?.remainingSeconds : NaN)
if (Number.isFinite(nextScore) && nextScore >= 0) {
tick.value = nextScore
}
if (Number.isFinite(nextRank) && nextRank > 0) {
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(remainingSeconds) && remainingSeconds >= 0) {
syncGameCountdown(remainingSeconds)
}
} else if (typeof data === 'object' && data) {
const nextRank = Number(data.rank)
if (Number.isFinite(nextRank) && nextRank > 0) {
const nextRank = getServerRank(data)
if (nextRank > 0) {
rank.value = nextRank
}
showScoreView()
markFinalScoreResult()
}
return { code: 'JGQF', score: tick.value, rank_no: rank.value }
},
onStatus: syncFinalStatus,
onRescoreSubmitted: (_recount) => {
submitScore(true);
submitScore(true)
},
onGameRecover: (data) => {
const recoveredRank = Number(data?.rank)
const recoveredRank = getServerRank(data)
const recoveredScore = Number(data?.score)
const recoveredSeconds = Number(data?.remainingSeconds)
rank.value = Number.isFinite(recoveredRank) && recoveredRank > 0 ? recoveredRank : 0
rank.value = recoveredRank
tick.value = Number.isFinite(recoveredScore) && recoveredScore > 0 ? recoveredScore : 0
showGameRule.value = false
// showGameRank.value = false
currentView.value = 'playing'
roomStatus.value = 2
hasFinalScoreResult.value = false
currentView.value = 'playing'
startGameCountdown(
Number.isFinite(recoveredSeconds) && recoveredSeconds > 0 ? recoveredSeconds : 0,
)
......
......@@ -37,6 +37,8 @@ const countdownInterval = ref(60)
const isDivDescVisible = ref(false)
const score = ref(0)
const rank = ref(0) //排名
const roomStatus = ref(0)
const hasFinalScoreResult = ref(false)
const rankList = ref<RankPlayer[]>([])
const displayRankList = computed<RankPlayer[]>(() => {
const list = rankList.value.slice(0, 10)
......@@ -76,6 +78,56 @@ const confirmRefresh = (event: BeforeUnloadEvent) => {
event.returnValue = ''
}
function getServerRank(data: any, fallback?: unknown) {
const value = typeof data === 'object' && data
? data.rank ?? data.rank_no ?? data.rankNo ?? data.game_rank ?? data.gameRank ?? fallback
: data
const nextRank = Number(value)
return Number.isFinite(nextRank) && nextRank > 0 ? nextRank : 0
}
function showFinalScoreIfReady() {
if (roomStatus.value !== 4 || !hasFinalScoreResult.value) return
hasFinalScoreResult.value = false
showGameOverRank()
}
function markFinalScoreResult() {
hasFinalScoreResult.value = true
showFinalScoreIfReady()
}
function syncFinalStatus(data: any) {
const nextStatus = Number(data?.status)
if (!Number.isFinite(nextStatus)) return
roomStatus.value = nextStatus
if (nextStatus === 3) {
return
}
if (nextStatus !== 4) return
if (Array.isArray(data?.list)) {
rankList.value = data.list.map((item: any, index: number) => ({
rank: getServerRank(item, index + 1),
nickname: String(item?.nickname ?? ''),
avatar: String(item?.avatar ?? ''),
score: Number(item?.score ?? 0),
}))
}
const nextRank = getServerRank(data)
const nextScore = Number(data?.score)
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(nextScore) && nextScore >= 0) {
score.value = nextScore
}
showFinalScoreIfReady()
}
function setDivDescVisible(visible: boolean) {
isDivDescVisible.value = visible
}
......@@ -92,7 +144,7 @@ function submitScore(save: boolean = false, currentTick = score.value) {
if (save) {
//item_num 游戏项目(1到6)
//wechat 原始token
sendGameMessage('submit_score_save', {
const sent = sendGameMessage('submit_score_save', {
score: currentTick,
wechat: wechat.token,
item_num: 2,
......@@ -100,8 +152,14 @@ function submitScore(save: boolean = false, currentTick = score.value) {
avatar: wechat.avatar,
rank: rank.value,
})
if (sent && !userTop10RankStatus.value) {
window.setTimeout(() => {
$toast('成绩提交中,请稍候...', 5000)
}, 0)
}
return sent
} else {
sendGameMessage('submit_score', { score: currentTick })
return sendGameMessage('submit_score', { score: currentTick })
}
}
......@@ -118,6 +176,7 @@ function startGameCountdown(seconds = 60) {
gameCountdownTimer = undefined
currentView.value = 'loading'
setDivDescVisible(false)
hasFinalScoreResult.value = false
submitScore(true)
if (userTop10RankStatus.value) {
userJoinStatus.value = false;
......@@ -125,7 +184,7 @@ function startGameCountdown(seconds = 60) {
$toast('一人只能上榜领奖一次,您的福利已到手,把机会留给其他玩家吧', 30000)
//这里还要再发
} else {
$toast('成绩提交中,请稍候...', 2000)
$toast('成绩提交中,请稍候...', 5000)
}
return
}
......@@ -156,7 +215,11 @@ function resetToLoadingView() {
}
score.value = 0
rank.value = 0
roomStatus.value = 0
hasFinalScoreResult.value = false
rankList.value = []
roomStatus.value = 0
hasFinalScoreResult.value = false
countdownInterval.value = 60
// currentGu.value = guFrames[0]
isGuPlaying.value = false
......@@ -170,9 +233,15 @@ function startGameView() {
stopGameCountdown()
score.value = 0
rank.value = 0
roomStatus.value = 0
hasFinalScoreResult.value = false
rankList.value = []
roomStatus.value = 0
hasFinalScoreResult.value = false
// currentGu.value = guFrames[0]
showGameRank.value = false
roomStatus.value = 2
hasFinalScoreResult.value = false
currentView.value = 'playing'
startGameCountdown()
}
......@@ -186,9 +255,9 @@ function showGameOverRank() {
}
const touchHandler = (currentScore: number) => {
if (!submitScore(false, currentScore)) return
score.value = currentScore
playGame2Music();
submitScore(false, currentScore)
}
if (wechat) {
......@@ -217,12 +286,12 @@ if (wechat) {
onScoreSubmitted: (is_save, data) => {
if (!is_save) {//非保存状态下
const nextScore = Number(typeof data === 'object' ? data?.score : NaN)
const nextRank = Number(typeof data === 'object' ? data?.rank : data)
const nextRank = getServerRank(data)
const remainingSeconds = Number(typeof data === 'object' ? data?.remainingSeconds : NaN)
if (Number.isFinite(nextScore) && nextScore >= 0) {
score.value = nextScore
}
if (Number.isFinite(nextRank) && nextRank > 0) {
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(remainingSeconds) && remainingSeconds >= 0) {
......@@ -231,36 +300,37 @@ if (wechat) {
} else if (typeof data === 'object' && data) {
if (Array.isArray(data.list)) {
rankList.value = data.list.map((item: any, index: number) => ({
rank: Number(item?.rank ?? index + 1),
rank: getServerRank(item, index + 1),
nickname: String(item?.nickname ?? ''),
avatar: String(item?.avatar ?? ''),
score: Number(item?.score ?? 0),
}))
}
const nextRank = Number(data.rank)
if (Number.isFinite(nextRank) && nextRank > 0) {
const nextRank = getServerRank(data)
if (nextRank > 0) {
rank.value = nextRank
}
if (!userTop10RankStatus.value) {
showGameOverRank()
}
markFinalScoreResult()
}
return { code: 'JBJF', score: score.value, rank_no: rank.value }
},
onStatus: syncFinalStatus,
onRescoreSubmitted: (_recount) => {
submitScore(true);
submitScore(true)
},
onGameRecover: (data) => {
const recoveredRank = Number(data?.rank)
const recoveredRank = getServerRank(data)
const recoveredScore = Number(data?.score)
const recoveredSeconds = Number(data?.remainingSeconds)
rank.value = Number.isFinite(recoveredRank) && recoveredRank > 0 ? recoveredRank : 0
rank.value = recoveredRank
score.value = Number.isFinite(recoveredScore) && recoveredScore > 0 ? recoveredScore : 0
showGameRule.value = false
// showGameRank.value = false
currentView.value = 'playing'
roomStatus.value = 2
hasFinalScoreResult.value = false
currentView.value = 'playing'
startGameCountdown(
Number.isFinite(recoveredSeconds) && recoveredSeconds > 0 ? recoveredSeconds : 0,
)
......
<script setup lang="ts">
import { onBeforeUnmount, ref } from 'vue';
import { isGameSocketConnected } from '@/composables/useGameSocket';
defineProps<{
imageUrls: Record<string, string>
......@@ -74,6 +75,9 @@ function rotateCoin(event: PointerEvent) {
while (clockwiseAngle >= FULL_ROTATION) {
clockwiseAngle -= FULL_ROTATION
if (!isGameSocketConnected.value) {
return
}
score.value += 5
emit('touch', score.value)
}
......
......@@ -48,6 +48,8 @@ const countdownInterval = ref(60)
const isDivDescVisible = ref(false)
const score = ref(0)
const rank = ref(0) //排名
const roomStatus = ref(0)
const hasFinalScoreResult = ref(false)
const rankList = ref<RankPlayer[]>([])
const displayRankList = computed<RankPlayer[]>(() => {
const list = rankList.value.slice(0, 10)
......@@ -88,6 +90,56 @@ const confirmRefresh = (event: BeforeUnloadEvent) => {
event.returnValue = ''
}
function getServerRank(data: any, fallback?: unknown) {
const value = typeof data === 'object' && data
? data.rank ?? data.rank_no ?? data.rankNo ?? data.game_rank ?? data.gameRank ?? fallback
: data
const nextRank = Number(value)
return Number.isFinite(nextRank) && nextRank > 0 ? nextRank : 0
}
function showFinalScoreIfReady() {
if (roomStatus.value !== 4 || !hasFinalScoreResult.value) return
hasFinalScoreResult.value = false
showGameOverRank()
}
function markFinalScoreResult() {
hasFinalScoreResult.value = true
showFinalScoreIfReady()
}
function syncFinalStatus(data: any) {
const nextStatus = Number(data?.status)
if (!Number.isFinite(nextStatus)) return
roomStatus.value = nextStatus
if (nextStatus === 3) {
return
}
if (nextStatus !== 4) return
if (Array.isArray(data?.list)) {
rankList.value = data.list.map((item: any, index: number) => ({
rank: getServerRank(item, index + 1),
nickname: String(item?.nickname ?? ''),
avatar: String(item?.avatar ?? ''),
score: Number(item?.score ?? 0),
}))
}
const nextRank = getServerRank(data)
const nextScore = Number(data?.score)
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(nextScore) && nextScore >= 0) {
score.value = nextScore
}
showFinalScoreIfReady()
}
function setDivDescVisible(visible: boolean) {
isDivDescVisible.value = visible
}
......@@ -100,13 +152,13 @@ function stopGameCountdown() {
}
function submitScore(save: boolean = false, currentTick = score.value) {
if (!wechat && !isLocalMode) return
if (!wechat && !isLocalMode) return false
if (save) {
if (isLocalMode && !wechat) {
console.log('[game3 本地模式] 提交最终分数:', currentTick)
return
return true
}
sendGameMessage('submit_score_save', {
const sent = sendGameMessage('submit_score_save', {
score: currentTick,
wechat: wechat.token,
item_num: 3,
......@@ -114,12 +166,18 @@ function submitScore(save: boolean = false, currentTick = score.value) {
avatar: wechat.avatar,
rank: rank.value,
})
if (sent && !userTop10RankStatus.value) {
window.setTimeout(() => {
$toast('成绩提交中,请稍候...', 5000)
}, 0)
}
return sent
} else {
if (isLocalMode && !wechat) {
console.log('[game3 本地模式] 实时分数:', currentTick)
return
return true
}
sendGameMessage('submit_score', { score: currentTick })
return sendGameMessage('submit_score', { score: currentTick })
}
}
......@@ -136,13 +194,14 @@ function startGameCountdown(seconds = 60) {
gameCountdownTimer = undefined
currentView.value = 'loading'
setDivDescVisible(false)
hasFinalScoreResult.value = false
submitScore(true)
if (userTop10RankStatus.value) {
userJoinStatus.value = false;
showGameRank.value = false;
$toast('一人只能上榜领奖一次,您的福利已到手,把机会留给其他玩家吧', 30000)
} else {
$toast('成绩提交中,请稍候...', 2000)
$toast('成绩提交中,请稍候...', 5000)
}
return
}
......@@ -173,7 +232,11 @@ function resetToLoadingView() {
}
score.value = 0
rank.value = 0
roomStatus.value = 0
hasFinalScoreResult.value = false
rankList.value = []
roomStatus.value = 0
hasFinalScoreResult.value = false
countdownInterval.value = 60
// currentGu.value = guFrames[0]
isGuPlaying.value = false
......@@ -187,9 +250,15 @@ function startGameView() {
stopGameCountdown()
score.value = 0
rank.value = 0
roomStatus.value = 0
hasFinalScoreResult.value = false
rankList.value = []
roomStatus.value = 0
hasFinalScoreResult.value = false
// currentGu.value = guFrames[0]
showGameRank.value = false
roomStatus.value = 2
hasFinalScoreResult.value = false
currentView.value = 'playing'
startGameCountdown()
}
......@@ -209,20 +278,20 @@ const touchHandler = (mole: boolean) => {
let scoreDelta = mole ? 5 : -3;
let nextTick = score.value + scoreDelta
if (nextTick < 0) nextTick = 0
if (!submitScore(false, nextTick)) return
score.value = nextTick
if (mole) {
playGame3Music(1);
} else {
playGame3Music(2);
}
submitScore(false, nextTick)
}
const scoreChangeHandler = (delta: number) => {
let nextTick = score.value + delta
if (nextTick < 0) nextTick = 0
if (!submitScore(false, nextTick)) return
score.value = nextTick
submitScore(false, nextTick)
}
if (wechat) {
......@@ -245,12 +314,12 @@ if (wechat) {
onScoreSubmitted: (is_save, data) => {
if (!is_save) {//非保存状态下
const nextScore = Number(typeof data === 'object' ? data?.score : NaN)
const nextRank = Number(typeof data === 'object' ? data?.rank : data)
const nextRank = getServerRank(data)
const remainingSeconds = Number(typeof data === 'object' ? data?.remainingSeconds : NaN)
if (Number.isFinite(nextScore) && nextScore >= 0) {
score.value = nextScore
}
if (Number.isFinite(nextRank) && nextRank > 0) {
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(remainingSeconds) && remainingSeconds >= 0) {
......@@ -259,36 +328,37 @@ if (wechat) {
} else if (typeof data === 'object' && data) {
if (Array.isArray(data.list)) {
rankList.value = data.list.map((item: any, index: number) => ({
rank: Number(item?.rank ?? index + 1),
rank: getServerRank(item, index + 1),
nickname: String(item?.nickname ?? ''),
avatar: String(item?.avatar ?? ''),
score: Number(item?.score ?? 0),
}))
}
const nextRank = Number(data.rank)
if (Number.isFinite(nextRank) && nextRank > 0) {
const nextRank = getServerRank(data)
if (nextRank > 0) {
rank.value = nextRank
}
if (!userTop10RankStatus.value) {
showGameOverRank()
}
markFinalScoreResult()
}
return { code: 'MSYF', score: score.value , rank_no: rank.value}
},
onStatus: syncFinalStatus,
onRescoreSubmitted: (_recount) => {
submitScore(true);
submitScore(true)
},
onGameRecover: (data) => {
const recoveredRank = Number(data?.rank)
const recoveredRank = getServerRank(data)
const recoveredScore = Number(data?.score)
const recoveredSeconds = Number(data?.remainingSeconds)
rank.value = Number.isFinite(recoveredRank) && recoveredRank > 0 ? recoveredRank : 0
rank.value = recoveredRank
score.value = Number.isFinite(recoveredScore) && recoveredScore > 0 ? recoveredScore : 0
showGameRule.value = false
showGameRank.value = false
currentView.value = 'playing'
roomStatus.value = 2
hasFinalScoreResult.value = false
currentView.value = 'playing'
startGameCountdown(
Number.isFinite(recoveredSeconds) && recoveredSeconds > 0 ? recoveredSeconds : 0,
)
......@@ -318,7 +388,9 @@ onMounted(() => {
// TODO_LOCAL: 本地模拟游戏开始,稍后部署上线前删除此段
if ($is_run_local()) {
showGameRule.value = false
currentView.value = 'playing'
roomStatus.value = 2
hasFinalScoreResult.value = false
currentView.value = 'playing'
startGameCountdown()
}
})
......
......@@ -59,6 +59,8 @@ const countdownInterval = ref(60);
const isDivDescVisible = ref(false);
const tick = ref(0); // 点击次数/摇动次数
const rank = ref(0); // 排名
const roomStatus = ref(0)
const hasFinalScoreResult = ref(false)
const currentHorse = ref(imageUrls.horse);
const currentYaoyiyao = ref(imageUrls.yaoyiyao);
const token = ref(wechat?.token ?? "");
......@@ -76,6 +78,48 @@ const confirmRefresh = (event: BeforeUnloadEvent) => {
event.returnValue = "";
};
function getServerRank(data: any, fallback?: unknown) {
const value =
typeof data === "object" && data
? data.rank ?? data.rank_no ?? data.rankNo ?? data.game_rank ?? data.gameRank ?? fallback
: data;
const nextRank = Number(value);
return Number.isFinite(nextRank) && nextRank > 0 ? nextRank : 0;
}
function showFinalScoreIfReady() {
if (roomStatus.value !== 4 || !hasFinalScoreResult.value) return
hasFinalScoreResult.value = false
showScoreView()
}
function markFinalScoreResult() {
hasFinalScoreResult.value = true
showFinalScoreIfReady()
}
function syncFinalStatus(data: any) {
const nextStatus = Number(data?.status)
if (!Number.isFinite(nextStatus)) return
roomStatus.value = nextStatus
if (nextStatus === 3) {
return
}
if (nextStatus !== 4) return
const nextRank = getServerRank(data)
const nextScore = Number(data?.score)
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(nextScore) && nextScore >= 0) {
tick.value = nextScore
}
showFinalScoreIfReady()
}
function setDivDescVisible(visible: boolean) {
isDivDescVisible.value = visible;
}
......@@ -87,13 +131,13 @@ function stopGameCountdown() {
}
}
function submitScore(save: boolean = false) {
function submitScore(save: boolean = false, currentTick = tick.value) {
// 最后提交保存在数据库中
if (save) {
// item_num 游戏项目(1到6)
// wechat 原始token
sendGameMessage("submit_score_save", {
score: tick.value,
return sendGameMessage("submit_score_save", {
score: currentTick,
wechat: wechat.token,
item_num: 4,
nickname: wechat.nickname,
......@@ -101,7 +145,7 @@ function submitScore(save: boolean = false) {
rank: rank.value,
});
} else {
sendGameMessage("submit_score", { score: tick.value });
return sendGameMessage("submit_score", { score: currentTick });
}
}
......@@ -123,7 +167,7 @@ function startGameCountdown(seconds = 60) {
// showGameRank.value = false;
$toast('一人只能上榜领奖一次,您的福利已到手,把机会留给其他玩家吧', 30000)
} else {
$toast('成绩提交中,请稍候...', 2000)
$toast('成绩提交中,请稍候...', 5000)
}
return;
}
......@@ -189,8 +233,9 @@ function backToWaiting() { }
function handleRoomBack() { }
const touchHandler = () => {
tick.value += 5;
submitScore();
const nextTick = tick.value + 5;
if (!submitScore(false, nextTick)) return;
tick.value = nextTick;
};
const showGameRuleHandler = () => {
......@@ -233,7 +278,7 @@ if (wechat || isLocalMode) {
},
onScoreSubmitted: (is_save, data) => {
const nextScore = Number(typeof data === "object" ? data?.score : NaN);
const nextRank = Number(typeof data === "object" ? data?.rank : data);
const nextRank = getServerRank(data);
const remainingSeconds = Number(
typeof data === "object" ? data?.remainingSeconds : NaN,
);
......@@ -247,17 +292,18 @@ if (wechat || isLocalMode) {
) {
syncGameCountdown(remainingSeconds);
}
if (Number.isFinite(nextRank) && nextRank > 0) {
if (nextRank > 0) {
rank.value = nextRank;
}
if (is_save && !userTop10RankStatus.value) {
showScoreView();
if (is_save) {
markFinalScoreResult();
}
return { code: "CMYF", score: tick.value, rank_no: rank.value };
},
onRescoreSubmitted: (_recount) => {
submitScore(true);
},
onStatus: syncFinalStatus,
onRescoreSubmitted: (_recount) => {
submitScore(true)
},
onRoomClosed: showLoadingView,
onRoomBack: handleRoomBack,
onGameRecover: () => {
......
......@@ -86,6 +86,8 @@ const countdownInterval = ref(60)
const isDivDescVisible = ref(false)
const tick = ref(0) //点击次数
const rank = ref(0) //排名
const roomStatus = ref(0)
const hasFinalScoreResult = ref(false)
const guFrames: [string, string, string] = [imageUrls.gu01, imageUrls.gu02, imageUrls.gu03]
const currentGu = ref(imageUrls.gu01)
const isGuPlaying = ref(false)
......@@ -118,6 +120,56 @@ const confirmRefresh = (event: BeforeUnloadEvent) => {
event.returnValue = ''
}
function getServerRank(data: any, fallback?: unknown) {
const value = typeof data === 'object' && data
? data.rank ?? data.rank_no ?? data.rankNo ?? data.game_rank ?? data.gameRank ?? fallback
: data
const nextRank = Number(value)
return Number.isFinite(nextRank) && nextRank > 0 ? nextRank : 0
}
function showFinalScoreIfReady() {
if (roomStatus.value !== 4 || !hasFinalScoreResult.value) return
hasFinalScoreResult.value = false
showGameOverRank()
}
function markFinalScoreResult() {
hasFinalScoreResult.value = true
showFinalScoreIfReady()
}
function syncFinalStatus(data: any) {
const nextStatus = Number(data?.status)
if (!Number.isFinite(nextStatus)) return
roomStatus.value = nextStatus
if (nextStatus === 3) {
return
}
if (nextStatus !== 4) return
if (Array.isArray(data?.list)) {
rankList.value = data.list.map((item: any, index: number) => ({
rank: getServerRank(item, index + 1),
nickname: String(item?.nickname ?? ''),
avatar: String(item?.avatar ?? ''),
score: Number(item?.score ?? 0),
}))
}
const nextRank = getServerRank(data)
const nextScore = Number(data?.score)
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(nextScore) && nextScore >= 0) {
tick.value = nextScore
}
showFinalScoreIfReady()
}
function setDivDescVisible(visible: boolean) {
isDivDescVisible.value = visible
}
......@@ -129,13 +181,13 @@ function stopGameCountdown() {
}
}
function submitScore(save: boolean = false) {
function submitScore(save: boolean = false, currentTick = tick.value) {
//最后提交保存在数据库中
if (save) {
//item_num 游戏项目(1到6)
//wechat 原始token
const data = {
score: tick.value,
score: currentTick,
wechat: wechat.token,
item_num: 5,
nickname: wechat.nickname,
......@@ -143,11 +195,11 @@ function submitScore(save: boolean = false) {
rank: rank.value,
}
console.log('[WebSocket] send submit_score_save:', JSON.stringify(data, null, 2))
sendGameMessage('submit_score_save', data)
return sendGameMessage('submit_score_save', data)
} else {
const data = { score: tick.value }
const data = { score: currentTick }
console.log('[WebSocket] send submit_score:', JSON.stringify(data))
sendGameMessage('submit_score', data)
return sendGameMessage('submit_score', data)
}
}
......@@ -163,13 +215,14 @@ function startGameCountdown(seconds = 60) {
countdownInterval.value = 0
gameCountdownTimer = undefined
setDivDescVisible(false)
hasFinalScoreResult.value = false
submitScore(true)
if (userTop10RankStatus.value) {
userJoinStatus.value = false;
// showGameRank.value = false;
$toast('一人只能上榜领奖一次,您的福利已到手,把机会留给其他玩家吧', 30000)
} else {
$toast('成绩提交中,请稍候...', 2000)
$toast('成绩提交中,请稍候...', 5000)
}
return
}
......@@ -210,7 +263,11 @@ function resetToLoadingView() {
}
tick.value = 0
rank.value = 0
roomStatus.value = 0
hasFinalScoreResult.value = false
rankList.value = []
roomStatus.value = 0
hasFinalScoreResult.value = false
countdownInterval.value = 60
currentGu.value = guFrames[0]
isGuPlaying.value = false
......@@ -224,9 +281,15 @@ function startGameView() {
stopGameCountdown()
tick.value = 0
rank.value = 0
roomStatus.value = 0
hasFinalScoreResult.value = false
rankList.value = []
roomStatus.value = 0
hasFinalScoreResult.value = false
currentGu.value = guFrames[0]
showGameRank.value = false
roomStatus.value = 2
hasFinalScoreResult.value = false
currentView.value = 'playing'
startGameCountdown()
}
......@@ -244,9 +307,10 @@ function handleRoomBack() {
}
const hitHandler = (score: number) => {
tick.value += score
const nextTick = tick.value + score
if (!submitScore(false, nextTick)) return
tick.value = nextTick
console.log('[WebSocket] 圈中马!得分:', score, '当前总分:', tick.value)
submitScore()
// console.log('[Audio] playGame1Music() 被调用 - 播放 game1.mp3 背景音乐')
// playGame1Music()
}
......@@ -275,12 +339,12 @@ if (wechat) {
onScoreSubmitted: (is_save, data) => {
if (!is_save) {//非保存状态下
const nextScore = Number(typeof data === 'object' ? data?.score : NaN)
const nextRank = Number(typeof data === 'object' ? data?.rank : data)
const nextRank = getServerRank(data)
const remainingSeconds = Number(typeof data === 'object' ? data?.remainingSeconds : NaN)
if (Number.isFinite(nextScore) && nextScore >= 0) {
tick.value = nextScore
}
if (Number.isFinite(nextRank) && nextRank > 0) {
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(remainingSeconds) && remainingSeconds >= 0) {
......@@ -289,25 +353,24 @@ if (wechat) {
} else if (typeof data === 'object' && data) {
if (Array.isArray(data.list)) {
rankList.value = data.list.map((item: any, index: number) => ({
rank: Number(item?.rank ?? index + 1),
rank: getServerRank(item, index + 1),
nickname: String(item?.nickname ?? ''),
avatar: String(item?.avatar ?? ''),
score: Number(item?.score ?? 0),
}))
}
const nextRank = Number(data.rank)
if (Number.isFinite(nextRank) && nextRank > 0) {
const nextRank = getServerRank(data)
if (nextRank > 0) {
rank.value = nextRank
}
if (!userTop10RankStatus.value) {
showGameOverRank()
}
markFinalScoreResult()
}
return { code: 'QCNF', score: tick.value, rank_no: rank.value }
},
onStatus: syncFinalStatus,
onRescoreSubmitted: (_recount) => {
submitScore(true);
submitScore(true)
},
onRoomClosed: showLoadingView,
onRoomBack: handleRoomBack,
......
......@@ -44,6 +44,8 @@ const countdownInterval = ref(60)
const isDivDescVisible = ref(false)
const score = ref(0)
const rank = ref(0) //排名
const roomStatus = ref(0)
const hasFinalScoreResult = ref(false)
const rankList = ref<RankPlayer[]>([])
const displayRankList = computed<RankPlayer[]>(() => {
const list = rankList.value.slice(0, 10)
......@@ -83,6 +85,56 @@ const confirmRefresh = (event: BeforeUnloadEvent) => {
event.returnValue = ''
}
function getServerRank(data: any, fallback?: unknown) {
const value = typeof data === 'object' && data
? data.rank ?? data.rank_no ?? data.rankNo ?? data.game_rank ?? data.gameRank ?? fallback
: data
const nextRank = Number(value)
return Number.isFinite(nextRank) && nextRank > 0 ? nextRank : 0
}
function showFinalScoreIfReady() {
if (roomStatus.value !== 4 || !hasFinalScoreResult.value) return
hasFinalScoreResult.value = false
showGameOverRank()
}
function markFinalScoreResult() {
hasFinalScoreResult.value = true
showFinalScoreIfReady()
}
function syncFinalStatus(data: any) {
const nextStatus = Number(data?.status)
if (!Number.isFinite(nextStatus)) return
roomStatus.value = nextStatus
if (nextStatus === 3) {
return
}
if (nextStatus !== 4) return
if (Array.isArray(data?.list)) {
rankList.value = data.list.map((item: any, index: number) => ({
rank: getServerRank(item, index + 1),
nickname: String(item?.nickname ?? ''),
avatar: String(item?.avatar ?? ''),
score: Number(item?.score ?? 0),
}))
}
const nextRank = getServerRank(data)
const nextScore = Number(data?.score)
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(nextScore) && nextScore >= 0) {
score.value = nextScore
}
showFinalScoreIfReady()
}
function setDivDescVisible(visible: boolean) {
isDivDescVisible.value = visible
}
......@@ -99,7 +151,7 @@ function submitScore(save: boolean = false, currentTick = score.value) {
if (save) {
//item_num 游戏项目(1到6)
//wechat 原始token
sendGameMessage('submit_score_save', {
const sent = sendGameMessage('submit_score_save', {
score: currentTick,
wechat: wechat.token,
item_num: 6,
......@@ -107,8 +159,14 @@ function submitScore(save: boolean = false, currentTick = score.value) {
avatar: wechat.avatar,
rank: rank.value,
})
if (sent && !userTop10RankStatus.value) {
window.setTimeout(() => {
$toast('成绩提交中,请稍候...', 5000)
}, 0)
}
return sent
} else {
sendGameMessage('submit_score', { score: currentTick })
return sendGameMessage('submit_score', { score: currentTick })
}
}
......@@ -125,13 +183,14 @@ function startGameCountdown(seconds = 60) {
gameCountdownTimer = undefined
currentView.value = 'loading'
setDivDescVisible(false)
hasFinalScoreResult.value = false
submitScore(true)
if (userTop10RankStatus.value) {
userJoinStatus.value = false;
showGameRank.value = false;
$toast('一人只能上榜领奖一次,您的福利已到手,把机会留给其他玩家吧', 30000)
} else {
$toast('成绩提交中,请稍候...', 2000)
$toast('成绩提交中,请稍候...', 5000)
}
return
}
......@@ -162,7 +221,11 @@ function resetToLoadingView() {
}
score.value = 0
rank.value = 0
roomStatus.value = 0
hasFinalScoreResult.value = false
rankList.value = []
roomStatus.value = 0
hasFinalScoreResult.value = false
countdownInterval.value = 60
// currentGu.value = guFrames[0]
isGuPlaying.value = false
......@@ -176,9 +239,15 @@ function startGameView() {
stopGameCountdown()
score.value = 0
rank.value = 0
roomStatus.value = 0
hasFinalScoreResult.value = false
rankList.value = []
roomStatus.value = 0
hasFinalScoreResult.value = false
// currentGu.value = guFrames[0]
showGameRank.value = false
roomStatus.value = 2
hasFinalScoreResult.value = false
currentView.value = 'playing'
startGameCountdown()
}
......@@ -194,10 +263,10 @@ function showGameOverRank() {
const touchHandler = (mole: boolean) => {
let scoreDelta = mole ? 5 : 0;
let nextTick = score.value + scoreDelta
score.value = nextTick
if (mole) {
if (!submitScore(false, nextTick)) return
score.value = nextTick
playGame6Music(1);
submitScore(false, nextTick)
} else {
playGame6Music(2);
}
......@@ -223,12 +292,12 @@ if (wechat) {
onScoreSubmitted: (is_save, data) => {
if (!is_save) {//非保存状态下
const nextScore = Number(typeof data === 'object' ? data?.score : NaN)
const nextRank = Number(typeof data === 'object' ? data?.rank : data)
const nextRank = getServerRank(data)
const remainingSeconds = Number(typeof data === 'object' ? data?.remainingSeconds : NaN)
if (Number.isFinite(nextScore) && nextScore >= 0) {
score.value = nextScore
}
if (Number.isFinite(nextRank) && nextRank > 0) {
if (nextRank > 0) {
rank.value = nextRank
}
if (Number.isFinite(remainingSeconds) && remainingSeconds >= 0) {
......@@ -237,36 +306,37 @@ if (wechat) {
} else if (typeof data === 'object' && data) {
if (Array.isArray(data.list)) {
rankList.value = data.list.map((item: any, index: number) => ({
rank: Number(item?.rank ?? index + 1),
rank: getServerRank(item, index + 1),
nickname: String(item?.nickname ?? ''),
avatar: String(item?.avatar ?? ''),
score: Number(item?.score ?? 0),
}))
}
const nextRank = Number(data.rank)
if (Number.isFinite(nextRank) && nextRank > 0) {
const nextRank = getServerRank(data)
if (nextRank > 0) {
rank.value = nextRank
}
if (!userTop10RankStatus.value) {
showGameOverRank()
}
markFinalScoreResult()
}
return { code: 'FYDT', score: score.value, rank_no: rank.value }
},
onStatus: syncFinalStatus,
onRescoreSubmitted: (_recount) => {
submitScore(true);
submitScore(true)
},
onGameRecover: (data) => {
const recoveredRank = Number(data?.rank)
const recoveredRank = getServerRank(data)
const recoveredScore = Number(data?.score)
const recoveredSeconds = Number(data?.remainingSeconds)
rank.value = Number.isFinite(recoveredRank) && recoveredRank > 0 ? recoveredRank : 0
rank.value = recoveredRank
score.value = Number.isFinite(recoveredScore) && recoveredScore > 0 ? recoveredScore : 0
showGameRule.value = false
showGameRank.value = false
currentView.value = 'playing'
roomStatus.value = 2
hasFinalScoreResult.value = false
currentView.value = 'playing'
startGameCountdown(
Number.isFinite(recoveredSeconds) && recoveredSeconds > 0 ? recoveredSeconds : 0,
)
......
......@@ -24,11 +24,16 @@ type AdminGameSocketOptions = {
export function useAdminGameSocket(options: AdminGameSocketOptions) {
let offSocketMessage: (() => void) | undefined
let statusTimer: ReturnType<typeof window.setInterval> | undefined
function requestRoomState() {
return sendSocketMessage('room_state')
}
function requestStatus() {
return sendSocketMessage('status')
}
function createRoom(index: number) {
return sendSocketMessage('room_create', index)
}
......@@ -65,9 +70,15 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
}
}
window.setTimeout(requestRoomState, 200)
window.setTimeout(requestStatus, 200)
statusTimer = window.setInterval(requestStatus, 1000)
},
onDisconnect: (_reason: string) => {
console.log('[useAdminGameSocket] WebSocket 断开连接,reason:', _reason, 'gameId:', options.gameId);
console.log('[useAdminGameSocket] WebSocket disconnected:', _reason, 'gameId:', options.gameId);
if (statusTimer) {
window.clearInterval(statusTimer)
statusTimer = undefined
}
},
onError: (error: any) => {
console.error('[useAdminGameSocket] WebSocket 错误:', error, 'gameId:', options.gameId);
......@@ -97,7 +108,8 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
}
switch (evt) {
case 'room_state_result': {
case 'room_state_result':
case 'status_result': {
if (data.gameId) {
//如果这两个值不一致说明浏览器有直接更新游戏房间号
if (data.gameId != options.gameId) {
......@@ -134,11 +146,16 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
})
onBeforeUnmount(() => {
if (statusTimer) {
window.clearInterval(statusTimer)
statusTimer = undefined
}
offSocketMessage?.()
})
return {
requestRoomState,
requestStatus,
createRoom,
backRoom,
startGame,
......
......@@ -38,6 +38,9 @@ const playerCount = ref(0)
const rankPlayers = ref<Player[]>(Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index)))
const rank1Ref = ref<InstanceType<typeof Rank1> | null>(null)
let isPlayingStartAnimation = false
const isSubmitWaiting = ref(false)
const submitWaitingSeconds = ref(5)
let submitWaitingTimer: ReturnType<typeof window.setInterval> | undefined
const confirmRefresh = (event: BeforeUnloadEvent) => {
// closeRoom();
......@@ -87,11 +90,37 @@ const seedRankPlayersFromLoading = () => {
)
}
function showSubmitWaiting() {
if (!isSubmitWaiting.value) { submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
}
submitWaitingTimer = window.setInterval(() => {
submitWaitingSeconds.value = Math.max(0, submitWaitingSeconds.value - 1)
if (submitWaitingSeconds.value <= 0 && submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}, 1000)
}
isSubmitWaiting.value = true
}
function clearSubmitWaiting() {
isSubmitWaiting.value = false
submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}
const resetRoundState = () => {
playerCount.value = 0
loadingPlayers.value = Array.from({ length: PLAYER_LIMIT }, emptyLoadingPlayer)
rankPlayers.value = Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index))
isPlayingStartAnimation = false
clearSubmitWaiting()
}
const gotoLoading = (reset = false) => {
......@@ -110,7 +139,7 @@ const gotoRank1WithIntro = async () => {
screen.value = 'rank1'
await nextTick()
await rank1Ref.value?.startIntro()
screen.value = 'rank2'
showSubmitWaiting()
isPlayingStartAnimation = false
}
......@@ -118,7 +147,7 @@ const gameId = 'game1'
const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
gameId,
onRoomState: (data: any) => {
if (Array.isArray(data?.list)) {
if ((data?.status === 0 || data?.status === 1) && Array.isArray(data?.list)) {
renderPlayers(data)
}
switch (data?.status) {
......@@ -133,10 +162,17 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
gotoLoading()
break
case 2:
seedRankPlayersFromLoading()
screen.value = 'rank1'
screen.value = 'rank1'
clearSubmitWaiting()
break
case 3:
showSubmitWaiting()
break
case 4:
clearSubmitWaiting()
if (Array.isArray(data?.list)) {
updateRankPlayers(data.list)
}
screen.value = 'rank2'
break
}
......@@ -211,4 +247,66 @@ watch(screen, (value) => {
@start="startGameWithMusic" @back="backHandler" />
<Rank1 v-else-if="screen === 'rank1'" ref="rank1Ref" :rank-list="rankPlayers" @start="startGameWithMusic" />
<Rank2 v-else :rank-list="rankPlayers" @next="nextRoundWithMusic" @back="backHandler" />
<div v-if="isSubmitWaiting" class="submit-waiting-overlay">
<div class="submit-waiting-title">正在等待玩家提交分数</div>
<div :key="submitWaitingSeconds" class="submit-waiting-count">{{ submitWaitingSeconds }}</div>
</div>
</template>
<style scoped>
.submit-waiting-overlay {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.22) 0%, rgba(0, 0, 0, 0.08) 48%, transparent 72%);
color: #fff;
pointer-events: none;
}
.submit-waiting-title {
position: absolute;
bottom: 8vh;
left: 0;
width: 100%;
font-size: 56px;
font-weight: 700;
line-height: 1;
text-align: center;
text-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
}
.submit-waiting-count {
color: white;
font-size: 300pt;
font-weight: 800;
line-height: 1;
text-shadow: 0 18px 38px rgba(0, 0, 0, 0.25);
animation: submit-countdown-pop 1s ease both;
}
@keyframes submit-countdown-pop {
0% {
opacity: 0;
transform: scale(0.45);
}
18% {
opacity: 1;
transform: scale(1.14);
}
58% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.84);
}
}
</style>
......@@ -52,6 +52,9 @@ const playerCount = ref(0)
const rankPlayers = ref<Player[]>(Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index)))
const rank1Ref = ref<InstanceType<typeof Rank1> | null>(null)
let isPlayingStartAnimation = false
const isSubmitWaiting = ref(false)
const submitWaitingSeconds = ref(5)
let submitWaitingTimer: ReturnType<typeof window.setInterval> | undefined
const confirmRefresh = (event: BeforeUnloadEvent) => {
// closeRoom();
......@@ -117,11 +120,37 @@ const seedRankPlayersFromLoading = () => {
)
}
function showSubmitWaiting() {
if (!isSubmitWaiting.value) { submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
}
submitWaitingTimer = window.setInterval(() => {
submitWaitingSeconds.value = Math.max(0, submitWaitingSeconds.value - 1)
if (submitWaitingSeconds.value <= 0 && submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}, 1000)
}
isSubmitWaiting.value = true
}
function clearSubmitWaiting() {
isSubmitWaiting.value = false
submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}
const resetRoundState = () => {
playerCount.value = 0
loadingPlayers.value = Array.from({ length: PLAYER_LIMIT }, emptyLoadingPlayer)
rankPlayers.value = Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index))
isPlayingStartAnimation = false
clearSubmitWaiting()
}
const gotoLoading = (reset = false) => {
......@@ -140,7 +169,7 @@ const gotoRank1WithIntro = async () => {
screen.value = 'rank1'
await nextTick()
await rank1Ref.value?.startIntro()
screen.value = 'rank2'
showSubmitWaiting()
isPlayingStartAnimation = false
}
......@@ -152,7 +181,7 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
},
onRoomState: (data: any) => {
console.log("[Game2] onRoomState 触发,data:", JSON.stringify(data));
if (Array.isArray(data?.list)) {
if ((data?.status === 0 || data?.status === 1) && Array.isArray(data?.list)) {
renderPlayers(data)
}
switch (data?.status) {
......@@ -168,11 +197,18 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
break
case 2:
console.log("[Game2] status=2,准备进入 rank1,loadingPlayers:", loadingPlayers.value.length);
seedRankPlayersFromLoading()
console.log("[Game2] seedRankPlayersFromLoading 后,rankPlayers:", rankPlayers.value.length, JSON.stringify(rankPlayers.value.slice(0, 3)));
console.log("[Game2] seedRankPlayersFromLoading 后,rankPlayers:", rankPlayers.value.length, JSON.stringify(rankPlayers.value.slice(0, 3)));
screen.value = 'rank1'
clearSubmitWaiting()
break
case 3:
showSubmitWaiting()
break
case 4:
clearSubmitWaiting()
if (Array.isArray(data?.list)) {
updateRankPlayers(data.list)
}
screen.value = 'rank2'
break
}
......@@ -255,4 +291,66 @@ watch(screen, (value) => {
@start="startGameWithMusic" @back="backHandler" />
<Rank1 v-else-if="screen === 'rank1'" ref="rank1Ref" :rank-list="rankPlayers" @start="startGameWithMusic" />
<Rank2 v-else :rank-list="rankPlayers" @next="nextRoundWithMusic" @back="backHandler" />
<div v-if="isSubmitWaiting" class="submit-waiting-overlay">
<div class="submit-waiting-title">正在等待玩家提交分数</div>
<div :key="submitWaitingSeconds" class="submit-waiting-count">{{ submitWaitingSeconds }}</div>
</div>
</template>
<style scoped>
.submit-waiting-overlay {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.22) 0%, rgba(0, 0, 0, 0.08) 48%, transparent 72%);
color: #fff;
pointer-events: none;
}
.submit-waiting-title {
position: absolute;
bottom: 8vh;
left: 0;
width: 100%;
font-size: 56px;
font-weight: 700;
line-height: 1;
text-align: center;
text-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
}
.submit-waiting-count {
color: white;
font-size: 300pt;
font-weight: 800;
line-height: 1;
text-shadow: 0 18px 38px rgba(0, 0, 0, 0.25);
animation: submit-countdown-pop 1s ease both;
}
@keyframes submit-countdown-pop {
0% {
opacity: 0;
transform: scale(0.45);
}
18% {
opacity: 1;
transform: scale(1.14);
}
58% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.84);
}
}
</style>
......@@ -39,6 +39,9 @@ const rankPlayers = ref<Player[]>(Array.from({ length: RANK_LIMIT }, (_, index)
const horseBoosts = ref<Record<string, number>>({});
const rank1Ref = ref<InstanceType<typeof Rank1> | null>(null)
let isPlayingStartAnimation = false
const isSubmitWaiting = ref(false)
const submitWaitingSeconds = ref(5)
let submitWaitingTimer: ReturnType<typeof window.setInterval> | undefined
const confirmRefresh = (event: BeforeUnloadEvent) => {
event.preventDefault()
......@@ -99,12 +102,38 @@ const updateRankPlayers = (data: any) => {
);
}
function showSubmitWaiting() {
if (!isSubmitWaiting.value) { submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
}
submitWaitingTimer = window.setInterval(() => {
submitWaitingSeconds.value = Math.max(0, submitWaitingSeconds.value - 1)
if (submitWaitingSeconds.value <= 0 && submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}, 1000)
}
isSubmitWaiting.value = true
}
function clearSubmitWaiting() {
isSubmitWaiting.value = false
submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}
const resetRoundState = () => {
playerCount.value = 0
loadingPlayers.value = Array.from({ length: PLAYER_LIMIT }, emptyLoadingPlayer)
rankPlayers.value = Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index))
horseBoosts.value = {}
isPlayingStartAnimation = false
clearSubmitWaiting()
}
const gotoLoading = (reset = false) => {
......@@ -125,13 +154,14 @@ const gotoRank1WithIntro = async () => {
await rank1Ref.value?.startIntro()
isPlayingStartAnimation = false
clearSubmitWaiting()
}
function handleRoomState(data: any) {
console.log("[Game3] onRoomState 触发,data:", JSON.stringify(data));
if (Array.isArray(data?.list)) {
renderPlayers(data)
}
if ((data?.status === 0 || data?.status === 1) && Array.isArray(data?.list)) {
renderPlayers(data)
}
switch (data?.status) {
case 0:
......@@ -144,7 +174,7 @@ function handleRoomState(data: any) {
case 2:
console.log("[Game3] status=2,准备进入 rank1,loadingPlayers:", loadingPlayers.value.length);
// 重连/中途进房时游戏已在运行,直接进入赛马画面
if (isPlayingStartAnimation) break;
if (screen.value === 'rank1' || isPlayingStartAnimation) break;
isPlayingStartAnimation = true;
screen.value = 'rank1'
console.log('[game3] handleRoomState: status=2, 直接启动赛马动画');
......@@ -155,8 +185,15 @@ function handleRoomState(data: any) {
});
break
case 3:
screen.value = 'rank2'
break
showSubmitWaiting()
break
case 4:
clearSubmitWaiting()
if (Array.isArray(data?.list)) {
updateRankPlayers(data.list)
}
screen.value = 'rank2'
break
}
}
const gameId='game3'
......@@ -297,4 +334,66 @@ watch(screen, (value) => {
<Rank1 v-if="screen === 'rank1'" ref="rank1Ref" :rank-list="rankPlayers" :horse-boosts="horseBoosts"
@start="startGameWithMusic" @next="nextRoundWithMusic" @back="backHandler" />
<Rank2 v-if="screen === 'rank2'" :rank-list="rankPlayers" @next="nextRoundWithMusic" @back="backHandler" />
<div v-if="isSubmitWaiting" class="submit-waiting-overlay">
<div class="submit-waiting-title">正在等待玩家提交分数</div>
<div :key="submitWaitingSeconds" class="submit-waiting-count">{{ submitWaitingSeconds }}</div>
</div>
</template>
<style scoped>
.submit-waiting-overlay {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.22) 0%, rgba(0, 0, 0, 0.08) 48%, transparent 72%);
color: #fff;
pointer-events: none;
}
.submit-waiting-title {
position: absolute;
bottom: 8vh;
left: 0;
width: 100%;
font-size: 56px;
font-weight: 700;
line-height: 1;
text-align: center;
text-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
}
.submit-waiting-count {
color: white;
font-size: 300pt;
font-weight: 800;
line-height: 1;
text-shadow: 0 18px 38px rgba(0, 0, 0, 0.25);
animation: submit-countdown-pop 1s ease both;
}
@keyframes submit-countdown-pop {
0% {
opacity: 0;
transform: scale(0.45);
}
18% {
opacity: 1;
transform: scale(1.14);
}
58% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.84);
}
}
</style>
......@@ -39,6 +39,9 @@ const playerCount = ref(0)
const rankPlayers = ref<Player[]>(Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index)))
const rank1Ref = ref<InstanceType<typeof Rank1> | null>(null)
let isPlayingStartAnimation = false
const isSubmitWaiting = ref(false)
const submitWaitingSeconds = ref(5)
let submitWaitingTimer: ReturnType<typeof window.setInterval> | undefined
const confirmRefresh = (event: BeforeUnloadEvent) => {
// closeRoom();
......@@ -105,11 +108,37 @@ const seedRankPlayersFromLoading = () => {
console.log("[Game4] seedRankPlayersFromLoading 完成,rankPlayers:", rankPlayers.value.length, JSON.stringify(rankPlayers.value.slice(0, 3)));
}
function showSubmitWaiting() {
if (!isSubmitWaiting.value) { submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
}
submitWaitingTimer = window.setInterval(() => {
submitWaitingSeconds.value = Math.max(0, submitWaitingSeconds.value - 1)
if (submitWaitingSeconds.value <= 0 && submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}, 1000)
}
isSubmitWaiting.value = true
}
function clearSubmitWaiting() {
isSubmitWaiting.value = false
submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}
const resetRoundState = () => {
playerCount.value = 0
loadingPlayers.value = Array.from({ length: PLAYER_LIMIT }, emptyLoadingPlayer)
rankPlayers.value = Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index))
isPlayingStartAnimation = false
clearSubmitWaiting()
}
const gotoLoading = (reset = false) => {
......@@ -131,6 +160,7 @@ const gotoRank1WithIntro = async () => {
await rank1Ref.value?.startIntro()
// 结果页已在 Rank1View 内部渲染(showResult + result-overlay),不需要切到 rank2
isPlayingStartAnimation = false
clearSubmitWaiting()
}
const horseBoosts = ref<Record<string, number>>({});
......@@ -181,7 +211,7 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
},
onRoomState: (data: any) => {
console.log("[Game4] onRoomState 触发,data:", JSON.stringify(data));
if (Array.isArray(data?.list)) {
if ((data?.status === 0 || data?.status === 1) && Array.isArray(data?.list)) {
renderPlayers(data)
}
switch (data?.status) {
......@@ -197,11 +227,18 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
break
case 2:
console.log("[Game4] status=2,准备进入 rank1,loadingPlayers:", loadingPlayers.value.length);
seedRankPlayersFromLoading()
console.log("[Game4] seedRankPlayersFromLoading 后,rankPlayers:", rankPlayers.value.length, JSON.stringify(rankPlayers.value.slice(0, 3)));
console.log("[Game4] seedRankPlayersFromLoading 后,rankPlayers:", rankPlayers.value.length, JSON.stringify(rankPlayers.value.slice(0, 3)));
screen.value = 'rank1'
clearSubmitWaiting()
break
case 3:
showSubmitWaiting()
break
case 4:
clearSubmitWaiting()
if (Array.isArray(data?.list)) {
updateRankPlayers(data.list)
}
screen.value = 'rank1'
break
}
......@@ -492,4 +529,66 @@ watch(screen, (value) => {
<Rank1 v-if="screen === 'rank1'" ref="rank1Ref" :rank-list="rankPlayers" :horse-boosts="horseBoosts"
@start="startGameWithMusic" @next="nextRound" @back="backHandler" />
<!-- <Rank2 v-else-if="screen === 'rank2'" :rank-list="rankPlayers" @next="nextRound" /> -->
<div v-if="isSubmitWaiting" class="submit-waiting-overlay">
<div class="submit-waiting-title">正在等待玩家提交分数</div>
<div :key="submitWaitingSeconds" class="submit-waiting-count">{{ submitWaitingSeconds }}</div>
</div>
</template>
<style scoped>
.submit-waiting-overlay {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.22) 0%, rgba(0, 0, 0, 0.08) 48%, transparent 72%);
color: #fff;
pointer-events: none;
}
.submit-waiting-title {
position: absolute;
bottom: 8vh;
left: 0;
width: 100%;
font-size: 56px;
font-weight: 700;
line-height: 1;
text-align: center;
text-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
}
.submit-waiting-count {
color: white;
font-size: 300pt;
font-weight: 800;
line-height: 1;
text-shadow: 0 18px 38px rgba(0, 0, 0, 0.25);
animation: submit-countdown-pop 1s ease both;
}
@keyframes submit-countdown-pop {
0% {
opacity: 0;
transform: scale(0.45);
}
18% {
opacity: 1;
transform: scale(1.14);
}
58% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.84);
}
}
</style>
......@@ -53,6 +53,9 @@ const playerCount = ref(0)
const rankPlayers = ref<Player[]>(Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index)))
const rank1Ref = ref<InstanceType<typeof Rank1> | null>(null)
let isPlayingStartAnimation = false
const isSubmitWaiting = ref(false)
const submitWaitingSeconds = ref(5)
let submitWaitingTimer: ReturnType<typeof window.setInterval> | undefined
const confirmRefresh = (event: BeforeUnloadEvent) => {
event.preventDefault()
......@@ -112,11 +115,37 @@ const seedRankPlayersFromLoading = () => {
)
}
function showSubmitWaiting() {
if (!isSubmitWaiting.value) { submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
}
submitWaitingTimer = window.setInterval(() => {
submitWaitingSeconds.value = Math.max(0, submitWaitingSeconds.value - 1)
if (submitWaitingSeconds.value <= 0 && submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}, 1000)
}
isSubmitWaiting.value = true
}
function clearSubmitWaiting() {
isSubmitWaiting.value = false
submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}
const resetRoundState = () => {
playerCount.value = 0
loadingPlayers.value = Array.from({ length: PLAYER_LIMIT }, emptyLoadingPlayer)
rankPlayers.value = Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index))
isPlayingStartAnimation = false
clearSubmitWaiting()
}
const gotoLoading = (reset = false) => {
......@@ -137,15 +166,15 @@ const gotoRank1WithIntro = async () => {
screen.value = 'rank1'
await nextTick()
await rank1Ref.value?.startIntro()
screen.value = 'rank2'
showSubmitWaiting()
isPlayingStartAnimation = false
}
function handleRoomState(data: any) {
console.log('[WebSocket] 房间状态更新 - status:', data?.status, '数据:', JSON.stringify(data))
if (Array.isArray(data?.list)) {
renderPlayers(data)
}
if ((data?.status === 0 || data?.status === 1) && Array.isArray(data?.list)) {
renderPlayers(data)
}
switch (data?.status) {
case 0:
......@@ -156,12 +185,19 @@ function handleRoomState(data: any) {
gotoLoading()
break
case 2:
seedRankPlayersFromLoading()
screen.value = 'rank1'
break
screen.value = 'rank1'
clearSubmitWaiting()
break
case 3:
screen.value = 'rank2'
break
showSubmitWaiting()
break
case 4:
clearSubmitWaiting()
if (Array.isArray(data?.list)) {
updateRankPlayers(data.list)
}
screen.value = 'rank2'
break
}
}
......@@ -263,4 +299,66 @@ watch(screen, (value) => {
@next="nextRoundWithMusic" />
<!-- <Rank1 v-else ref="rank1Ref" :rank-list="rankPlayers" @start="startGameWithMusic" -->
<Rank2 v-else :rank-list="rankPlayers" @next="nextRoundWithMusic" @back="backHandler" />
<div v-if="isSubmitWaiting" class="submit-waiting-overlay">
<div class="submit-waiting-title">正在等待玩家提交分数</div>
<div :key="submitWaitingSeconds" class="submit-waiting-count">{{ submitWaitingSeconds }}</div>
</div>
</template>
<style scoped>
.submit-waiting-overlay {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.22) 0%, rgba(0, 0, 0, 0.08) 48%, transparent 72%);
color: #fff;
pointer-events: none;
}
.submit-waiting-title {
position: absolute;
bottom: 8vh;
left: 0;
width: 100%;
font-size: 56px;
font-weight: 700;
line-height: 1;
text-align: center;
text-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
}
.submit-waiting-count {
color: white;
font-size: 300pt;
font-weight: 800;
line-height: 1;
text-shadow: 0 18px 38px rgba(0, 0, 0, 0.25);
animation: submit-countdown-pop 1s ease both;
}
@keyframes submit-countdown-pop {
0% {
opacity: 0;
transform: scale(0.45);
}
18% {
opacity: 1;
transform: scale(1.14);
}
58% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.84);
}
}
</style>
......@@ -38,6 +38,9 @@ const playerCount = ref(0)
const rankPlayers = ref<Player[]>(Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index)))
const rank1Ref = ref<InstanceType<typeof Rank1> | null>(null)
let isPlayingStartAnimation = false
const isSubmitWaiting = ref(false)
const submitWaitingSeconds = ref(5)
let submitWaitingTimer: ReturnType<typeof window.setInterval> | undefined
const confirmRefresh = (event: BeforeUnloadEvent) => {
event.preventDefault()
......@@ -86,11 +89,37 @@ const seedRankPlayersFromLoading = () => {
)
}
function showSubmitWaiting() {
if (!isSubmitWaiting.value) { submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
}
submitWaitingTimer = window.setInterval(() => {
submitWaitingSeconds.value = Math.max(0, submitWaitingSeconds.value - 1)
if (submitWaitingSeconds.value <= 0 && submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}, 1000)
}
isSubmitWaiting.value = true
}
function clearSubmitWaiting() {
isSubmitWaiting.value = false
submitWaitingSeconds.value = 5
if (submitWaitingTimer) {
window.clearInterval(submitWaitingTimer)
submitWaitingTimer = undefined
}
}
const resetRoundState = () => {
playerCount.value = 0
loadingPlayers.value = Array.from({ length: PLAYER_LIMIT }, emptyLoadingPlayer)
rankPlayers.value = Array.from({ length: RANK_LIMIT }, (_, index) => emptyRankPlayer(index))
isPlayingStartAnimation = false
clearSubmitWaiting()
}
const gotoLoading = (reset = false) => {
......@@ -109,14 +138,14 @@ const gotoRank1WithIntro = async () => {
screen.value = 'rank1'
await nextTick()
await rank1Ref.value?.startIntro()
screen.value = 'rank2'
showSubmitWaiting()
isPlayingStartAnimation = false
}
function handleRoomState(data: any) {
if (Array.isArray(data?.list)) {
renderPlayers(data)
}
if ((data?.status === 0 || data?.status === 1) && Array.isArray(data?.list)) {
renderPlayers(data)
}
switch (data?.status) {
case 0:
......@@ -127,12 +156,19 @@ function handleRoomState(data: any) {
gotoLoading()
break
case 2:
seedRankPlayersFromLoading()
screen.value = 'rank1'
break
case 3:
screen.value = 'rank2'
break
screen.value = 'rank1'
clearSubmitWaiting()
break
case 3:
showSubmitWaiting()
break
case 4:
clearSubmitWaiting()
if (Array.isArray(data?.list)) {
updateRankPlayers(data.list)
}
screen.value = 'rank2'
break
}
}
const gameId='game6'
......@@ -209,4 +245,66 @@ watch(screen, (value) => {
@start="startGameWithMusic" @back="backHandler" />
<Rank1 v-else-if="screen === 'rank1'" ref="rank1Ref" :rank-list="rankPlayers" @start="startGameWithMusic" />
<Rank2 v-else :rank-list="rankPlayers" @next="nextRoundWithMusic" @back="backHandler" />
<div v-if="isSubmitWaiting" class="submit-waiting-overlay">
<div class="submit-waiting-title">正在等待玩家提交分数</div>
<div :key="submitWaitingSeconds" class="submit-waiting-count">{{ submitWaitingSeconds }}</div>
</div>
</template>
<style scoped>
.submit-waiting-overlay {
position: fixed;
inset: 0;
z-index: 9999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background: radial-gradient(ellipse at center, rgba(0, 0, 0, 0.22) 0%, rgba(0, 0, 0, 0.08) 48%, transparent 72%);
color: #fff;
pointer-events: none;
}
.submit-waiting-title {
position: absolute;
bottom: 8vh;
left: 0;
width: 100%;
font-size: 56px;
font-weight: 700;
line-height: 1;
text-align: center;
text-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
}
.submit-waiting-count {
color: white;
font-size: 300pt;
font-weight: 800;
line-height: 1;
text-shadow: 0 18px 38px rgba(0, 0, 0, 0.25);
animation: submit-countdown-pop 1s ease both;
}
@keyframes submit-countdown-pop {
0% {
opacity: 0;
transform: scale(0.45);
}
18% {
opacity: 1;
transform: scale(1.14);
}
58% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.84);
}
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment