Files
tianshu-engine/engine/systems/SaveSystem.ts
2026-06-09 17:21:54 +08:00

142 lines
3.5 KiB
TypeScript

import Dexie, { type Table } from 'dexie'
import type { SaveData } from '../types'
interface SaveRecord {
id?: number
slot: number
timestamp: number
currentScene: string
variables: string
flags: string
history: string
thumbnail?: string
}
interface UnlockRecord {
chapterId: string
}
interface WatchedRecord {
sceneId: string
timestamp: number
}
interface AchievementRecord {
achievementId: string
}
class SaveDB extends Dexie {
saves!: Table<SaveRecord, number>
unlocks!: Table<UnlockRecord, string>
watched!: Table<WatchedRecord, string>
achievements!: Table<AchievementRecord, string>
constructor() {
super('MovieGameSaves')
this.version(5).stores({
saves: '++id, slot',
unlocks: 'chapterId',
watched: 'sceneId',
achievements: 'achievementId',
})
}
}
const db = new SaveDB()
export class SaveSystem {
async save(slot: number, data: Omit<SaveData, 'slot'>): Promise<void> {
const record: SaveRecord = {
slot,
timestamp: data.timestamp || Date.now(),
currentScene: data.currentScene,
variables: JSON.stringify(data.variables),
flags: JSON.stringify(data.flags),
history: JSON.stringify(data.history),
thumbnail: data.thumbnail,
}
const existing = await db.saves.where('slot').equals(slot).first()
if (existing) {
await db.saves.update(existing.id!, record)
} else {
await db.saves.add(record)
}
}
async load(slot: number): Promise<SaveData | null> {
const record = await db.saves.where('slot').equals(slot).first()
if (!record) return null
return {
slot: record.slot,
timestamp: record.timestamp,
currentScene: record.currentScene,
variables: JSON.parse(record.variables),
flags: JSON.parse(record.flags),
history: JSON.parse(record.history),
thumbnail: record.thumbnail,
}
}
async listSlots(): Promise<{ slot: number; timestamp: number; sceneLabel: string; thumbnail?: string }[]> {
const records = await db.saves.orderBy('slot').toArray()
return records.map((r) => ({
slot: r.slot,
timestamp: r.timestamp,
sceneLabel: r.currentScene,
thumbnail: r.thumbnail,
}))
}
async delete(slot: number): Promise<void> {
await db.saves.where('slot').equals(slot).delete()
}
async unlockChapter(chapterId: string) {
const exists = await db.unlocks.get(chapterId)
if (!exists) {
await db.unlocks.put({ chapterId })
}
}
async isChapterUnlocked(chapterId: string): Promise<boolean> {
const record = await db.unlocks.get(chapterId)
return !!record
}
async getUnlockedChapters(): Promise<string[]> {
const records = await db.unlocks.toArray()
return records.map((r) => r.chapterId)
}
async markWatched(sceneId: string) {
const exists = await db.watched.get(sceneId)
if (!exists) {
await db.watched.put({ sceneId, timestamp: Date.now() })
}
}
async isWatched(sceneId: string): Promise<boolean> {
const record = await db.watched.get(sceneId)
return !!record
}
async getWatchedSceneIds(): Promise<string[]> {
const records = await db.watched.toArray()
return records.map((r) => r.sceneId)
}
async unlockAchievement(id: string) {
const exists = await db.achievements.get(id)
if (!exists) {
await db.achievements.put({ achievementId: id })
}
}
async getUnlockedAchievements(): Promise<string[]> {
const records = await db.achievements.toArray()
return records.map((r) => r.achievementId)
}
}