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:
@@ -3,15 +3,35 @@ import type { Choice } from '@engine/types'
|
||||
|
||||
const props = defineProps<{
|
||||
choices: Choice[]
|
||||
timerTotal: number
|
||||
timerRemaining: number
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
choose: [index: number]
|
||||
}>()
|
||||
|
||||
function timerPercent(): number {
|
||||
if (props.timerTotal <= 0) return 0
|
||||
return (props.timerRemaining / props.timerTotal) * 100
|
||||
}
|
||||
|
||||
function timerClass(): string {
|
||||
if (props.timerRemaining <= 3) return 'danger'
|
||||
return ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="choice-panel" v-if="choices.length > 0">
|
||||
<div class="timer-bar" v-if="timerTotal > 0">
|
||||
<div
|
||||
class="timer-fill"
|
||||
:class="timerClass()"
|
||||
:style="{ width: timerPercent() + '%' }"
|
||||
></div>
|
||||
<span class="timer-text">{{ timerRemaining.toFixed(1) }}s</span>
|
||||
</div>
|
||||
<div class="choice-prompt">做出你的选择</div>
|
||||
<div class="choice-list">
|
||||
<button
|
||||
@@ -33,10 +53,38 @@ const emit = defineEmits<{
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.85));
|
||||
padding: 40px 20px 30px;
|
||||
padding: 20px 20px 30px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.timer-bar {
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 2px;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.timer-fill {
|
||||
height: 100%;
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
transition: width 0.1s linear;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.timer-fill.danger {
|
||||
background: #e74c3c;
|
||||
}
|
||||
|
||||
.timer-text {
|
||||
position: absolute;
|
||||
top: -18px;
|
||||
right: 0;
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.choice-prompt {
|
||||
color: #ccc;
|
||||
font-size: 14px;
|
||||
|
||||
Reference in New Issue
Block a user