feat: P2 - QTE system, subtitles, save thumbnails

- QTESystem: trigger detection via timeupdate, multi-key matching, timeout handling
- QTEOverlay: SVG countdown ring + key prompts + success/fail animation
- Engine: integrate QTE (timeupdate check, conditional branching, effect application)
- Subtitles: WebVTT parsing + synchronized subtitle rendering
- GamePlayer: overlay QTE and subtitle components
- SaveSystem: DB v2 with thumbnail field, canvas snapshot at 320x180 JPEG
- SaveLoadMenu: thumbnail preview for save slots
- VideoManager: getActiveVideoElement() for canvas capture
- App.vue: QTE/subtitle integration, thumbnail capture on save
- stores: QTE state management, save list with thumbnails
- demo.json: QTE scene (right_door), subtitles, new event types
- ROADMAP: mark P2 as completed
This commit is contained in:
2026-06-07 19:35:14 +08:00
parent c168e30e52
commit 319a379921
18 changed files with 625 additions and 53 deletions

View File

@@ -2,6 +2,8 @@
import { ref } from 'vue'
import GamePlayer from '@/components/GamePlayer.vue'
import ChoicePanel from '@/components/ChoicePanel.vue'
import QTEOverlay from '@/components/QTEOverlay.vue'
import Subtitles from '@/components/Subtitles.vue'
import SaveLoadMenu from '@/components/SaveLoadMenu.vue'
import { useGameEngine } from '@/composables/useGameEngine'
import { useGameStore } from '@/stores/gameStore'
@@ -67,7 +69,20 @@ init()
<template v-else>
<div class="game-screen">
<GamePlayer @video-ready="onVideoReady" />
<Subtitles
:current-time="store.videoTime"
:video-url="store.currentScene?.subtitleUrl ?? null"
/>
<QTEOverlay
:visible="store.qteActive"
:prompt="store.qteDef?.prompt ?? ''"
:keys="store.qteDef?.keys ?? []"
:total="store.qteTotal"
:remaining="store.qteRemaining"
:result="store.qteResult"
/>
<ChoicePanel
v-if="!store.qteActive"
:choices="store.choices"
:timer-total="store.timerTotal"
:timer-remaining="store.timerRemaining"