feat(game): 添加开会暂停系统——点击开会按钮暂停游戏,可安心发激励,结束开会后恢复

This commit is contained in:
Cloud Bot
2026-03-24 09:13:14 +00:00
parent 6843d2b74c
commit b8ba572ffb
4 changed files with 157 additions and 33 deletions

View File

@@ -474,13 +474,24 @@ export default function GamePage() {
const [selectedTower, setSelectedTower] = useState<TowerType | null>(null)
const [gameReady, setGameReady] = useState(false)
const [waveStarted, setWaveStarted] = useState(false)
// 召唤按钮状态(由 HUD 通过 window.__gameSetWaveBtn 驱动)
const [inMeeting, setInMeeting] = useState(false) // 开会=游戏暂停
const [waveBtn, setWaveBtn] = useState<{ text: string; disabled: boolean }>({
text: '▶ 召唤下一波',
disabled: false,
})
const selectedTowerRef = useRef<TowerType | null>(null)
const handleMeeting = useCallback(() => {
if (!gameReady) return
if (!inMeeting) {
const ok = typeof window !== 'undefined' && (window as any).__gamePause?.()
if (ok) setInMeeting(true)
} else {
const ok = typeof window !== 'undefined' && (window as any).__gameResume?.()
if (ok) setInMeeting(false)
}
}, [gameReady, inMeeting])
const handleSelectTower = useCallback((type: TowerType) => {
const next = selectedTowerRef.current === type ? null : type
selectedTowerRef.current = next
@@ -545,7 +556,8 @@ export default function GamePage() {
;['__gameOnHCChange','__gameOnTowerDeselect','__gameSelectTower',
'__gameReady','__gameDifficulty','__gamePuaBuff',
'__gameGetHC','__gameSpendHC','__gameWaveStarted',
'__gameSetWaveBtn','__gameWaveBtnState','__gameOnWaveClick'].forEach(k => {
'__gameSetWaveBtn','__gameWaveBtnState','__gameOnWaveClick',
'__gamePause','__gameResume','__gameIsPaused'].forEach(k => {
delete (window as any)[k]
})
}
@@ -565,79 +577,126 @@ export default function GamePage() {
style={{ backgroundColor: '#0A1628' }}
/>
{/* 右侧面板HC + 召唤按钮 + PUA激励台 */}
{/* 右侧面板HC + 开会按钮 + 召唤按钮 + PUA激励台 */}
<div style={{
width: '240px',
flexShrink: 0,
backgroundColor: 'rgba(10,18,40,0.97)',
borderLeft: '2px solid #1e3a5f',
borderLeft: `2px solid ${inMeeting ? '#22C55E' : '#1e3a5f'}`,
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
transition: 'border-color 0.2s',
boxShadow: inMeeting ? 'inset 0 0 20px rgba(34,197,94,0.08)' : 'none',
}}>
{/* HC 数量显示 */}
{/* HC 数量 + 开会按钮(同一行) */}
<div style={{
padding: '10px 12px 8px',
padding: '8px 12px',
borderBottom: '1px solid #1e3a5f',
display: 'flex',
alignItems: 'center',
gap: '8px',
}}>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<span style={{ fontFamily: 'VT323, monospace', fontSize: '13px', color: '#64748B' }}>
<div style={{ flex: 1 }}>
<div style={{ fontFamily: 'VT323, monospace', fontSize: '11px', color: '#475569', marginBottom: '1px' }}>
</span>
<span style={{
</div>
<div style={{
fontFamily: "'Press Start 2P', monospace",
fontSize: '12px',
fontSize: '11px',
color: '#A78BFA',
letterSpacing: '1px',
}}>
{hc} HC
</span>
</div>
</div>
{/* 开会按钮 */}
<button
onClick={handleMeeting}
disabled={!gameReady || !waveStarted}
title={inMeeting ? '结束开会,恢复游戏' : '开会暂停,发起激励'}
style={{
flexShrink: 0,
padding: '6px 10px',
backgroundColor: inMeeting ? '#14532D' : '#0F1B2D',
border: `2px solid ${inMeeting ? '#22C55E' : '#1e3a5f'}`,
borderRadius: '8px',
color: inMeeting ? '#4ADE80' : (!gameReady || !waveStarted ? '#334155' : '#94A3B8'),
fontFamily: 'VT323, monospace',
fontSize: '14px',
cursor: !gameReady || !waveStarted ? 'not-allowed' : 'pointer',
transition: 'all 0.15s',
whiteSpace: 'nowrap',
lineHeight: 1.2,
boxShadow: inMeeting ? '0 0 10px rgba(34,197,94,0.3)' : 'none',
}}
>
{inMeeting ? '📋 结束开会' : '📋 开会'}
</button>
</div>
{/* 召唤下一波按钮 */}
<div style={{ padding: '10px 12px', borderBottom: '2px solid #1e3a5f' }}>
{/* 开会中提示条 */}
{inMeeting && (
<div style={{
backgroundColor: '#14532D',
borderBottom: '1px solid #22C55E',
padding: '5px 12px',
fontFamily: 'VT323, monospace',
fontSize: '13px',
color: '#86EFAC',
display: 'flex',
alignItems: 'center',
gap: '6px',
}}>
<span style={{ animation: 'pulse 1.5s ease-in-out infinite' }}></span>
</div>
)}
{/* 召唤下一波按钮(开会时禁用) */}
<div style={{ padding: '8px 12px', borderBottom: '2px solid #1e3a5f' }}>
<button
onClick={() => {
if (inMeeting) return
if (typeof window !== 'undefined') {
(window as any).__gameOnWaveClick?.()
}
}}
disabled={waveBtn.disabled || !gameReady}
disabled={waveBtn.disabled || !gameReady || inMeeting}
style={{
width: '100%',
padding: '10px 8px',
backgroundColor: waveBtn.disabled || !gameReady ? '#0F172A' : '#1e3a5f',
border: `2px solid ${waveBtn.disabled || !gameReady ? '#1e293b' : '#7C3AED'}`,
backgroundColor: waveBtn.disabled || !gameReady || inMeeting ? '#0F172A' : '#1e3a5f',
border: `2px solid ${waveBtn.disabled || !gameReady || inMeeting ? '#1e293b' : '#7C3AED'}`,
borderRadius: '8px',
color: waveBtn.disabled || !gameReady ? '#4B5563' : '#C4B5FD',
color: waveBtn.disabled || !gameReady || inMeeting ? '#4B5563' : '#C4B5FD',
fontFamily: 'VT323, monospace',
fontSize: '20px',
cursor: waveBtn.disabled || !gameReady ? 'not-allowed' : 'pointer',
cursor: waveBtn.disabled || !gameReady || inMeeting ? 'not-allowed' : 'pointer',
transition: 'all 0.15s ease',
letterSpacing: '1px',
lineHeight: 1.2,
boxShadow: waveBtn.disabled || !gameReady ? 'none' : '0 0 12px rgba(124,58,237,0.3)',
boxShadow: waveBtn.disabled || !gameReady || inMeeting ? 'none' : '0 0 12px rgba(124,58,237,0.3)',
}}
onMouseEnter={e => {
if (!waveBtn.disabled && gameReady) {
if (!waveBtn.disabled && gameReady && !inMeeting) {
(e.currentTarget as HTMLButtonElement).style.backgroundColor = '#2d3a5e'
;(e.currentTarget as HTMLButtonElement).style.boxShadow = '0 0 18px rgba(124,58,237,0.5)'
}
}}
onMouseLeave={e => {
if (!waveBtn.disabled && gameReady) {
if (!waveBtn.disabled && gameReady && !inMeeting) {
(e.currentTarget as HTMLButtonElement).style.backgroundColor = '#1e3a5f'
;(e.currentTarget as HTMLButtonElement).style.boxShadow = '0 0 12px rgba(124,58,237,0.3)'
}
}}
>
{waveBtn.text}
{inMeeting ? '开会中...' : waveBtn.text}
</button>
</div>
{/* PUA 激励台 */}
<PuaPanel gameReady={gameReady} hc={hc} waveStarted={waveStarted} />
<PuaPanel gameReady={gameReady} hc={hc} waveStarted={waveStarted || inMeeting} />
</div>
</div>

View File

@@ -725,3 +725,8 @@
/* 深色背景(终端/代码块保留不覆盖bg-slate-900 bg-slate-950 */
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}