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:
66
src/App.vue
66
src/App.vue
@@ -2,15 +2,19 @@
|
||||
import { ref } from 'vue'
|
||||
import GamePlayer from '@/components/GamePlayer.vue'
|
||||
import ChoicePanel from '@/components/ChoicePanel.vue'
|
||||
import SaveLoadMenu from '@/components/SaveLoadMenu.vue'
|
||||
import { useGameEngine } from '@/composables/useGameEngine'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
|
||||
const store = useGameStore()
|
||||
const videoElRef = ref<HTMLVideoElement | null>(null)
|
||||
const videoElA = ref<HTMLVideoElement | null>(null)
|
||||
const videoElB = ref<HTMLVideoElement | null>(null)
|
||||
const loading = ref(true)
|
||||
const started = ref(false)
|
||||
const showMenu = ref(false)
|
||||
|
||||
const { loadGame, start, makeChoice } = useGameEngine(() => videoElRef.value)
|
||||
const { loadGame, start, makeChoice, saveGame, loadGameFromSlot, refreshSaves } =
|
||||
useGameEngine(() => [videoElA.value, videoElB.value])
|
||||
|
||||
async function init() {
|
||||
await loadGame('/scenes/demo.json')
|
||||
@@ -22,14 +26,31 @@ function handleStart() {
|
||||
start()
|
||||
}
|
||||
|
||||
function onVideoReady(el: HTMLVideoElement) {
|
||||
videoElRef.value = el
|
||||
function onVideoReady(elA: HTMLVideoElement, elB: HTMLVideoElement) {
|
||||
videoElA.value = elA
|
||||
videoElB.value = elB
|
||||
}
|
||||
|
||||
function onChoose(index: number) {
|
||||
makeChoice(index)
|
||||
}
|
||||
|
||||
function toggleMenu() {
|
||||
showMenu.value = !showMenu.value
|
||||
if (showMenu.value) {
|
||||
refreshSaves()
|
||||
}
|
||||
}
|
||||
|
||||
async function onSave(slot: number) {
|
||||
await saveGame(slot)
|
||||
}
|
||||
|
||||
async function onLoad(slot: number) {
|
||||
await loadGameFromSlot(slot)
|
||||
showMenu.value = false
|
||||
}
|
||||
|
||||
init()
|
||||
</script>
|
||||
|
||||
@@ -39,7 +60,15 @@ init()
|
||||
<template v-else>
|
||||
<div class="game-screen">
|
||||
<GamePlayer @video-ready="onVideoReady" />
|
||||
<ChoicePanel :choices="store.choices" @choose="onChoose" />
|
||||
<ChoicePanel
|
||||
:choices="store.choices"
|
||||
:timer-total="store.timerTotal"
|
||||
:timer-remaining="store.timerRemaining"
|
||||
@choose="onChoose"
|
||||
/>
|
||||
<button v-if="started && !store.gameEnded" class="menu-trigger" @click="toggleMenu">
|
||||
菜单
|
||||
</button>
|
||||
</div>
|
||||
<div v-if="!started" class="start-overlay">
|
||||
<button class="start-btn" @click="handleStart">开始游戏</button>
|
||||
@@ -47,6 +76,13 @@ init()
|
||||
<div v-if="store.gameEnded" class="game-end-overlay">
|
||||
<div class="game-end-text">游戏结束</div>
|
||||
</div>
|
||||
<SaveLoadMenu
|
||||
v-if="showMenu"
|
||||
:saves="store.saves"
|
||||
@save="onSave"
|
||||
@load="onLoad"
|
||||
@close="showMenu = false"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
@@ -94,6 +130,26 @@ html, body {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.menu-trigger {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
z-index: 20;
|
||||
padding: 8px 16px;
|
||||
font-size: 13px;
|
||||
color: #aaa;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s, color 0.2s;
|
||||
}
|
||||
|
||||
.menu-trigger:hover {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.game-end-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
|
||||
Reference in New Issue
Block a user