fix(game): 修复输了能进下一关的Bug、换关后塔残留、召唤按钮太小、战斗前不能发激励
This commit is contained in:
@@ -48,7 +48,7 @@ function calcPuaCost(hc: number): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── PUA 输入面板 ─────────────────────────────────────────────────────────────
|
// ── PUA 输入面板 ─────────────────────────────────────────────────────────────
|
||||||
function PuaPanel({ gameReady, hc }: { gameReady: boolean; hc: number }) {
|
function PuaPanel({ gameReady, hc, waveStarted }: { gameReady: boolean; hc: number; waveStarted: boolean }) {
|
||||||
const [text, setText] = useState('')
|
const [text, setText] = useState('')
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [result, setResult] = useState<PuaResult | null>(null)
|
const [result, setResult] = useState<PuaResult | null>(null)
|
||||||
@@ -61,7 +61,7 @@ function PuaPanel({ gameReady, hc }: { gameReady: boolean; hc: number }) {
|
|||||||
const canAfford = hc >= cost
|
const canAfford = hc >= cost
|
||||||
|
|
||||||
const handleSubmit = useCallback(async () => {
|
const handleSubmit = useCallback(async () => {
|
||||||
if (!text.trim() || loading || !gameReady) return
|
if (!text.trim() || loading || !gameReady || !waveStarted) return
|
||||||
|
|
||||||
// 先从游戏扣除 HC(扣不到则拒绝)
|
// 先从游戏扣除 HC(扣不到则拒绝)
|
||||||
const spendHC: ((n: number) => boolean) | undefined =
|
const spendHC: ((n: number) => boolean) | undefined =
|
||||||
@@ -102,7 +102,7 @@ function PuaPanel({ gameReady, hc }: { gameReady: boolean; hc: number }) {
|
|||||||
} finally {
|
} finally {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
}, [text, loading, gameReady, hc])
|
}, [text, loading, gameReady, hc, waveStarted])
|
||||||
|
|
||||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||||
if (e.key === 'Enter' && !e.shiftKey) {
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
@@ -143,7 +143,7 @@ function PuaPanel({ gameReady, hc }: { gameReady: boolean; hc: number }) {
|
|||||||
color: '#475569',
|
color: '#475569',
|
||||||
marginTop: '2px',
|
marginTop: '2px',
|
||||||
}}>
|
}}>
|
||||||
输入打鸡血的话,AI判断鸡血值
|
{!waveStarted ? '⚠ 召唤第一波后才能激励' : '输入打鸡血的话,AI判断鸡血值'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -210,12 +210,12 @@ function PuaPanel({ gameReady, hc }: { gameReady: boolean; hc: number }) {
|
|||||||
onChange={e => setText(e.target.value)}
|
onChange={e => setText(e.target.value)}
|
||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
disabled={loading || !gameReady}
|
disabled={loading || !gameReady || !waveStarted}
|
||||||
rows={4}
|
rows={4}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
backgroundColor: '#0F1B2D',
|
backgroundColor: '#0F1B2D',
|
||||||
border: `1px solid ${canAfford ? '#1e3a5f' : '#7F1D1D'}`,
|
border: `1px solid ${!waveStarted ? '#1e3a5f' : canAfford ? '#1e3a5f' : '#7F1D1D'}`,
|
||||||
borderRadius: '6px',
|
borderRadius: '6px',
|
||||||
color: '#E2E8F0',
|
color: '#E2E8F0',
|
||||||
fontFamily: 'VT323, monospace',
|
fontFamily: 'VT323, monospace',
|
||||||
@@ -224,7 +224,7 @@ function PuaPanel({ gameReady, hc }: { gameReady: boolean; hc: number }) {
|
|||||||
resize: 'none',
|
resize: 'none',
|
||||||
outline: 'none',
|
outline: 'none',
|
||||||
lineHeight: 1.4,
|
lineHeight: 1.4,
|
||||||
opacity: gameReady ? 1 : 0.5,
|
opacity: gameReady && waveStarted ? 1 : 0.4,
|
||||||
transition: 'border-color 0.2s',
|
transition: 'border-color 0.2s',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@@ -232,23 +232,23 @@ function PuaPanel({ gameReady, hc }: { gameReady: boolean; hc: number }) {
|
|||||||
{/* 提交按钮 */}
|
{/* 提交按钮 */}
|
||||||
<button
|
<button
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
disabled={loading || !text.trim() || !gameReady || !canAfford}
|
disabled={loading || !text.trim() || !gameReady || !canAfford || !waveStarted}
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
padding: '8px',
|
padding: '8px',
|
||||||
backgroundColor: loading ? '#1e3a5f' : canAfford ? '#7C3AED' : '#4C1D95',
|
backgroundColor: loading ? '#1e3a5f' : !waveStarted ? '#1e3a5f' : canAfford ? '#7C3AED' : '#4C1D95',
|
||||||
border: 'none',
|
border: 'none',
|
||||||
borderRadius: '6px',
|
borderRadius: '6px',
|
||||||
color: '#E2E8F0',
|
color: '#E2E8F0',
|
||||||
fontFamily: "'Press Start 2P', monospace",
|
fontFamily: "'Press Start 2P', monospace",
|
||||||
fontSize: '8px',
|
fontSize: '8px',
|
||||||
cursor: loading || !text.trim() || !gameReady || !canAfford ? 'not-allowed' : 'pointer',
|
cursor: loading || !text.trim() || !gameReady || !canAfford || !waveStarted ? 'not-allowed' : 'pointer',
|
||||||
opacity: !text.trim() || !gameReady || !canAfford ? 0.45 : 1,
|
opacity: !text.trim() || !gameReady || !canAfford || !waveStarted ? 0.45 : 1,
|
||||||
transition: 'all 0.15s',
|
transition: 'all 0.15s',
|
||||||
letterSpacing: '0.5px',
|
letterSpacing: '0.5px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{loading ? '分析中...' : !canAfford ? 'HC不足' : `发起激励 -${cost}HC`}
|
{loading ? '分析中...' : !waveStarted ? '战斗开始后可用' : !canAfford ? 'HC不足' : `发起激励 -${cost}HC`}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{/* 当前结果 */}
|
{/* 当前结果 */}
|
||||||
@@ -397,6 +397,7 @@ export default function GamePage() {
|
|||||||
const [hc, setHc] = useState(200)
|
const [hc, setHc] = useState(200)
|
||||||
const [selectedTower, setSelectedTower] = useState<TowerType | null>(null)
|
const [selectedTower, setSelectedTower] = useState<TowerType | null>(null)
|
||||||
const [gameReady, setGameReady] = useState(false)
|
const [gameReady, setGameReady] = useState(false)
|
||||||
|
const [waveStarted, setWaveStarted] = useState(false) // 第一波开始后才允许激励
|
||||||
const selectedTowerRef = useRef<TowerType | null>(null)
|
const selectedTowerRef = useRef<TowerType | null>(null)
|
||||||
|
|
||||||
const handleSelectTower = useCallback((type: TowerType) => {
|
const handleSelectTower = useCallback((type: TowerType) => {
|
||||||
@@ -438,6 +439,13 @@ export default function GamePage() {
|
|||||||
if (mounted) { selectedTowerRef.current = null; setSelectedTower(null) }
|
if (mounted) { selectedTowerRef.current = null; setSelectedTower(null) }
|
||||||
}
|
}
|
||||||
;(window as any).__gameReady = () => { if (mounted) setGameReady(true) }
|
;(window as any).__gameReady = () => { if (mounted) setGameReady(true) }
|
||||||
|
// 轮询 __gameWaveStarted(Phaser 设置后通知 React)
|
||||||
|
const checkWaveStarted = setInterval(() => {
|
||||||
|
if ((window as any).__gameWaveStarted) {
|
||||||
|
if (mounted) setWaveStarted(true)
|
||||||
|
clearInterval(checkWaveStarted)
|
||||||
|
}
|
||||||
|
}, 200)
|
||||||
}
|
}
|
||||||
|
|
||||||
gameRef.current = new Phaser.Game(config)
|
gameRef.current = new Phaser.Game(config)
|
||||||
@@ -450,7 +458,7 @@ export default function GamePage() {
|
|||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
;['__gameOnHCChange','__gameOnTowerDeselect','__gameSelectTower',
|
;['__gameOnHCChange','__gameOnTowerDeselect','__gameSelectTower',
|
||||||
'__gameReady','__gameDifficulty','__gamePuaBuff',
|
'__gameReady','__gameDifficulty','__gamePuaBuff',
|
||||||
'__gameGetHC','__gameSpendHC'].forEach(k => {
|
'__gameGetHC','__gameSpendHC','__gameWaveStarted'].forEach(k => {
|
||||||
delete (window as any)[k]
|
delete (window as any)[k]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -470,7 +478,7 @@ export default function GamePage() {
|
|||||||
style={{ backgroundColor: '#0A1628' }}
|
style={{ backgroundColor: '#0A1628' }}
|
||||||
/>
|
/>
|
||||||
{/* PUA 激励台(右侧) */}
|
{/* PUA 激励台(右侧) */}
|
||||||
<PuaPanel gameReady={gameReady} hc={hc} />
|
<PuaPanel gameReady={gameReady} hc={hc} waveStarted={waveStarted} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 底部塔选择面板 */}
|
{/* 底部塔选择面板 */}
|
||||||
|
|||||||
@@ -365,8 +365,11 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene {
|
|||||||
this.waveManager.startNextWave()
|
this.waveManager.startNextWave()
|
||||||
const waveNum = this.waveManager.getCurrentWaveNumber()
|
const waveNum = this.waveManager.getCurrentWaveNumber()
|
||||||
this.hud.showWaveBanner(waveNum, this.waveManager.totalWaves)
|
this.hud.showWaveBanner(waveNum, this.waveManager.totalWaves)
|
||||||
// 波次开始音效
|
|
||||||
AudioEngine.getInstance().playWaveStart()
|
AudioEngine.getInstance().playWaveStart()
|
||||||
|
// 通知 React 层:战斗已开始,允许发起激励
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
;(window as any).__gameWaveStarted = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onWeeklyReport(): void {
|
private onWeeklyReport(): void {
|
||||||
@@ -375,7 +378,11 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private onMapCleared(): void {
|
private onMapCleared(): void {
|
||||||
|
// 若游戏已失败,不触发地图切换
|
||||||
|
if (this.manager.gameState === 'defeat') return
|
||||||
|
|
||||||
this.mapInTransition = true
|
this.mapInTransition = true
|
||||||
|
this.autoNextWaveTimer = -1
|
||||||
this.isWaveRunning = false
|
this.isWaveRunning = false
|
||||||
this.hud.disableWaveButton()
|
this.hud.disableWaveButton()
|
||||||
this.hud.setWaveButtonText('关卡完成!')
|
this.hud.setWaveButtonText('关卡完成!')
|
||||||
@@ -390,6 +397,8 @@ export function createGameScene(PhaserLib: typeof Phaser): typeof Phaser.Scene {
|
|||||||
this.manager.totalWaveCleared += currentMap.waveCount
|
this.manager.totalWaveCleared += currentMap.waveCount
|
||||||
this.manager.currentMapIndex = nextIndex
|
this.manager.currentMapIndex = nextIndex
|
||||||
this.waveManager.clearAllEnemies()
|
this.waveManager.clearAllEnemies()
|
||||||
|
// 清除上一关所有防御塔
|
||||||
|
this.towerManager.clearAllTowers()
|
||||||
this.loadMap(ALL_MAPS[nextIndex])
|
this.loadMap(ALL_MAPS[nextIndex])
|
||||||
this.hud.enableWaveButton()
|
this.hud.enableWaveButton()
|
||||||
this.hud.setWaveButtonText('▶ 召唤下一波')
|
this.hud.setWaveButtonText('▶ 召唤下一波')
|
||||||
|
|||||||
@@ -258,6 +258,17 @@ export class TowerManager {
|
|||||||
return this.towers
|
return this.towers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 切换地图时清除所有防御塔(静默销毁,不播放音效) */
|
||||||
|
clearAllTowers(): void {
|
||||||
|
for (const tower of this.towers) {
|
||||||
|
tower.destroy()
|
||||||
|
}
|
||||||
|
this.towers = []
|
||||||
|
this.occupiedCells.clear()
|
||||||
|
this.infoLabel?.destroy()
|
||||||
|
this.infoLabel = null
|
||||||
|
}
|
||||||
|
|
||||||
hasTowerAt(gridX: number, gridY: number): boolean {
|
hasTowerAt(gridX: number, gridY: number): boolean {
|
||||||
return this.occupiedCells.has(`${gridX},${gridY}`)
|
return this.occupiedCells.has(`${gridX},${gridY}`)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,25 +23,27 @@ export class HUD {
|
|||||||
*/
|
*/
|
||||||
createWaveButton(onClick: () => void): void {
|
createWaveButton(onClick: () => void): void {
|
||||||
if (this.waveBtn) this.waveBtn.destroy()
|
if (this.waveBtn) this.waveBtn.destroy()
|
||||||
this._onClick = onClick // 保存引用,供 enableWaveButton 重新绑定
|
this._onClick = onClick
|
||||||
|
|
||||||
this.waveBtn = this.scene.add
|
this.waveBtn = this.scene.add
|
||||||
.text(GAME_WIDTH - 160, HUD_HEIGHT + 20, '▶ 召唤下一波', {
|
.text(GAME_WIDTH / 2, HUD_HEIGHT + 16, '▶ 召唤下一波', {
|
||||||
fontFamily: "'Press Start 2P', monospace",
|
fontFamily: 'VT323, monospace',
|
||||||
fontSize: '9px',
|
fontSize: '26px',
|
||||||
color: '#A78BFA',
|
color: '#A78BFA',
|
||||||
backgroundColor: '#1e3a5f',
|
backgroundColor: '#1e3a5f',
|
||||||
padding: { x: 10, y: 6 },
|
padding: { x: 20, y: 8 },
|
||||||
|
stroke: '#7C3AED',
|
||||||
|
strokeThickness: 1,
|
||||||
})
|
})
|
||||||
.setOrigin(0, 0)
|
.setOrigin(0.5, 0)
|
||||||
.setDepth(20)
|
.setDepth(20)
|
||||||
.setInteractive({ useHandCursor: true })
|
.setInteractive({ useHandCursor: true })
|
||||||
|
|
||||||
this.waveBtn.on('pointerover', () => {
|
this.waveBtn.on('pointerover', () => {
|
||||||
if (this.waveBtn) this.waveBtn.setStyle({ backgroundColor: '#2d5a8e' })
|
if (this.waveBtn) this.waveBtn.setStyle({ backgroundColor: '#2d5a8e', color: '#C4B5FD' })
|
||||||
})
|
})
|
||||||
this.waveBtn.on('pointerout', () => {
|
this.waveBtn.on('pointerout', () => {
|
||||||
if (this.waveBtn) this.waveBtn.setStyle({ backgroundColor: '#1e3a5f' })
|
if (this.waveBtn) this.waveBtn.setStyle({ backgroundColor: '#1e3a5f', color: '#A78BFA' })
|
||||||
})
|
})
|
||||||
this.waveBtn.on('pointerdown', () => onClick())
|
this.waveBtn.on('pointerdown', () => onClick())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user