184 lines
5.4 KiB
TypeScript
184 lines
5.4 KiB
TypeScript
import type { MapConfig } from '../data/mapConfigs'
|
||
import { ALL_MAPS } from '../data/mapConfigs'
|
||
import { GameManager } from '../GameManager'
|
||
|
||
/**
|
||
* 地图切换过渡弹窗(DOM 层,覆盖 canvas)
|
||
* 显示当前地图通关信息,3秒后自动关闭并触发下一张地图加载
|
||
*/
|
||
export class MapTransitionModal {
|
||
private container: HTMLElement | null = null
|
||
private autoCloseTimer: ReturnType<typeof setTimeout> | null = null
|
||
|
||
/**
|
||
* 显示地图过关弹窗
|
||
* @param clearedMap 刚通关的地图配置
|
||
* @param onNext 3秒后自动触发的回调(加载下一张地图)
|
||
*/
|
||
show(clearedMap: MapConfig, onNext: () => void): void {
|
||
this.cleanup()
|
||
const manager = GameManager.getInstance()
|
||
const nextMapIndex = manager.currentMapIndex + 1
|
||
const nextMap = ALL_MAPS[nextMapIndex]
|
||
const isFinalMap = !nextMap
|
||
|
||
this.createDOM(clearedMap, nextMap ?? null, isFinalMap, onNext)
|
||
}
|
||
|
||
private createDOM(
|
||
clearedMap: MapConfig,
|
||
nextMap: MapConfig | null,
|
||
isFinalMap: boolean,
|
||
onNext: () => void
|
||
): void {
|
||
const manager = GameManager.getInstance()
|
||
const kpi = manager.kpi
|
||
|
||
const overlay = document.createElement('div')
|
||
overlay.id = 'map-transition-overlay'
|
||
overlay.style.cssText = `
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0,0,0,0.85);
|
||
z-index: 9998;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-family: 'VT323', monospace;
|
||
animation: mtFadeIn 0.4s ease;
|
||
`
|
||
|
||
const card = document.createElement('div')
|
||
card.style.cssText = `
|
||
background: #0f1b2d;
|
||
border: 2px solid #7c3aed;
|
||
border-radius: 16px;
|
||
padding: 36px 48px;
|
||
max-width: 520px;
|
||
width: 90%;
|
||
box-shadow: 0 0 60px rgba(124,58,237,0.6);
|
||
text-align: center;
|
||
animation: mtSlideIn 0.4s ease;
|
||
`
|
||
|
||
// 过关标题
|
||
const titleEl = document.createElement('div')
|
||
titleEl.textContent = isFinalMap ? '全部通关!最终胜利!' : `过关!`
|
||
titleEl.style.cssText = `
|
||
font-size: 36px;
|
||
color: #fbbf24;
|
||
margin-bottom: 8px;
|
||
letter-spacing: 3px;
|
||
`
|
||
card.appendChild(titleEl)
|
||
|
||
// 地图名称
|
||
const mapNameEl = document.createElement('div')
|
||
mapNameEl.textContent = `「${clearedMap.name}」已清场`
|
||
mapNameEl.style.cssText = `
|
||
font-size: 22px;
|
||
color: #a78bfa;
|
||
margin-bottom: 20px;
|
||
`
|
||
card.appendChild(mapNameEl)
|
||
|
||
// 分割线
|
||
const hr = document.createElement('div')
|
||
hr.style.cssText = `
|
||
height: 1px;
|
||
background: linear-gradient(90deg, transparent, #7c3aed, transparent);
|
||
margin-bottom: 16px;
|
||
`
|
||
card.appendChild(hr)
|
||
|
||
// KPI & 说明
|
||
const statsEl = document.createElement('div')
|
||
statsEl.innerHTML = `最终KPI: <span style="color:#34d399">${kpi}%</span> | <span style="color:#94a3b8">HC 将重置为 170</span>`
|
||
statsEl.style.cssText = `
|
||
font-size: 20px;
|
||
color: #e2e8f0;
|
||
margin-bottom: 24px;
|
||
`
|
||
card.appendChild(statsEl)
|
||
|
||
// 下一关提示 / 最终胜利提示
|
||
const nextEl = document.createElement('div')
|
||
if (isFinalMap) {
|
||
nextEl.textContent = '恭喜完成全部关卡!你是真正的打工人传奇!'
|
||
nextEl.style.cssText = `
|
||
font-size: 18px;
|
||
color: #fbbf24;
|
||
background: rgba(251,191,36,0.1);
|
||
border: 1px solid #fbbf24;
|
||
border-radius: 8px;
|
||
padding: 12px 16px;
|
||
margin-bottom: 20px;
|
||
`
|
||
} else {
|
||
nextEl.innerHTML = `<span style="color:#9ca3af">下一关:</span> <span style="color:#60a5fa">${nextMap!.name}</span>`
|
||
nextEl.style.cssText = `
|
||
font-size: 20px;
|
||
color: #e2e8f0;
|
||
background: rgba(96,165,250,0.1);
|
||
border: 1px solid #3b82f6;
|
||
border-radius: 8px;
|
||
padding: 12px 16px;
|
||
margin-bottom: 20px;
|
||
`
|
||
}
|
||
card.appendChild(nextEl)
|
||
|
||
// 倒计时提示(HC 重置提醒)
|
||
const countdownEl = document.createElement('div')
|
||
countdownEl.id = 'mt-countdown'
|
||
countdownEl.textContent = isFinalMap ? '3秒后返回主菜单...' : '3秒后进入下一关,HC重置为170...'
|
||
countdownEl.style.cssText = `
|
||
font-size: 16px;
|
||
color: #6b7280;
|
||
`
|
||
card.appendChild(countdownEl)
|
||
|
||
// 注入动画
|
||
const style = document.createElement('style')
|
||
style.textContent = `
|
||
@keyframes mtFadeIn { from { opacity: 0 } to { opacity: 1 } }
|
||
@keyframes mtSlideIn { from { transform: scale(0.85); opacity: 0 } to { transform: scale(1); opacity: 1 } }
|
||
`
|
||
document.head.appendChild(style)
|
||
|
||
overlay.appendChild(card)
|
||
document.body.appendChild(overlay)
|
||
this.container = overlay
|
||
|
||
// 3秒自动触发
|
||
let remaining = 3
|
||
const tick = () => {
|
||
remaining--
|
||
const el = document.getElementById('mt-countdown')
|
||
if (el) el.textContent = isFinalMap
|
||
? `${remaining}秒后返回主菜单...`
|
||
: `${remaining}秒后进入下一关,HC重置为170...`
|
||
}
|
||
const interval = setInterval(tick, 1000)
|
||
this.autoCloseTimer = setTimeout(() => {
|
||
clearInterval(interval)
|
||
this.cleanup()
|
||
onNext()
|
||
}, 3000)
|
||
}
|
||
|
||
cleanup(): void {
|
||
if (this.autoCloseTimer !== null) {
|
||
clearTimeout(this.autoCloseTimer)
|
||
this.autoCloseTimer = null
|
||
}
|
||
const el = document.getElementById('map-transition-overlay')
|
||
if (el) el.remove()
|
||
this.container = null
|
||
}
|
||
|
||
destroy(): void {
|
||
this.cleanup()
|
||
}
|
||
}
|