/** * 游戏进度持久化:关卡、星级、连胜、每日打卡 */ import { ref } from 'vue' import { calcLevelStars } from '../data/gameLevels.js' const STORAGE_KEY = 'tongji_game_progress_v1' const DEFAULT = { currentLevel: 1, maxUnlocked: 1, stars: {}, winStreak: 0, dailyStreak: 0, lastPlayDate: '', totalWins: 0, bestCombo: 0, totalStars: 0 } function todayStr() { const d = new Date() return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}` } function sumStars(starsMap) { return Object.values(starsMap || {}).reduce((s, n) => s + (Number(n) || 0), 0) } function loadRaw() { try { const raw = uni.getStorageSync(STORAGE_KEY) if (raw && typeof raw === 'object') return { ...DEFAULT, ...raw } } catch (_) {} return { ...DEFAULT } } function saveRaw(data) { try { uni.setStorageSync(STORAGE_KEY, data) } catch (_) {} } export function useGameProgress() { const progress = ref(loadRaw()) function persist() { progress.value.totalStars = sumStars(progress.value.stars) saveRaw(progress.value) } /** 进入游戏时更新每日打卡 */ function touchDailyPlay() { const today = todayStr() const last = progress.value.lastPlayDate if (last === today) return progress.value.dailyStreak const yesterday = new Date() yesterday.setDate(yesterday.getDate() - 1) const yStr = `${yesterday.getFullYear()}-${String(yesterday.getMonth() + 1).padStart(2, '0')}-${String(yesterday.getDate()).padStart(2, '0')}` if (last === yStr) { progress.value.dailyStreak = (progress.value.dailyStreak || 0) + 1 } else { progress.value.dailyStreak = 1 } progress.value.lastPlayDate = today persist() return progress.value.dailyStreak } function getStars(levelId) { return progress.value.stars[String(levelId)] || 0 } function recordWin({ levelId, movesLeft, glucoseLevel, score, levelConfig, maxCombo }) { const stars = calcLevelStars({ movesLeft, glucoseLevel, score, levelConfig }) const key = String(levelId) const prev = progress.value.stars[key] || 0 if (stars > prev) progress.value.stars[key] = stars progress.value.winStreak = (progress.value.winStreak || 0) + 1 progress.value.totalWins = (progress.value.totalWins || 0) + 1 if (maxCombo > (progress.value.bestCombo || 0)) progress.value.bestCombo = maxCombo const next = levelId + 1 if (next > progress.value.maxUnlocked) progress.value.maxUnlocked = next progress.value.currentLevel = next persist() return { stars, newBest: stars > prev } } function recordLoss() { progress.value.winStreak = 0 persist() } function setCurrentLevel(levelId) { progress.value.currentLevel = Math.max(1, Math.min(progress.value.maxUnlocked, levelId)) persist() } return { progress, touchDailyPlay, getStars, recordWin, recordLoss, setCurrentLevel, persist } }