104 lines
4.2 KiB
JavaScript
104 lines
4.2 KiB
JavaScript
const PAD = 14;
|
|
const HP_W = 180;
|
|
const HP_H = 16;
|
|
const XP_W = 180;
|
|
const XP_H = 10;
|
|
const LIFE_R = 8;
|
|
const LIFE_GAP = 22;
|
|
|
|
export class HUD {
|
|
constructor(scene, player, xpSystem) {
|
|
this.scene = scene;
|
|
this.player = player;
|
|
this.xpSystem = xpSystem;
|
|
|
|
// Fix to camera
|
|
const cam = scene.cameras.main;
|
|
const cx = cam.x;
|
|
const cy = cam.y;
|
|
|
|
// --- HP Bar ---
|
|
this._hpLabel = scene.add.text(PAD, PAD, 'HP', { fontSize: '13px', fill: '#ffffff' }).setScrollFactor(0).setDepth(10);
|
|
this._hpBg = scene.add.rectangle(PAD + HP_W / 2, PAD + 8, HP_W, HP_H, 0x333333).setScrollFactor(0).setDepth(10);
|
|
this._hpFill = scene.add.rectangle(PAD + HP_W / 2, PAD + 8, HP_W, HP_H, 0xff3333).setScrollFactor(0).setDepth(10);
|
|
this._hpLabel.setY(PAD - 2);
|
|
this._hpBg.setY(PAD + 20);
|
|
this._hpFill.setY(PAD + 20);
|
|
|
|
// --- Life icons ---
|
|
this._lifeIcons = [];
|
|
for (let i = 0; i < 3; i++) {
|
|
const lx = PAD + LIFE_R + i * LIFE_GAP;
|
|
const icon = scene.add.circle(lx, PAD + 48, LIFE_R, 0x00ccff).setScrollFactor(0).setDepth(10);
|
|
this._lifeIcons.push(icon);
|
|
}
|
|
|
|
// --- XP Bar ---
|
|
const xpY = PAD + 64;
|
|
this._xpBg = scene.add.rectangle(PAD + XP_W / 2, xpY, XP_W, XP_H, 0x333333).setScrollFactor(0).setDepth(10);
|
|
this._xpFill = scene.add.rectangle(PAD + XP_W / 2, xpY, XP_W, XP_H, 0x44ffaa).setScrollFactor(0).setDepth(10);
|
|
this._xpLabel = scene.add.text(PAD, xpY + 8, 'Level 1', { fontSize: '12px', fill: '#aaffcc' }).setScrollFactor(0).setDepth(10);
|
|
|
|
// --- Weapon timer bar (top-center, hidden by default) ---
|
|
const W = scene.scale.width;
|
|
this._weaponBarBg = scene.add.rectangle(W / 2, 12, 200, 14, 0x333333).setScrollFactor(0).setDepth(10).setVisible(false);
|
|
this._weaponBarFill = scene.add.rectangle(W / 2 - 100, 12, 0, 14, 0xff9900).setScrollFactor(0).setDepth(11).setOrigin(0, 0.5).setVisible(false);
|
|
this._weaponLabel = scene.add.text(W / 2, 23, '', { fontSize: '11px', fill: '#ffffff' }).setScrollFactor(0).setDepth(10).setOrigin(0.5, 0).setVisible(false);
|
|
|
|
// --- Zone/Wave indicator ---
|
|
this._zoneText = scene.add.text(0, PAD, 'Zone 1 — Wave 1/3', { fontSize: '14px', fill: '#ffffff' })
|
|
.setScrollFactor(0).setDepth(10).setOrigin(1, 0);
|
|
this._zoneText.setX(scene.scale.width - PAD);
|
|
|
|
// Listen for wave/zone updates
|
|
scene.events.on('wave-start', ({ zone, wave, totalWaves }) => {
|
|
this._zoneText.setText(`Zone ${zone} — Wave ${wave}/${totalWaves}`);
|
|
});
|
|
scene.events.on('zone-clear', ({ zone }) => {
|
|
this._zoneText.setText(`Zone ${zone} Complete!`);
|
|
});
|
|
}
|
|
|
|
update() {
|
|
// HP bar
|
|
const hpRatio = Math.max(0, this.player.hp / this.player.stats.maxHp);
|
|
this._hpFill.width = HP_W * hpRatio;
|
|
this._hpFill.x = PAD + (HP_W * hpRatio) / 2;
|
|
|
|
// Lives
|
|
this._lifeIcons.forEach((icon, i) => {
|
|
icon.setAlpha(i < this.player.lives ? 1 : 0.2);
|
|
});
|
|
|
|
// XP bar
|
|
const xpRatio = this.xpSystem.progress;
|
|
this._xpFill.width = XP_W * xpRatio;
|
|
this._xpFill.x = PAD + (XP_W * xpRatio) / 2;
|
|
this._xpLabel.setText(`Level ${this.xpSystem.level}`);
|
|
|
|
// Weapon timer bar
|
|
const hasWeapon = this.player.weaponMode !== 'default';
|
|
this._weaponBarBg.setVisible(hasWeapon);
|
|
this._weaponBarFill.setVisible(hasWeapon);
|
|
this._weaponLabel.setVisible(hasWeapon);
|
|
if (hasWeapon) {
|
|
const ratio = Math.max(0, this.player.weaponTimeLeft / this.player.weaponDuration);
|
|
this._weaponBarFill.width = 200 * ratio;
|
|
const colorMap = { shotgun: 0xff9900, rocket: 0xff3300, fourway: 0x00ffff, stimulant: 0xaa44ff };
|
|
const labelMap = { shotgun: 'SHOTGUN', rocket: 'ROCKET LAUNCHER', fourway: 'FOUR-WAY CANNON', stimulant: 'STIMULANT' };
|
|
this._weaponBarFill.setFillStyle(colorMap[this.player.weaponMode] ?? 0xffffff);
|
|
this._weaponLabel.setText(labelMap[this.player.weaponMode] ?? '');
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
this.scene.events.off('wave-start');
|
|
this.scene.events.off('zone-clear');
|
|
[this._hpLabel, this._hpBg, this._hpFill,
|
|
this._xpBg, this._xpFill, this._xpLabel,
|
|
this._weaponBarBg, this._weaponBarFill, this._weaponLabel,
|
|
this._zoneText, ...this._lifeIcons]
|
|
.forEach(o => o?.destroy());
|
|
}
|
|
}
|