const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); const scoreElement = document.getElementById('score'); const livesElement = document.getElementById('lives'); // Game elements const player = { x: canvas.width / 2, y: canvas.height / 2, radius: 15, color: '#FF5733', speed: 5 }; const enemy = { x: 50, y: 50, radius: 18, color: '#DC143C', speed: 2.5, stolenScore: 0, alive: true, respawnTimer: 0 }; let angerMode = false; let angerCooldown = 0; let playerMoveCount = 0; let lastPlayerX = null; let lastPlayerY = null; const toppings = { beef: [], lettuce: [], cheese: [] }; let score = 0; let lives = 3; let gameRunning = true; const TOTAL_POINTS = 200; // 20 beef * 10 points each // Initialize game function init() { // Create toppings for (let i = 0; i < 20; i++) { toppings.beef.push(createTopping('beef')); toppings.lettuce.push(createTopping('lettuce')); if (i < 5) toppings.cheese.push(createTopping('cheese')); } // Event listeners window.addEventListener('keydown', movePlayer); // Start game loop gameLoop(); } function createTopping(type) { return { x: Math.random() * (canvas.width - 30) + 15, y: Math.random() * (canvas.height - 30) + 15, radius: 10, type: type, color: type === 'beef' ? '#8B4513' : type === 'lettuce' ? '#7CFC00' : '#FFD700' }; } function movePlayer(e) { if (!gameRunning) { // Allow reset with R or Space when game is over if (e.key === 'r' || e.key === 'R' || e.key === ' ') { resetGame(); } return; } // Activate anger mode with Shift if (e.key === 'Shift' && angerCooldown <= 0 && !angerMode) { angerMode = true; angerCooldown = 300; // 5 seconds at 60fps setTimeout(() => { angerMode = false; }, 2000); // Anger lasts 2 seconds } // Store position before move lastPlayerX = player.x; lastPlayerY = player.y; switch(e.key) { case 'ArrowUp': player.y -= player.speed; break; case 'ArrowDown': player.y += player.speed; break; case 'ArrowLeft': player.x -= player.speed; break; case 'ArrowRight': player.x += player.speed; break; } // Boundary check player.x = Math.max(player.radius, Math.min(canvas.width - player.radius, player.x)); player.y = Math.max(player.radius, Math.min(canvas.height - player.radius, player.y)); // Only count as a move if position actually changed if (player.x !== lastPlayerX || player.y !== lastPlayerY) { playerMoveCount++; } } function resetGame() { // Reset player player.x = canvas.width / 2; player.y = canvas.height / 2; // Reset enemy enemy.x = 50; enemy.y = 50; enemy.stolenScore = 0; enemy.alive = true; enemy.respawnTimer = 0; // Reset anger mode angerMode = false; angerCooldown = 0; // Reset game state score = 0; lives = 3; playerMoveCount = 0; lastPlayerX = null; lastPlayerY = null; gameRunning = true; // Reset toppings toppings.beef = []; toppings.lettuce = []; toppings.cheese = []; // Recreate toppings for (let i = 0; i < 20; i++) { toppings.beef.push(createTopping('beef')); toppings.lettuce.push(createTopping('lettuce')); if (i < 5) toppings.cheese.push(createTopping('cheese')); } // Update display scoreElement.textContent = score; livesElement.textContent = lives; // Restart loop gameLoop(); } function moveEnemy() { if (!gameRunning || !enemy.alive) { // Handle respawn if (!enemy.alive) { enemy.respawnTimer--; if (enemy.respawnTimer <= 0) { enemy.alive = true; enemy.x = 50; enemy.y = 50; } } return; } // Enemy only moves once for every 2 player moves if (playerMoveCount % 2 !== 0) return; // Calculate direction to player const dx = player.x - enemy.x; const dy = player.y - enemy.y; const distance = Math.hypot(dx, dy); if (distance > 0) { // Normalize and move towards player enemy.x += (dx / distance) * enemy.speed; enemy.y += (dy / distance) * enemy.speed; } // Boundary check enemy.x = Math.max(enemy.radius, Math.min(canvas.width - enemy.radius, enemy.x)); enemy.y = Math.max(enemy.radius, Math.min(canvas.height - enemy.radius, enemy.y)); } function checkCollision() { // Check beef collisions for (let i = 0; i < toppings.beef.length; i++) { const beef = toppings.beef[i]; const dist = Math.hypot(player.x - beef.x, player.y - beef.y); if (dist < player.radius + beef.radius) { toppings.beef.splice(i, 1); score += 10; scoreElement.textContent = score; break; } } // Check lettuce collisions for (let i = 0; i < toppings.lettuce.length; i++) { const lettuce = toppings.lettuce[i]; const dist = Math.hypot(player.x - lettuce.x, player.y - lettuce.y); if (dist < player.radius + lettuce.radius) { toppings.lettuce.splice(i, 1); score = Math.max(0, score - 5); scoreElement.textContent = score; break; } } // Check cheese collisions for (let i = 0; i < toppings.cheese.length; i++) { const cheese = toppings.cheese[i]; const dist = Math.hypot(player.x - cheese.x, player.y - cheese.y); if (dist < player.radius + cheese.radius) { toppings.cheese.splice(i, 1); lives++; livesElement.textContent = lives; break; } } // Check enemy collision with player if (enemy.alive) { const enemyDist = Math.hypot(player.x - enemy.x, player.y - enemy.y); if (enemyDist < player.radius + enemy.radius) { if (angerMode) { // Player kills enemy with anger! enemy.alive = false; enemy.respawnTimer = 180; // Respawn after 3 seconds score += 25; // Bonus for killing enemy scoreElement.textContent = score; // Visual feedback ctx.fillStyle = 'white'; ctx.font = 'bold 30px Arial'; ctx.textAlign = 'center'; ctx.fillText('💥 RAGE KILL! 💥', canvas.width/2, canvas.height/2); } else { // Enemy steals points from player if (score > 0) { const stealAmount = Math.min(10, score); // Steal up to 10 points at a time score -= stealAmount; enemy.stolenScore += stealAmount; scoreElement.textContent = score; // Push player away from enemy const pushAngle = Math.atan2(player.y - enemy.y, player.x - enemy.x); player.x += Math.cos(pushAngle) * 30; player.y += Math.sin(pushAngle) * 30; // Boundary check after push player.x = Math.max(player.radius, Math.min(canvas.width - player.radius, player.x)); player.y = Math.max(player.radius, Math.min(canvas.height - player.radius, player.y)); } } } } // Check win/lose conditions if (toppings.beef.length === 0) { gameRunning = false; alert('You won! Final score: ' + score + '\nEnemy stole: ' + enemy.stolenScore + ' points\nPress R or Space to restart'); } else if (enemy.stolenScore >= TOTAL_POINTS) { gameRunning = false; alert('The Red Devourer wins!\nIt stole all ' + enemy.stolenScore + ' points from you!\nYour final score: ' + score + '\nPress R or Space to restart'); } else if (lives <= 0) { gameRunning = false; alert('Game over! Final score: ' + score + '\nPress R or Space to restart'); } } function draw() { // Clear canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw player (glows red when angry) ctx.beginPath(); ctx.arc(player.x, player.y, player.radius, 0, Math.PI * 2); if (angerMode) { // Pulsing red glow const pulse = Math.sin(Date.now() / 100) * 5 + 10; ctx.shadowBlur = 20; ctx.shadowColor = '#FF0000'; ctx.fillStyle = '#FF0000'; } else { ctx.shadowBlur = 0; ctx.fillStyle = player.color; } ctx.fill(); ctx.shadowBlur = 0; // Reset shadow // Draw anger meter/cooldown if (angerCooldown > 0) { angerCooldown--; ctx.fillStyle = 'rgba(255, 0, 0, 0.3)'; ctx.fillRect(10, 70, 200, 20); ctx.fillStyle = angerCooldown > 0 ? '#FF0000' : '#00FF00'; const cooldownWidth = (1 - angerCooldown / 300) * 200; ctx.fillRect(10, 70, cooldownWidth, 20); ctx.strokeStyle = '#8B4513'; ctx.lineWidth = 2; ctx.strokeRect(10, 70, 200, 20); ctx.fillStyle = '#8B4513'; ctx.font = 'bold 14px Arial'; ctx.textAlign = 'left'; ctx.fillText(angerMode ? '🔥 RAGE ACTIVE! 🔥' : (angerCooldown <= 0 ? 'SHIFT: ACTIVATE RAGE' : 'RAGE COOLDOWN'), 15, 85); } else { ctx.fillStyle = '#00AA00'; ctx.font = 'bold 14px Arial'; ctx.textAlign = 'left'; ctx.fillText('✓ SHIFT: ACTIVATE RAGE MODE', 15, 85); } // Draw enemy if alive if (enemy.alive) { ctx.beginPath(); ctx.arc(enemy.x, enemy.y, enemy.radius, 0, Math.PI * 2); ctx.fillStyle = angerMode ? '#FF0000' : enemy.color; ctx.fill(); // Draw enemy eyes (angry when player is in anger mode) ctx.fillStyle = angerMode ? '#FF0000' : 'white'; ctx.beginPath(); ctx.arc(enemy.x - 6, enemy.y - 4, 4, 0, Math.PI * 2); ctx.arc(enemy.x + 6, enemy.y - 4, 4, 0, Math.PI * 2); ctx.fill(); ctx.fillStyle = angerMode ? '#000' : 'black'; ctx.beginPath(); ctx.arc(enemy.x - 6, enemy.y - 4, 2, 0, Math.PI * 2); ctx.arc(enemy.x + 6, enemy.y - 4, 2, 0, Math.PI * 2); ctx.fill(); // Draw scared eyebrows when player is angry if (angerMode) { ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(enemy.x - 10, enemy.y - 8); ctx.lineTo(enemy.x - 2, enemy.y - 6); ctx.moveTo(enemy.x + 10, enemy.y - 8); ctx.lineTo(enemy.x + 2, enemy.y - 6); ctx.stroke(); } } else { // Draw skull when enemy is dead ctx.fillStyle = 'rgba(100, 100, 100, 0.5)'; ctx.font = '30px Arial'; ctx.textAlign = 'center'; ctx.fillText('💀', enemy.x, enemy.y + 10); // Respawn countdown ctx.fillStyle = '#DC143C'; ctx.font = 'bold 16px Arial'; ctx.fillText('RESPAWN: ' + Math.ceil(enemy.respawnTimer/60), enemy.x, enemy.y - 25); } // Draw toppings drawToppings(toppings.beef); drawToppings(toppings.lettuce); drawToppings(toppings.cheese); // Draw enemy threat meter ctx.fillStyle = 'rgba(220, 20, 60, 0.3)'; ctx.fillRect(canvas.width - 210, 10, 200, 20); ctx.fillStyle = '#DC143C'; const threatWidth = (enemy.stolenScore / TOTAL_POINTS) * 200; ctx.fillRect(canvas.width - 210, 10, threatWidth, 20); ctx.strokeStyle = '#8B4513'; ctx.lineWidth = 2; ctx.strokeRect(canvas.width - 210, 10, 200, 20); ctx.fillStyle = '#8B4513'; ctx.font = 'bold 14px Arial'; ctx.textAlign = 'center'; ctx.fillText('ENEMY THREAT: ' + enemy.stolenScore + '/' + TOTAL_POINTS, canvas.width - 110, 25); } function drawToppings(toppingArray) { toppingArray.forEach(topping => { ctx.beginPath(); ctx.arc(topping.x, topping.y, topping.radius, 0, Math.PI * 2); ctx.fillStyle = topping.color; ctx.fill(); }); } function gameLoop() { if (!gameRunning) return; moveEnemy(); draw(); checkCollision(); requestAnimationFrame(gameLoop); } // Start the game init();