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:
@@ -1,11 +1,12 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref, shallowRef } from 'vue'
|
||||
import type { SceneNode, Choice } from '@engine/types'
|
||||
import type { SceneNode, Choice, QTEDefinition } from '@engine/types'
|
||||
|
||||
export interface SlotInfo {
|
||||
slot: number
|
||||
timestamp: number
|
||||
sceneLabel: string
|
||||
thumbnail?: string
|
||||
}
|
||||
|
||||
export const useGameStore = defineStore('game', () => {
|
||||
@@ -16,6 +17,13 @@ export const useGameStore = defineStore('game', () => {
|
||||
const timerRemaining = ref(0)
|
||||
const saves = ref<SlotInfo[]>([])
|
||||
|
||||
const qteActive = ref(false)
|
||||
const qteDef = shallowRef<QTEDefinition | null>(null)
|
||||
const qteTotal = ref(0)
|
||||
const qteRemaining = ref(0)
|
||||
const qteResult = ref<'none' | 'success' | 'fail'>('none')
|
||||
const videoTime = ref(0)
|
||||
|
||||
function setScene(scene: SceneNode) {
|
||||
currentScene.value = scene
|
||||
}
|
||||
@@ -46,9 +54,36 @@ export const useGameStore = defineStore('game', () => {
|
||||
saves.value = list
|
||||
}
|
||||
|
||||
function showQTE(qte: QTEDefinition) {
|
||||
qteActive.value = true
|
||||
qteDef.value = qte
|
||||
qteTotal.value = qte.timeLimit
|
||||
qteRemaining.value = qte.timeLimit
|
||||
qteResult.value = 'none'
|
||||
}
|
||||
|
||||
function updateQTE(remaining: number) {
|
||||
qteRemaining.value = remaining
|
||||
}
|
||||
|
||||
function resolveQTE(success: boolean) {
|
||||
qteResult.value = success ? 'success' : 'fail'
|
||||
setTimeout(() => {
|
||||
qteActive.value = false
|
||||
qteDef.value = null
|
||||
qteResult.value = 'none'
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
function setVideoTime(t: number) {
|
||||
videoTime.value = t
|
||||
}
|
||||
|
||||
return {
|
||||
currentScene, choices, gameEnded, timerTotal, timerRemaining, saves,
|
||||
qteActive, qteDef, qteTotal, qteRemaining, qteResult, videoTime,
|
||||
setScene, setChoices, clearChoices, setGameEnded,
|
||||
setTimer, clearTimer, setSaves,
|
||||
showQTE, updateQTE, resolveQTE, setVideoTime,
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user