feat: P15 ending gallery, chapter recap, visited tracking, save system v6
This commit is contained in:
@@ -28,6 +28,7 @@ export class Engine {
|
||||
private loopActive = false
|
||||
private onUnlockChapter: ((chapterId: string) => void) | null = null
|
||||
private onMarkWatched: ((sceneId: string) => void) | null = null
|
||||
private onMarkVisited: ((sceneId: string) => void) | null = null
|
||||
|
||||
setChapterUnlockHandler(handler: (chapterId: string) => void) {
|
||||
this.onUnlockChapter = handler
|
||||
@@ -37,6 +38,10 @@ export class Engine {
|
||||
this.onMarkWatched = handler
|
||||
}
|
||||
|
||||
setMarkVisitedHandler(handler: (sceneId: string) => void) {
|
||||
this.onMarkVisited = handler
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.sceneManager = new SceneManager()
|
||||
this.videoManager = new VideoManager()
|
||||
@@ -74,40 +79,16 @@ export class Engine {
|
||||
}
|
||||
|
||||
private goToScene(scene: SceneNode) {
|
||||
this.currentScene = scene
|
||||
|
||||
const chapter = this.sceneManager.getChapterBySceneId(scene.id)
|
||||
if (chapter) {
|
||||
this.onUnlockChapter?.(chapter.id)
|
||||
this.emit('chapterUnlock', chapter)
|
||||
}
|
||||
|
||||
if (scene.onEnter) {
|
||||
this.stateManager.apply(scene.onEnter)
|
||||
}
|
||||
this.onMarkVisited?.(scene.id)
|
||||
|
||||
if (scene.videoMuted) {
|
||||
this.videoManager.setMuted(true)
|
||||
} else {
|
||||
this.videoManager.setMuted(false)
|
||||
}
|
||||
|
||||
const bgmUrl = scene.bgmUrl || null
|
||||
if (bgmUrl) {
|
||||
this.audioSystem.play(
|
||||
bgmUrl,
|
||||
scene.bgmVolume ?? 0.8,
|
||||
scene.bgmCrossFade ?? 2.0,
|
||||
scene.bgmDuckLevel,
|
||||
scene.bgmDuckFade,
|
||||
)
|
||||
} else {
|
||||
this.audioSystem.stop(scene.bgmCrossFade ?? 2.0)
|
||||
}
|
||||
|
||||
this.enterScene(scene)
|
||||
}
|
||||
|
||||
private enterScene(scene: SceneNode) {
|
||||
this.currentScene = scene
|
||||
this.qteTriggered = false
|
||||
this.qteResolved = false
|
||||
this.loopActive = false
|
||||
@@ -405,7 +386,7 @@ export class Engine {
|
||||
|
||||
this.ended = false
|
||||
this.isInitialScene = false
|
||||
this.enterScene(scene)
|
||||
this.goToScene(scene)
|
||||
}
|
||||
|
||||
destroy() {
|
||||
|
||||
@@ -25,6 +25,10 @@ export class SceneManager {
|
||||
return Object.keys(this.scenes)
|
||||
}
|
||||
|
||||
getScenes(): Record<string, SceneNode> {
|
||||
return this.scenes
|
||||
}
|
||||
|
||||
getChapterBySceneId(sceneId: string): ChapterInfo | undefined {
|
||||
return this.chapters.find((ch) => ch.startScene === sceneId)
|
||||
}
|
||||
|
||||
@@ -25,19 +25,25 @@ interface AchievementRecord {
|
||||
achievementId: string
|
||||
}
|
||||
|
||||
interface VisitedRecord {
|
||||
sceneId: string
|
||||
}
|
||||
|
||||
class SaveDB extends Dexie {
|
||||
saves!: Table<SaveRecord, number>
|
||||
unlocks!: Table<UnlockRecord, string>
|
||||
watched!: Table<WatchedRecord, string>
|
||||
achievements!: Table<AchievementRecord, string>
|
||||
visited!: Table<VisitedRecord, string>
|
||||
|
||||
constructor() {
|
||||
super('MovieGameSaves')
|
||||
this.version(5).stores({
|
||||
this.version(6).stores({
|
||||
saves: '++id, slot',
|
||||
unlocks: 'chapterId',
|
||||
watched: 'sceneId',
|
||||
achievements: 'achievementId',
|
||||
visited: 'sceneId',
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -138,4 +144,16 @@ export class SaveSystem {
|
||||
const records = await db.achievements.toArray()
|
||||
return records.map((r) => r.achievementId)
|
||||
}
|
||||
|
||||
async markVisited(sceneId: string) {
|
||||
const exists = await db.visited.get(sceneId)
|
||||
if (!exists) {
|
||||
await db.visited.put({ sceneId })
|
||||
}
|
||||
}
|
||||
|
||||
async getVisitedSceneIds(): Promise<string[]> {
|
||||
const records = await db.visited.toArray()
|
||||
return records.map((r) => r.sceneId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,12 +88,20 @@ export interface AchievementDef {
|
||||
condition: Condition
|
||||
}
|
||||
|
||||
export interface EndingDef {
|
||||
id: string
|
||||
label: string
|
||||
sceneId: string
|
||||
thumbnail?: string
|
||||
}
|
||||
|
||||
export interface GameData {
|
||||
scenes: Record<string, SceneNode>
|
||||
startScene: string
|
||||
variables: Record<string, number>
|
||||
chapters?: ChapterInfo[]
|
||||
achievements?: AchievementDef[]
|
||||
endings?: EndingDef[]
|
||||
}
|
||||
|
||||
export interface ChoiceRecord {
|
||||
|
||||
Reference in New Issue
Block a user