100 lines
2.5 KiB
TypeScript
100 lines
2.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
|
|
}
|
|
|
|
class SaveDB extends Dexie {
|
|
saves!: Table<SaveRecord, number>
|
|
unlocks!: Table<UnlockRecord, string>
|
|
|
|
constructor() {
|
|
super('MovieGameSaves')
|
|
this.version(3).stores({
|
|
saves: '++id, slot',
|
|
unlocks: 'chapterId',
|
|
})
|
|
}
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|