const $ = (s) => document.querySelector(s); const $$ = (s) => Array.from(document.querySelectorAll(s)); const ROOT = $('#root'); const X_BOUND = 0.05; const clouds = new Set(); const bullets = new Set(); let shootingPad = new ShootingPad(); let running = true; let direction = 1; const initCloudField = () => { for (let y = 0; y < 6; y++) { for (let x = 0; x < 11; x++) { const cloud = new Cloud(X_BOUND + 0.06 * x, 0.04 + 0.04 * y, 0.03, 0.02); clouds.add(cloud); } } } const start = () => { $('.result')?.remove(); shootingPad?.destroy(); shootingPad = new ShootingPad(); bullets.forEach(c => c.destroy()); bullets.clear(); clouds.forEach(c => c.destroy()); clouds.clear(); Cloud.reset(); initCloudField(); running = true; loop(); } const gameOver = (result) => { const resultLabel = document.createElement('div'); resultLabel.className = 'result'; resultLabel.textContent = result; ROOT.appendChild(resultLabel); running = false; } const loop = () => { if (!running) return; let directionUpdated = false; let isOver = false; Array.from(clouds).forEach(c => { c.update(direction); c.draw(); if (c.x < X_BOUND - 0.01 || c.x > 1.01 - X_BOUND) { directionUpdated = true; } if (c.y >= 0.95) { isOver = true; } if (!c.alive) { clouds.delete(c); } }); if (directionUpdated) { direction = -direction; } if (isOver) { gameOver('Game over'); } Array.from(bullets).forEach(b => { b.update(); b.draw(); if (!b.alive) { bullets.delete(b); } }); shootingPad.update(); shootingPad.draw(); if (clouds.size === 0) { gameOver('WIN'); } requestAnimationFrame(loop); }; document.addEventListener('keypress', () => { if (!running) { start(); } }); start();