const keys = {}; document.addEventListener('keydown', (e) => { keys[e.key] = true; }); document.addEventListener('keyup', (e) => { keys[e.key] = false; }); let blink = false; setInterval(() => blink = !blink, 200); const MAX_LIVES = 3; const INVULNERABILITY_INTERVAL = 2000; class ShootingPad extends Entity { constructor() { super(0.5, 0.95, 0.04, 0.03); const image = new Image(); image.src = 'img/shooting-pad.svg'; image.onload = () => this.image = image; this.shootSound = new Audio(); this.shootSound.src = 'snd/shoot.ogg'; this.hitSound = new Audio(); this.hitSound.src = 'snd/hit.ogg'; this.lastShootTime = Date.now(); this.invulnerable = false; this.lives = MAX_LIVES; this.initLives(); } update() { if (keys['ArrowLeft'] || keys['a']) { this.x -= VELOCITY[0] * 2; } if (keys['ArrowRight'] || keys['d']) { this.x += VELOCITY[0] * 2; } if (this.x < X_BOUND) { this.x = X_BOUND; } if (this.x > 1 - X_BOUND) { this.x = 1 - X_BOUND; } if (keys[' '] && Date.now() - this.lastShootTime >= 300) { this.lastShootTime = Date.now(); const bullet = new Bullet(this.x, this.y - 0.009); bullets.add(bullet); if (soundEnabled) { this.shootSound.currentTime = 0; this.shootSound.play().catch(() => { }); } } } draw() { if (!this.invulnerable || blink) { super.draw(); } } initLives() { LIVES_LABEL.textContent = `× ${this.lives}`; } lifeOver() { if (this.invulnerable) return; this.lives--; this.initLives(); if (soundEnabled) { this.hitSound.currentTime = 0; this.hitSound.play().catch(() => { }); } if (this.lives === 0) { return gameOver('Game over'); } else { this.invulnerable = true; setTimeout(() => { this.invulnerable = false; }, INVULNERABILITY_INTERVAL); } } }