feat: P1 core - seamless video switching, conditional branches, save/load

- VideoManager: A/B dual-buffered video with crossfade transitions and candidate preloading
- Engine: condition-based choice filtering, ChoiceSystem timer, resumeScene for save/load
- SceneManager: getCandidateUrls for preloading next scenes
- SaveSystem: Dexie.js IndexedDB multi-slot save/load
- ChoiceSystem: timed choices with countdown and auto-default on timeout
- GamePlayer: dual video elements with crossfade CSS
- ChoicePanel: timer progress bar and countdown text
- SaveLoadMenu: save/load UI component
- App.vue: menu trigger, dual video refs, save/load integration
- gameStore: timer state, saves list
- demo.json: conditional choice example (secret ending, requires trust >= 80)
- ROADMAP: mark P1 as completed
This commit is contained in:
2026-06-07 16:48:52 +08:00
parent 42181fe185
commit 937e45c203
16 changed files with 763 additions and 71 deletions

View File

@@ -2,39 +2,40 @@
import { ref, onMounted } from 'vue'
const emit = defineEmits<{
videoReady: [el: HTMLVideoElement]
videoReady: [elA: HTMLVideoElement, elB: HTMLVideoElement]
}>()
const videoRef = ref<HTMLVideoElement | null>(null)
const videoARef = ref<HTMLVideoElement | null>(null)
const videoBRef = ref<HTMLVideoElement | null>(null)
onMounted(() => {
if (videoRef.value) {
emit('videoReady', videoRef.value)
if (videoARef.value && videoBRef.value) {
emit('videoReady', videoARef.value, videoBRef.value)
}
})
defineExpose({ videoRef })
defineExpose({ videoARef, videoBRef })
</script>
<template>
<div class="game-player">
<video ref="videoRef" class="player-video" preload="auto"></video>
<video ref="videoARef" class="player-video" preload="auto"></video>
<video ref="videoBRef" class="player-video" preload="auto"></video>
</div>
</template>
<style scoped>
.game-player {
position: relative;
width: 100%;
height: 100%;
background: #000;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.player-video {
max-width: 100%;
max-height: 100%;
object-fit: contain;
will-change: opacity;
}
</style>