feat: accessibility settings, subtitle/QTE improvements, docs update
This commit is contained in:
73
src/App.vue
73
src/App.vue
@@ -13,6 +13,7 @@ import AchievementToast from '@/components/AchievementToast.vue'
|
||||
import AchievementPanel from '@/components/AchievementPanel.vue'
|
||||
import EndingGallery from '@/components/EndingGallery.vue'
|
||||
import ChapterRecap from '@/components/ChapterRecap.vue'
|
||||
import AccessibilitySettings from '@/components/AccessibilitySettings.vue'
|
||||
import { useGameEngine } from '@/composables/useGameEngine'
|
||||
import { useGameStore } from '@/stores/gameStore'
|
||||
import { useFullscreen } from '@/composables/useFullscreen'
|
||||
@@ -33,6 +34,7 @@ const recapChapterId = ref<string | null>(null)
|
||||
const hasAutoSave = ref(false)
|
||||
const currentSpeed = ref(1)
|
||||
const canSkip = ref(false)
|
||||
const paused = ref(false)
|
||||
const promptToast = ref('')
|
||||
const showPromptToast = ref(false)
|
||||
|
||||
@@ -49,14 +51,21 @@ async function init() {
|
||||
|
||||
function handleStart() {
|
||||
started.value = true
|
||||
applyQteParams()
|
||||
start()
|
||||
}
|
||||
|
||||
async function handleResume() {
|
||||
started.value = true
|
||||
applyQteParams()
|
||||
await resumeAutoSave()
|
||||
}
|
||||
|
||||
function applyQteParams() {
|
||||
engine.qteSystem.timeLimitMultiplier = store.qteTimeRelax ? 1.5 : 1
|
||||
engine.qteSystem.singleKeyMode = store.qteSingleKey
|
||||
}
|
||||
|
||||
function onVideoReady(elA: HTMLVideoElement, elB: HTMLVideoElement) {
|
||||
videoElA.value = elA
|
||||
videoElB.value = elB
|
||||
@@ -117,12 +126,24 @@ watch(() => store.currentScene?.id, async (newId) => {
|
||||
|
||||
function onGlobalKeydown(e: KeyboardEvent) {
|
||||
const key = e.key
|
||||
|
||||
if (key === ' ' && store.pauseEnabled && started.value && !store.gameEnded) {
|
||||
const activeEl = document.activeElement
|
||||
if (!activeEl || activeEl.tagName === 'BODY' || activeEl === document.body) {
|
||||
e.preventDefault()
|
||||
togglePause()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (key === 'ArrowDown' || key === 'ArrowUp' || key === 'ArrowLeft' || key === 'ArrowRight' ||
|
||||
key === 'Enter' || key === ' ' || key === 'Tab' || key === 'w' || key === 'a' || key === 's' || key === 'd') {
|
||||
store.setInputMode('keyboard')
|
||||
}
|
||||
if (key === 'Escape') {
|
||||
if (showChapterSelect.value) {
|
||||
if (store.showSettings) {
|
||||
store.showSettings = false
|
||||
} else if (showChapterSelect.value) {
|
||||
showChapterSelect.value = false
|
||||
} else if (showMenu.value) {
|
||||
showMenu.value = false
|
||||
@@ -133,6 +154,17 @@ function onGlobalKeydown(e: KeyboardEvent) {
|
||||
}
|
||||
}
|
||||
|
||||
function togglePause() {
|
||||
if (!started.value || store.gameEnded) return
|
||||
if (paused.value) {
|
||||
paused.value = false
|
||||
engine.videoManager.getActiveVideoElement()?.play().catch(() => {})
|
||||
} else {
|
||||
paused.value = true
|
||||
engine.videoManager.getActiveVideoElement()?.pause()
|
||||
}
|
||||
}
|
||||
|
||||
function onGlobalMouseMove() {
|
||||
store.setInputMode('mouse')
|
||||
}
|
||||
@@ -199,6 +231,7 @@ init()
|
||||
{{ isFullscreen ? '⛶' : '⛶' }}
|
||||
</button>
|
||||
<button class="top-btn" @click="toggleMenu">{{ t('ui.menu') }}</button>
|
||||
<button class="top-btn" @click="store.setShowSettings(true)">设置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!started" class="start-overlay">
|
||||
@@ -208,6 +241,7 @@ init()
|
||||
<button v-if="store.chapters.length > 0" class="start-btn chapters-btn" @click="openChapterSelect">{{ t('ui.chapters') }}</button>
|
||||
<button v-if="store.achievementDefs.length > 0" class="start-btn achievement-btn" @click="showAchievements = true">成就</button>
|
||||
<button v-if="store.endings.length > 0" class="start-btn gallery-btn" @click="showEndingGallery = true">画廊</button>
|
||||
<button class="start-btn settings-btn" @click="store.setShowSettings(true)">设置</button>
|
||||
</div>
|
||||
<div v-if="store.gameEnded" class="game-end-overlay">
|
||||
<div class="game-end-text">{{ t('ui.gameEnd') }}</div>
|
||||
@@ -249,6 +283,14 @@ init()
|
||||
:visited-ids="store.visitedSceneIds"
|
||||
@close="recapChapterId = null"
|
||||
/>
|
||||
<AccessibilitySettings
|
||||
v-if="store.showSettings"
|
||||
@close="store.setShowSettings(false)"
|
||||
/>
|
||||
<div v-if="paused" class="pause-overlay" @click="togglePause">
|
||||
<div class="pause-text">已暂停</div>
|
||||
<div class="pause-hint">点击或按 Space 继续</div>
|
||||
</div>
|
||||
<AchievementToast
|
||||
:achievement-id="store.toastAchievementId"
|
||||
:definitions="store.achievementDefs"
|
||||
@@ -390,6 +432,35 @@ html, body {
|
||||
color: #ce93d8;
|
||||
}
|
||||
|
||||
.settings-btn {
|
||||
border-color: rgba(255, 255, 255, 0.3);
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
.pause-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 150;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pause-text {
|
||||
font-size: 36px;
|
||||
color: #fff;
|
||||
letter-spacing: 4px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.pause-hint {
|
||||
font-size: 14px;
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.game-end-actions {
|
||||
margin-top: 24px;
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user