burrito-runner / game.js
CommanderLazarus's picture
🐳 13/02 - 02:47 - Activate enemy mode, where anger will kill the red devourer.
dcf5681 verified
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();