fix(tongji): 恢复游戏进度并完善周榜
This commit is contained in:
@@ -224,7 +224,7 @@
|
||||
v-for="player in weeklyRankRows"
|
||||
:key="player.id"
|
||||
class="eg-weekly-row"
|
||||
:class="{ 'is-me': player.isMe }"
|
||||
:class="{ 'is-me': player.isMe, 'is-waiting': player.isWaiting }"
|
||||
>
|
||||
<text class="eg-weekly-place">{{ player.rank }}</text>
|
||||
<view class="eg-weekly-avatar" :class="`is-tone-${player.tone}`">
|
||||
@@ -237,6 +237,11 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="!platformConnected" class="eg-weekly-connection" @tap.stop="retryPlatformConnection">
|
||||
<text>{{ platformConnectionHint }}</text>
|
||||
<text class="eg-weekly-retry">点击重试</text>
|
||||
</view>
|
||||
|
||||
<view class="eg-cheer-card" :class="`is-${currentCheer.tone}`">
|
||||
<view class="eg-cheer-top">
|
||||
<text class="eg-cheer-source">{{ currentCheer.source }}</text>
|
||||
@@ -246,7 +251,7 @@
|
||||
<text v-if="currentCheer.userGenerated" class="eg-cheer-note">亲友自定义内容,不作为医学判断</text>
|
||||
</view>
|
||||
|
||||
<view class="eg-weekly-start" @tap="closeWeeklyRank">开始消除</view>
|
||||
<view class="eg-weekly-start" @tap="closeWeeklyRank">{{ restoredGame ? '继续上次游戏' : '开始消除' }}</view>
|
||||
<button class="eg-weekly-share" open-type="share">邀请亲友一起认糖</button>
|
||||
<text class="eg-weekly-footnote">使用平台昵称参加真实周榜,每周一重新分组;分享不会自动绑定家庭关系</text>
|
||||
</view>
|
||||
@@ -300,7 +305,7 @@
|
||||
|
||||
<script setup>
|
||||
import { computed, getCurrentInstance, ref } from 'vue'
|
||||
import { onLoad, onShareAppMessage, onShareTimeline, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import { onHide, onLoad, onShareAppMessage, onShareTimeline, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import TongjiIcon from '../components/TongjiIcon.vue'
|
||||
import { useGameSfx } from './composables/useGameSfx.js'
|
||||
import { useGamePlatform } from './composables/useGamePlatform.js'
|
||||
@@ -319,6 +324,8 @@ const COMBO_TASK_WAVE = 2
|
||||
const COMBO_TASK_TARGET = 2
|
||||
const COMBO_PITY_TURNS = 6
|
||||
const MEGA_COMBO_SCORE = 300
|
||||
const ACTIVE_GAME_KEY = 'tongji_endless_active_game_v1'
|
||||
const ACTIVE_GAME_VERSION = 1
|
||||
const DANGER_DROP_CHANCE = 0.05 + GAME_DIFFICULTY * 0.01
|
||||
const COLUMN_GROUP_BIAS = 0.86 - GAME_DIFFICULTY * 0.028
|
||||
const CHEER_MESSAGES = [
|
||||
@@ -399,10 +406,12 @@ const {
|
||||
connected: platformConnected,
|
||||
loading: platformLoading,
|
||||
syncStatus: platformSyncStatus,
|
||||
lastError: platformLastError,
|
||||
leaderboard: platformLeaderboard,
|
||||
confirmedSessionLearned,
|
||||
connect: connectPlatform,
|
||||
beginSession: beginPlatformSession,
|
||||
getSessionKey: getPlatformSessionKey,
|
||||
queueProgress: queuePlatformProgress,
|
||||
flushProgress: flushPlatformProgress,
|
||||
syncAndRefresh: syncAndRefreshPlatform,
|
||||
@@ -447,6 +456,7 @@ const reshuffleDone = ref(false)
|
||||
const reshuffling = ref(false)
|
||||
const confettiVisible = ref(false)
|
||||
const weeklyRankVisible = ref(false)
|
||||
const restoredGame = ref(false)
|
||||
const cheerIndex = ref(0)
|
||||
const CONFETTI_COLORS = ['#ff3154', '#ffd928', '#28c76f', '#4d8dff', '#a855f7', '#ff7a1a']
|
||||
const confettiPieces = Array.from({ length: 34 }, (_, index) => ({
|
||||
@@ -515,9 +525,24 @@ const weeklyRankRows = computed(() => {
|
||||
tone: 1,
|
||||
isMe: true
|
||||
}]
|
||||
return source
|
||||
const ranked = source
|
||||
.sort((a, b) => b.count - a.count || Number(b.best_score || 0) - Number(a.best_score || 0))
|
||||
.map((player, index) => ({ ...player, rank: index + 1 }))
|
||||
while (ranked.length < 7) {
|
||||
const rank = ranked.length + 1
|
||||
ranked.push({
|
||||
id: `waiting-${rank}`,
|
||||
rank,
|
||||
name: '等待同行加入',
|
||||
avatar: '+',
|
||||
avatarUrl: '',
|
||||
count: '—',
|
||||
tone: rank,
|
||||
isMe: false,
|
||||
isWaiting: true
|
||||
})
|
||||
}
|
||||
return ranked.slice(0, 7)
|
||||
})
|
||||
const weeklyMyRank = computed(() => weeklyRankRows.value.find(player => player.isMe)?.rank || 1)
|
||||
const weeklyRankRise = computed(() => 0)
|
||||
@@ -535,13 +560,24 @@ const weeklyRangeLabel = computed(() => {
|
||||
return `${formatWeekDate(start)}—${formatWeekDate(end)}`
|
||||
})
|
||||
const weeklySexLabel = computed(() => platformLeaderboard.value.sex_label || '同行')
|
||||
const weeklyMemberCount = computed(() => Math.max(1, Number(platformLeaderboard.value.member_count || weeklyRankRows.value.length)))
|
||||
const weeklyMemberCount = computed(() => {
|
||||
const reported = Number(platformLeaderboard.value.member_count || 0)
|
||||
const remoteCount = Array.isArray(platformLeaderboard.value.players)
|
||||
? platformLeaderboard.value.players.length
|
||||
: 0
|
||||
return Math.max(1, Math.min(7, reported || remoteCount || 1))
|
||||
})
|
||||
const weeklyStatusLabel = computed(() => {
|
||||
if (platformLoading.value) return '连接中'
|
||||
if (!platformConnected.value) return '离线记录中'
|
||||
if (platformSyncStatus.value === 'pending' || platformSyncStatus.value === 'syncing') return '保存中'
|
||||
return '真实周榜'
|
||||
})
|
||||
const platformConnectionHint = computed(() => (
|
||||
platformLoading.value
|
||||
? '正在连接甄养堂账号和本周同行榜'
|
||||
: (platformLastError.value || '当前成绩暂存在本机,联网登录后会自动补传')
|
||||
))
|
||||
|
||||
onShareAppMessage(() => {
|
||||
recordPlatformShare()
|
||||
@@ -592,7 +628,7 @@ onLoad((options = {}) => {
|
||||
initNavLayout()
|
||||
bestScore.value = Number(uni.getStorageSync('tongji_endless_best') || 0)
|
||||
incomingInviteCode = String(options.invite_code || '').trim()
|
||||
startGame()
|
||||
if (!restoreActiveGame()) startGame()
|
||||
weeklyRankVisible.value = true
|
||||
connectPlatform(incomingInviteCode)
|
||||
})
|
||||
@@ -603,7 +639,12 @@ onShow(() => {
|
||||
})
|
||||
})
|
||||
|
||||
onHide(() => {
|
||||
saveActiveGame()
|
||||
})
|
||||
|
||||
onUnload(() => {
|
||||
saveActiveGame()
|
||||
if (learned.value > 0 || gameOver.value) {
|
||||
queuePlatformProgress(learned.value, score.value, gameOver.value)
|
||||
flushPlatformProgress()
|
||||
@@ -679,8 +720,117 @@ function cycleCheer() {
|
||||
playSfx('select', .68)
|
||||
}
|
||||
|
||||
async function retryPlatformConnection() {
|
||||
if (platformLoading.value) return
|
||||
const connected = await connectPlatform(incomingInviteCode)
|
||||
if (connected) {
|
||||
await syncAndRefreshPlatform(learned.value, score.value)
|
||||
uni.showToast({ title: '周榜已更新', icon: 'none' })
|
||||
return
|
||||
}
|
||||
uni.showToast({ title: platformConnectionHint.value, icon: 'none' })
|
||||
}
|
||||
|
||||
function currentWeekStartKey(now = new Date()) {
|
||||
const date = new Date(now)
|
||||
const day = date.getDay() || 7
|
||||
date.setHours(0, 0, 0, 0)
|
||||
date.setDate(date.getDate() - day + 1)
|
||||
const pad = value => String(value).padStart(2, '0')
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
|
||||
}
|
||||
|
||||
function clearActiveGame() {
|
||||
try { uni.removeStorageSync(ACTIVE_GAME_KEY) } catch (_) {}
|
||||
}
|
||||
|
||||
function saveActiveGame() {
|
||||
if (gameOver.value) {
|
||||
clearActiveGame()
|
||||
return false
|
||||
}
|
||||
if (busy.value || board.value.length !== ROWS * COLS) return false
|
||||
const stableBoard = board.value.every(tile => tile && FOODS[tile.key])
|
||||
if (!stableBoard) return false
|
||||
try {
|
||||
uni.setStorageSync(ACTIVE_GAME_KEY, {
|
||||
version: ACTIVE_GAME_VERSION,
|
||||
week_start: currentWeekStartKey(),
|
||||
session_key: getPlatformSessionKey(),
|
||||
confirmed_session_learned: Math.max(0, Number(confirmedSessionLearned.value) || 0),
|
||||
saved_at: Date.now(),
|
||||
theme_index: themeIndex.value,
|
||||
board: board.value.map(tile => ({ id: tile.id, key: tile.key, special: tile.special || '' })),
|
||||
score: score.value,
|
||||
learned: learned.value,
|
||||
combo: combo.value,
|
||||
feast_moves: feastMoves.value,
|
||||
camel_count: camelCount.value,
|
||||
task_index: taskIndex.value,
|
||||
task_progress: taskProgress.value,
|
||||
combo_dry_turns: comboDryTurns.value
|
||||
})
|
||||
return true
|
||||
} catch (_) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function restoreActiveGame() {
|
||||
let saved
|
||||
try { saved = uni.getStorageSync(ACTIVE_GAME_KEY) } catch (_) { return false }
|
||||
const validBoard = Array.isArray(saved?.board)
|
||||
&& saved.board.length === ROWS * COLS
|
||||
&& saved.board.every(tile => tile && FOODS[tile.key])
|
||||
if (saved?.version !== ACTIVE_GAME_VERSION || saved?.week_start !== currentWeekStartKey() || !validBoard) {
|
||||
clearActiveGame()
|
||||
return false
|
||||
}
|
||||
|
||||
themeIndex.value = Math.max(0, Math.min(FOOD_GROUPS.length - 1, Number(saved.theme_index) || 0))
|
||||
board.value = saved.board.map(tile => ({
|
||||
id: Math.max(1, Number(tile.id) || tileId++),
|
||||
key: tile.key,
|
||||
special: ['row', 'col', 'all', 'burst'].includes(tile.special) ? tile.special : ''
|
||||
}))
|
||||
tileId = Math.max(tileId, ...board.value.map(tile => tile.id + 1))
|
||||
score.value = Math.max(0, Number(saved.score) || 0)
|
||||
learned.value = Math.max(0, Number(saved.learned) || 0)
|
||||
combo.value = Math.max(0, Number(saved.combo) || 0)
|
||||
feastMoves.value = Math.max(0, Number(saved.feast_moves) || 0)
|
||||
camelCount.value = Math.max(0, Math.min(MAX_CAMEL_COUNT, Number(saved.camel_count) || 0))
|
||||
taskIndex.value = Math.max(0, Number(saved.task_index) || 0) % 4
|
||||
taskProgress.value = Math.max(0, Number(saved.task_progress) || 0)
|
||||
comboDryTurns.value = Math.max(0, Number(saved.combo_dry_turns) || 0)
|
||||
beginPlatformSession(saved.session_key)
|
||||
confirmedSessionLearned.value = Math.max(0, Number(saved.confirmed_session_learned) || 0)
|
||||
selected.value = -1
|
||||
camelMode.value = false
|
||||
busy.value = false
|
||||
gameOver.value = false
|
||||
taskRewardVisible.value = false
|
||||
clearingIndices.value = []
|
||||
fireClearingIndices.value = []
|
||||
impactText.value = ''
|
||||
camelJackpot.value.visible = false
|
||||
stageVisible.value = false
|
||||
reshuffleVisible.value = false
|
||||
reshuffleDone.value = false
|
||||
reshuffling.value = false
|
||||
confettiVisible.value = false
|
||||
pendingThemeMatchCount = 0
|
||||
taskRewardStatusUntil = 0
|
||||
endedFood.value = foodOf(currentTheme.value.danger)
|
||||
restoredGame.value = true
|
||||
resetDrag()
|
||||
statusText.value = '已恢复上次未完成的游戏和得分'
|
||||
scheduleHint()
|
||||
return true
|
||||
}
|
||||
|
||||
function startGame() {
|
||||
if (hintTimer) clearTimeout(hintTimer)
|
||||
clearActiveGame()
|
||||
beginPlatformSession()
|
||||
themeIndex.value = 0
|
||||
board.value = buildPlayableBoard(openingKeysForTheme(currentTheme.value))
|
||||
@@ -708,8 +858,10 @@ function startGame() {
|
||||
confettiVisible.value = false
|
||||
pendingThemeMatchCount = 0
|
||||
taskRewardStatusUntil = 0
|
||||
restoredGame.value = false
|
||||
resetDrag()
|
||||
statusText.value = '交换相邻食物,三个相同即可消除'
|
||||
saveActiveGame()
|
||||
scheduleHint()
|
||||
}
|
||||
|
||||
@@ -863,6 +1015,7 @@ async function tapTile(index) {
|
||||
finishTurn()
|
||||
} else {
|
||||
statusText.value = `已将${foodOf(currentTheme.value.danger).short}移出餐盘,凑齐三个驼乳可换主题`
|
||||
saveActiveGame()
|
||||
if (!checkBoardHealth()) scheduleHint()
|
||||
}
|
||||
return
|
||||
@@ -928,6 +1081,7 @@ async function trySwap(a, b, soundPlayed = false) {
|
||||
endedFood.value = foodOf(highTile.key)
|
||||
saveBest()
|
||||
gameOver.value = true
|
||||
clearActiveGame()
|
||||
queuePlatformProgress(learned.value, score.value, true)
|
||||
busy.value = false
|
||||
return
|
||||
@@ -1422,6 +1576,7 @@ function showTaskCamelReward() {
|
||||
|
||||
function finishTurn() {
|
||||
saveBest()
|
||||
saveActiveGame()
|
||||
if (checkBoardHealth()) return
|
||||
if (stageVisible.value) {
|
||||
scheduleHint()
|
||||
@@ -1497,6 +1652,7 @@ async function performReshuffle() {
|
||||
reshuffleVisible.value = false
|
||||
reshuffling.value = false
|
||||
busy.value = false
|
||||
saveActiveGame()
|
||||
scheduleHint()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user