饥荒改名年糕生存,优化动画

This commit is contained in:
李琦
2026-08-15 09:52:01 +08:00
parent 7c56d1e658
commit e70b7aa3d7
10 changed files with 385 additions and 19 deletions

View File

@@ -28,6 +28,9 @@ const detail = ref(null) // {game, owned, best_score}
const phase = ref('loading') // loading / locked / ready / playing / over
const liveScore = ref(0) // 实时得分
const finalScore = ref(0) // 结束得分
const displayName = computed(() => (code === 'starve' ? '年糕求生' : detail.value?.game?.name || '游戏'))
const showManual = ref(false)
const showSettings = ref(false)
const result = ref(null) // 结算响应
const useDouble = ref(false) // 是否使用双倍积分卡
const bag = ref([]) // 我的道具背包
@@ -61,6 +64,31 @@ function onHeadMouseMove(e) {
if (!floatHead.value) return
headShow.value = e.clientY <= 130
}
const touchMode = ref(localStorage.getItem('starve_touch_mode') || 'both')
function saveTouchMode(mode) {
touchMode.value = mode
localStorage.setItem('starve_touch_mode', mode)
window.dispatchEvent(new CustomEvent('starve-touch-mode', { detail: mode }))
}
function openManual() {
showManual.value = true
if (phase.value === 'playing') gameRef.value?.setPaused?.(true)
}
function closeManual() {
showManual.value = false
if (phase.value === 'playing') gameRef.value?.setPaused?.(false)
}
function openSettings() {
showSettings.value = true
if (phase.value === 'playing') gameRef.value?.setPaused?.(true)
}
function closeSettings() {
showSettings.value = false
if (phase.value === 'playing') gameRef.value?.setPaused?.(false)
}
function togglePause() {
gameRef.value?.togglePause?.()
}
const playHeadClasses = computed(() => ({
'play-head-float': floatHead.value,
'play-head-hidden': floatHead.value && !headShow.value,
@@ -549,10 +577,11 @@ watch(() => coopStore.roomState?.code, (code) => {
})
onMounted(() => {
window.addEventListener('mousemove', onHeadMouseMove)
// play-head 通过吸顶按钮手动展开/收起,不再跟随鼠标自动弹出
// 宽屏游戏(如饥荒):给 body 打标解除全局 1200px 限宽
if (meta?.wide) document.body.classList.add('wide-page')
load()
window.addEventListener('nlg:open-manual', openManual)
if (coopable) {
window.addEventListener('nlg:pending-join', tryPendingJoin)
tryPendingJoin()
@@ -560,8 +589,9 @@ onMounted(() => {
})
onBeforeUnmount(() => {
document.body.classList.remove('wide-page')
window.removeEventListener('nlg:open-manual', openManual)
window.removeEventListener('nlg:pending-join', tryPendingJoin)
window.removeEventListener('mousemove', onHeadMouseMove)
// play-head 展开状态由组件自行管理
clearInterval(inviteTickTimer)
gameRef.value?.stop()
// 离开游玩页断开联机连接(房间自动退出)
@@ -571,18 +601,24 @@ onBeforeUnmount(() => {
<template>
<div v-if="detail" class="play-wrap" :class="{ 'play-fill': meta?.wide }">
<button v-if="floatHead" class="head-toggle" @click="headShow = !headShow">
{{ headShow ? ' 收起' : ' 年糕求生' }}
</button>
<!-- 顶部信息条 -->
<div class="play-head panel" :class="playHeadClasses">
<button class="btn btn-ghost btn-sm" @click="router.back()"> 返回</button>
<GameIcon class="g-icon" :code="code" :icon="detail.game.icon" :size="34" />
<div class="g-info">
<b>{{ detail.game.name }}</b>
<b>{{ displayName }}</b>
<span class="text-dim" style="font-size: 12px">{{ meta.controls }}</span>
</div>
<div class="g-stats">
<span class="stat-chip">本局 <b class="text-accent">{{ liveScore }}</b></span>
<span class="stat-chip">最高 <b class="text-primary">{{ detail.best_score }}</b></span>
<span class="stat-chip">💰 {{ userStore.user?.points ?? 0 }}</span>
<button class="btn btn-ghost btn-sm" @click="openManual">📖 手册</button>
<button class="btn btn-ghost btn-sm" @click="openSettings"> 设置</button>
<button v-if="phase === 'playing'" class="btn btn-ghost btn-sm" @click="togglePause"> 暂停</button>
</div>
</div>
<!-- 游戏区域 -->
@@ -595,7 +631,7 @@ onBeforeUnmount(() => {
<!-- 1. 模式选择 -->
<template v-if="coopStage === 'mode'">
<div class="ov-icon"><GameIcon :code="code" :icon="detail.game.icon" :size="56" /></div>
<h3>{{ detail.game.name }}</h3>
<h3>{{ displayName }}</h3>
<p class="text-dim ov-desc">{{ detail.game.description }}</p>
<div class="mode-cards">
<button class="mode-card" @click="coopStage = 'solo'">
@@ -617,6 +653,10 @@ onBeforeUnmount(() => {
<i>2~4 人共享同一世界</i>
</button>
</div>
<div class="ov-btns">
<button class="btn btn-ghost btn-sm" @click="openManual">📖 操作手册</button>
<button class="btn btn-ghost btn-sm" @click="openSettings"> 设置</button>
</div>
<button class="btn btn-ghost" @click="coopStage = 'skins'">
更衣室 · 30 款人物皮肤
</button>
@@ -783,9 +823,13 @@ onBeforeUnmount(() => {
<!-- ============ 普通游戏原有开始面板 ============ -->
<template v-else>
<div class="ov-icon"><GameIcon :code="code" :icon="detail.game.icon" :size="levelsMeta ? 40 : 56" /></div>
<h3>{{ detail.game.name }}</h3>
<h3>{{ displayName }}</h3>
<p v-if="!levelsMeta" class="text-dim ov-desc">{{ detail.game.description }}</p>
<p class="text-dim" style="font-size: 12px">🎮 {{ meta.controls }}</p>
<div class="ov-btns">
<button class="btn btn-ghost btn-sm" @click="openManual">📖 操作手册</button>
<button class="btn btn-ghost btn-sm" @click="openSettings"> 设置</button>
</div>
<label v-if="meta.supports.includes('double_points') && propCount('double_points') > 0" class="double-check">
<input v-model="useDouble" type="checkbox" />
使用 双倍积分卡 {{ propCount('double_points') }} 本局积分翻倍
@@ -893,6 +937,54 @@ onBeforeUnmount(() => {
</div>
</div>
</div>
<!-- 操作手册 -->
<div v-if="showManual" class="modal-mask" @click.self="closeManual">
<div class="mod-modal panel manual-modal">
<button class="bm-close" @click="closeManual"></button>
<h3>操作手册</h3>
<p class="text-dim" style="font-size: 12px; line-height: 1.6; text-align: left">
{{ meta.controls }}<br />
移动端点击地面移动{{ touchMode === 'both' || touchMode === 'joystick' ? ',也可使用左下角摇杆' : '' }}点击目标会自动攻击/采集<br />
快捷键WASD 移动 · 空格采集 · F 攻击 · C 合成 · M 地图 · P/Esc 暂停
</p>
<div class="ov-btns">
<button class="btn" @click="closeManual">知道了</button>
</div>
</div>
</div>
<!-- 设置 -->
<div v-if="showSettings" class="modal-mask" @click.self="closeSettings">
<div class="mod-modal panel settings-modal">
<button class="bm-close" @click="closeSettings"></button>
<h3>设置</h3>
<div class="setting-block">
<b>移动端移动方式</b>
<div class="setting-options">
<button class="chip" :class="{ on: touchMode === 'both' }" @click="saveTouchMode('both')">点击 + 摇杆</button>
<button class="chip" :class="{ on: touchMode === 'tap' }" @click="saveTouchMode('tap')">仅点击</button>
<button class="chip" :class="{ on: touchMode === 'joystick' }" @click="saveTouchMode('joystick')">仅摇杆</button>
</div>
<p class="text-dim" style="font-size: 12px">默认都支持摇杆未选中时自动隐藏</p>
</div>
<div class="setting-block">
<b>PC 快捷键</b>
<ul class="shortcut-list">
<li><span>WASD / 方向键</span>移动</li>
<li><span>空格</span>采集 / 交互</li>
<li><span>F</span>攻击</li>
<li><span>1~9</span>使用物品栏</li>
<li><span>C</span>合成</li>
<li><span>M</span>地图</li>
<li><span>P / Esc</span>暂停 / 继续</li>
</ul>
</div>
<div class="ov-btns">
<button class="btn" @click="closeSettings">完成</button>
</div>
</div>
</div>
</div>
<!-- 未拥有提示 -->
@@ -1657,4 +1749,55 @@ onBeforeUnmount(() => {
.overlay.room-entry {
justify-content: center;
}
/* 二级导航:吸顶小按钮手动展开/收起,不随鼠标弹出 */
.head-toggle {
position: fixed;
top: 2px;
left: 50%;
transform: translateX(-50%);
z-index: 160;
padding: 4px 12px;
font-size: 12px;
font-weight: 700;
border-radius: 999px;
border: 1px solid var(--border);
background: var(--bg-panel);
color: var(--text);
cursor: pointer;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.35);
}
/* 手册/设置弹窗 */
.manual-modal,
.settings-modal {
width: min(560px, 100%);
text-align: left;
}
.setting-block {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: 12px;
padding: 12px 14px;
margin: 10px 0;
}
.setting-options {
display: flex;
gap: 8px;
flex-wrap: wrap;
margin-top: 10px;
}
.shortcut-list {
margin: 8px 0 0;
padding-left: 18px;
display: flex;
flex-direction: column;
gap: 6px;
font-size: 13px;
}
.shortcut-list span {
display: inline-block;
min-width: 96px;
color: var(--primary-2);
font-weight: 700;
}
</style>