This commit is contained in:
wfz
2026-04-15 20:58:16 +08:00
commit 8ab9a9fa70
33 changed files with 6140 additions and 0 deletions

149
frontend/src/ui/hud.js Normal file
View File

@@ -0,0 +1,149 @@
import { WEAPONS, WEAPON_CONFIG } from '../utils/constants.js'
export class HUD {
constructor(container) {
this.container = container
this.container.className = 'hud-container'
this.container.innerHTML = ''
this.visible = false
this._build()
}
_build() {
this.healthBar = document.createElement('div')
this.healthBar.className = 'hud-health-bar'
this.healthBar.innerHTML = '<div class="hud-health-fill"></div><span class="hud-health-text">100</span>'
this.container.appendChild(this.healthBar)
this.weaponPanel = document.createElement('div')
this.weaponPanel.className = 'hud-weapon-panel'
const weaponList = [WEAPONS.PISTOL, WEAPONS.MACHINE_GUN, WEAPONS.SHOTGUN, WEAPONS.GRENADE]
this.weaponSlots = []
for (let i = 0; i < weaponList.length; i++) {
const slot = document.createElement('div')
slot.className = 'hud-weapon-slot'
slot.dataset.index = i
const keyLabel = document.createElement('span')
keyLabel.className = 'hud-weapon-key'
keyLabel.textContent = (i + 1).toString()
const weaponName = document.createElement('span')
weaponName.className = 'hud-weapon-name'
weaponName.textContent = WEAPON_CONFIG[weaponList[i]].name
const ammoText = document.createElement('span')
ammoText.className = 'hud-weapon-ammo'
slot.appendChild(keyLabel)
slot.appendChild(weaponName)
slot.appendChild(ammoText)
this.weaponSlots.push({ slot, ammoText })
this.weaponPanel.appendChild(slot)
}
this.container.appendChild(this.weaponPanel)
this.grenadeChargeBar = document.createElement('div')
this.grenadeChargeBar.className = 'hud-grenade-charge'
this.grenadeChargeBar.innerHTML = '<div class="hud-grenade-charge-fill"></div>'
this.grenadeChargeBar.style.display = 'none'
this.container.appendChild(this.grenadeChargeBar)
this.infoPanel = document.createElement('div')
this.infoPanel.className = 'hud-info-panel'
this.waveText = document.createElement('span')
this.waveText.className = 'hud-info-item'
this.waveText.textContent = 'Wave: 0'
this.scoreText = document.createElement('span')
this.scoreText.className = 'hud-info-item'
this.scoreText.textContent = 'Score: 0'
this.timeText = document.createElement('span')
this.timeText.className = 'hud-info-item'
this.timeText.textContent = 'Time: 0:00'
this.infoPanel.appendChild(this.waveText)
this.infoPanel.appendChild(this.scoreText)
this.infoPanel.appendChild(this.timeText)
this.container.appendChild(this.infoPanel)
this.crosshair = document.createElement('div')
this.crosshair.className = 'hud-crosshair'
this.container.appendChild(this.crosshair)
this.killFeed = document.createElement('div')
this.killFeed.className = 'hud-kill-feed'
this.container.appendChild(this.killFeed)
}
show() {
this.visible = true
this.container.style.display = 'flex'
}
hide() {
this.visible = false
this.container.style.display = 'none'
}
updateHealth(health) {
const fill = this.healthBar.querySelector('.hud-health-fill')
const text = this.healthBar.querySelector('.hud-health-text')
const pct = Math.max(0, Math.min(100, health))
fill.style.width = pct + '%'
if (pct > 60) fill.style.backgroundColor = '#44ff44'
else if (pct > 30) fill.style.backgroundColor = '#ffaa00'
else fill.style.backgroundColor = '#ff4444'
text.textContent = Math.ceil(pct)
}
updateWeapons(currentIndex, ammo) {
const weaponList = [WEAPONS.PISTOL, WEAPONS.MACHINE_GUN, WEAPONS.SHOTGUN, WEAPONS.GRENADE]
for (let i = 0; i < this.weaponSlots.length; i++) {
const slot = this.weaponSlots[i]
slot.slot.classList.toggle('hud-weapon-active', i === currentIndex)
const weaponKey = weaponList[i]
const currentAmmo = ammo[weaponKey]
if (currentAmmo === Infinity) {
slot.ammoText.textContent = '∞'
} else {
slot.ammoText.textContent = currentAmmo + '/' + WEAPON_CONFIG[weaponKey].maxAmmo
}
}
}
updateGrenadeCharge(percent) {
if (percent > 0) {
this.grenadeChargeBar.style.display = 'block'
const fill = this.grenadeChargeBar.querySelector('.hud-grenade-charge-fill')
fill.style.width = (percent * 100) + '%'
} else {
this.grenadeChargeBar.style.display = 'none'
}
}
updateInfo(wave, score, time) {
this.waveText.textContent = 'Wave: ' + wave
this.scoreText.textContent = 'Score: ' + score
const mins = Math.floor(time / 60)
const secs = Math.floor(time % 60)
this.timeText.textContent = `Time: ${mins}:${secs.toString().padStart(2, '0')}`
}
addKillFeed(message) {
const entry = document.createElement('div')
entry.className = 'hud-kill-entry'
entry.textContent = message
this.killFeed.appendChild(entry)
setTimeout(() => {
if (entry.parentNode) entry.parentNode.removeChild(entry)
}, 4000)
while (this.killFeed.children.length > 5) {
this.killFeed.removeChild(this.killFeed.firstChild)
}
}
}