(function () {
function startWhenReady() {
const canvas = document.getElementById("game");
if (!canvas) {
setTimeout(startWhenReady, 200);
return;
}
const ctx = canvas.getContext("2d");
const W = canvas.width, H = canvas.height;
let score = 0, hp = 3, gameOver = false;
const keys = Object.create(null);
let bullets = [], enemies = [];
const player = { x: W / 2 - 20, y: H - 60, w: 40, h: 40, speed: 6, cooldown: 0 };
window.addEventListener("keydown", (e) => (keys[e.key] = true));
window.addEventListener("keyup", (e) => (keys[e.key] = false));
function overlap(a, b) {
return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
}
function shoot() {
if (player.cooldown <= 0) {
bullets.push({ x: player.x + player.w / 2 - 3, y: player.y, w: 6, h: 15 });
player.cooldown = 15;
}
}
function spawnEnemy() {
enemies.push({ x: Math.random() * (W - 40), y: -40, w: 40, h: 40, speed: 2 + Math.random() * 2 });
}
function update() {
if (gameOver) return;
if (keys["ArrowLeft"] || keys["a"] || keys["A"]) player.x -= player.speed;
if (keys["ArrowRight"] || keys["d"] || keys["D"]) player.x += player.speed;
if (keys[" "]) shoot();
player.x = Math.max(0, Math.min(W - player.w, player.x));
player.cooldown = Math.max(0, player.cooldown - 1);
bullets.forEach((b) => (b.y -= 8));
bullets = bullets.filter((b) => b.y > -20);
enemies.forEach((e) => (e.y += e.speed));
for (let ei = enemies.length - 1; ei >= 0; ei--) {
const e = enemies[ei];
for (let bi = bullets.length - 1; bi >= 0; bi--) {
const b = bullets[bi];
if (overlap(b, e)) {
enemies.splice(ei, 1);
bullets.splice(bi, 1);
score += 100;
break;
}
}
if (overlap(player, e)) {
enemies.splice(ei, 1);
hp -= 1;
if (hp <= 0) gameOver = true;
}
}
enemies = enemies.filter((e) => e.y < H + 50);
if (Math.random() < 0.02) spawnEnemy();
}
function draw() {
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, W, H);
ctx.fillStyle = "cyan";
ctx.beginPath();
ctx.moveTo(player.x + player.w / 2, player.y);
ctx.lineTo(player.x + player.w, player.y + player.h);
ctx.lineTo(player.x, player.y + player.h);
ctx.closePath();
ctx.fill();
ctx.fillStyle = "yellow";
bullets.forEach((b) => ctx.fillRect(b.x, b.y, b.w, b.h));
ctx.fillStyle = "red";
enemies.forEach((e) => ctx.fillRect(e.x, e.y, e.w, e.h));
ctx.fillStyle = "#fff";
ctx.font = "20px Arial";
ctx.fillText("Score: " + score, 20, 30);
ctx.fillText("HP: " + hp, 20, 60);
if (gameOver) {
ctx.font = "40px Arial";
ctx.fillText("GAME OVER", W / 2 - 120, H / 2);
}
}
function loop() {
update();
draw();
requestAnimationFrame(loop);
}
loop();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", startWhenReady);
} else {
startWhenReady();
}
})();