(() => { const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); canvas.style.display = "block"; canvas.width = window.innerWidth - 100; canvas.height = window.innerHeight - 200; const audio = new Audio('./zap.wav'); audio.preload = 'auto'; let audioLoaded = false; const getRandomColor = () => { const letters = '0123456789ABCDEF'; return Array.from({ length: 6 }, () => letters[Math.floor(Math.random() * 16)]).join(''); }; const createTargets = () => { const numTargets = 5; const minDistance = 100; const targets = Array.from({ length: numTargets }).reduce((acc) => { let isRound = Math.random() < 0.5; let sides = isRound ? 0 : Math.floor(Math.random() * 5) + 3; let radius = Math.random() * 50 + 20; let x, y, validPosition; do { validPosition = true; x = Math.random() * (canvas.width - 2 * radius - 120) + radius + 60; y = Math.random() * (canvas.height - 2 * radius - 120) + radius + 60; validPosition = !acc.some((otherTarget) => { const dx = otherTarget.x - x; const dy = otherTarget.y - y; const distance = Math.sqrt(dx * dx + dy * dy); return distance < minDistance; }); } while (!validPosition); let vx = (Math.random() - 0.5) * 5; let vy = (Math.random() - 0.5) * 5; let color = getRandomColor(); while (color === canvas.style.backgroundColor) { color = getRandomColor(); } acc.push({ x, y, vx, vy, radius, sides, color }); return acc; }, []); return targets; }; const drawScore = () => { ctx.font = '20px Arial'; ctx.fillStyle = 'white'; ctx.fillText('Score: ' + score, 10, 30); }; const drawHelp = () => { if (showHelp) { ctx.font = '20px Arial'; ctx.fillStyle = 'white'; ctx.fillText('Click to shoot. Press R to respawn targets. Press H to toggle help text.', 10, 50); } }; const drawLine = (line) => { if (line && Date.now() - line.time < 500) { ctx.beginPath(); ctx.moveTo(canvas.width, canvas.height); ctx.lineTo(line.endX, line.endY); ctx.strokeStyle = 'red'; ctx.lineWidth = 5; ctx.shadowColor = 'rgba(255, 255, 0, 0.75)'; ctx.shadowBlur = 50; ctx.shadowOffsetX = 0; ctx.shadowOffsetY = 0; ctx.stroke(); ctx.shadowColor = 'transparent'; ctx.shadowBlur = 0; } }; const detectCollision = (target, otherTarget) => { const dx = otherTarget.x - target.x; const dy = otherTarget.y - target.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance < target.radius + otherTarget.radius) { const normalX = dx / distance; const normalY = dy / distance; const tangentX = -normalY; const tangentY = normalX; const dotProductTarget = target.vx * normalX + target.vy * normalY; const dotProductOtherTarget = otherTarget.vx * normalX + otherTarget.vy * normalY; const targetVxAfterCollision = dotProductOtherTarget * normalX + dotProductTarget * tangentX; const targetVyAfterCollision = dotProductOtherTarget * normalY + dotProductTarget * tangentY; const otherTargetVxAfterCollision = dotProductTarget * normalX + dotProductOtherTarget * tangentX; const otherTargetVyAfterCollision = dotProductTarget * normalY + dotProductOtherTarget * tangentY; target.vx = targetVxAfterCollision; target.vy = targetVyAfterCollision; otherTarget.vx = otherTargetVxAfterCollision; otherTarget.vy = otherTargetVyAfterCollision; } }; const drawTargets = (targets) => { targets.forEach((target, index) => { target.x += target.vx; target.y += target.vy; if (target.x - target.radius < 0 || target.x + target.radius > canvas.width) { target.vx = -target.vx; } if (target.y - target.radius < 0 || target.y + target.radius > canvas.height) { target.vy = -target.vy; } targets.forEach((target, index) => { const otherTargets = targets.slice(index + 1); otherTargets.forEach((otherTarget) => { detectCollision(target, otherTarget); }); }); ctx.beginPath(); if (target.sides === 0) { ctx.arc(target.x, target.y, target.radius, 0, 2 * Math.PI); } else if (target.sides === 3) { ctx.moveTo(target.x + target.radius * Math.cos(0), target.y + target.radius * Math.sin(0)); for (let i = 1; i <= target.sides; i++) { ctx.lineTo(target.x + target.radius * Math.cos(i * 2 * Math.PI / target.sides), target.y + target.radius * Math.sin(i * 2 * Math.PI / target.sides)); } } else if (target.sides === 4) { ctx.rect(target.x - target.radius, target.y - target.radius, target.radius * 2, target.radius * 2); } else { ctx.moveTo(target.x + target.radius, target.y); for (let i = 1; i <= target.sides; i++) { ctx.lineTo(target.x + target.radius * Math.cos(i * 2 * Math.PI / target.sides), target.y + target.radius * Math.sin(i * 2 * Math.PI / target.sides)); } } ctx.fillStyle = target.color; ctx.fill(); }); }; let score = 0; let showHelp = true; let line = null; let targets = createTargets(); let mousePos = { x: 0, y: 0 }; let prevMousePos = { x: 0, y: 0 }; canvas.addEventListener('mousemove', (e) => { const rect = canvas.getBoundingClientRect(); prevMousePos.x = mousePos.x; prevMousePos.y = mousePos.y; mousePos.x = e.clientX - rect.left; mousePos.y = e.clientY - rect.top; }); const drawMouseCircle = () => { if (mousePos.x !== prevMousePos.x || mousePos.y !== prevMousePos.y) { ctx.clearRect(0, 0, canvas.width, canvas.height); drawScore(); drawHelp(); drawLine(line); drawTargets(targets); } ctx.beginPath(); ctx.arc(mousePos.x, mousePos.y, 10, 0, Math.PI * 2); ctx.fillStyle = 'white'; ctx.fill(); ctx.beginPath(); ctx.arc(mousePos.x, mousePos.y, 5, 0, Math.PI * 2); ctx.fillStyle = 'black'; ctx.fill(); }; const gameLoop = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); drawScore(); drawHelp(); drawLine(line); drawTargets(targets); drawMouseCircle(); requestAnimationFrame(gameLoop); }; audio.addEventListener('canplaythrough', function () { audioLoaded = true; }); audio.addEventListener('error', function () { console.error('Error loading audio file'); }); canvas.addEventListener('click', (e) => { const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; if (audioLoaded) { audio.play().catch((error) => { console.error('Error playing audio:', error); }); } line = { startX: 0, startY: canvas.height, endX: x, endY: y, time: Date.now() }; targets = targets.filter((target) => { const dx = target.x - x; const dy = target.y - y; if (dx * dx + dy * dy <= target.radius * target.radius) { score++; return false; } return true; }); }); window.addEventListener('resize', () => { canvas.width = window.innerWidth; canvas.height = window.innerHeight; }); window.addEventListener('keydown', (e) => { if (e.key === 'r' || e.key === 'R') { targets = createTargets(); } if (e.key === 'h' || e.key === 'H') { showHelp = !showHelp; } }); gameLoop(); })();