// Game initialization document.addEventListener('DOMContentLoaded', () => { // Initialize game const game = new WormateGame(); game.init(); // Initialize Feather Icons if (typeof feather !== 'undefined') { feather.replace(); } }); class WormateGame { constructor() { this.canvas = document.getElementById('gameCanvas'); this.ctx = this.canvas.getContext('2d'); this.scoreDisplay = document.getElementById('score-display'); this.leaderboard = document.getElementById('leaderboard'); this.playBtn = document.getElementById('play-btn'); this.timeDisplay = document.getElementById('time-display'); this.gameStarted = false; this.playerId = null; this.players = {}; this.foods = []; this.powerUps = []; this.score = 0; this.timeLeft = 60; // Starting time (seconds) this.maxTime = 120; // Maximum time bank this.timeDecayRate = 1.0; // Base decay rate (seconds per second) this.timeDecayMultiplier = 0.01; // Increases as worm grows this.gameTimer = null; this.keys = { ArrowUp: false, ArrowDown: false, ArrowLeft: false, ArrowRight: false }; } init() { this.resizeCanvas(); window.addEventListener('resize', () => this.resizeCanvas()); // Keyboard controls window.addEventListener('keydown', (e) => { if (this.keys.hasOwnProperty(e.key)) { this.keys[e.key] = true; e.preventDefault(); } }); window.addEventListener('keyup', (e) => { if (this.keys.hasOwnProperty(e.key)) { this.keys[e.key] = false; e.preventDefault(); } }); // Start game button this.playBtn.addEventListener('click', () => this.startGame()); // Start game loop this.gameLoop(); } resizeCanvas() { this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; } startGame() { const playerName = prompt('Enter your name:', 'Player' + Math.floor(Math.random() * 1000)); if (playerName) { this.playerId = 'local-' + Date.now(); this.players[this.playerId] = { id: this.playerId, name: playerName, color: this.getRandomColor(), score: 0, segments: [ { x: this.canvas.width/2, y: this.canvas.height/2, radius: 15 }, { x: this.canvas.width/2 - 20, y: this.canvas.height/2, radius: 14 }, { x: this.canvas.width/2 - 40, y: this.canvas.height/2, radius: 13 } ] }; // Generate initial food for (let i = 0; i < 50; i++) { const foodTypes = [ { type: 'lollipop', radius: 12, color: '#FF5252', time: 2, rarity: 0.5 }, { type: 'gummybear', radius: 10, color: '#FF4081', time: 2, rarity: 0.5 }, { type: 'candycorn', radius: 8, color: '#FFD740', time: 2, rarity: 0.5 }, { type: 'chocolate', radius: 14, color: '#7B3F00', time: 2, rarity: 0.1 }, { type: 'marshmallow', radius: 9, color: '#FFFFFF', time: 2, rarity: 0.2 } ]; const randomFood = foodTypes[Math.floor(Math.random() * foodTypes.length)]; this.foods.push({ x: Math.random() * this.canvas.width, y: Math.random() * this.canvas.height, radius: randomFood.radius, color: randomFood.color, type: randomFood.type, points: randomFood.points, time: 2 // Fixed 2 seconds for all food types , time: 2 // Fixed 2 seconds for all food types }); } this.gameStarted = true; this.playBtn.style.display = 'none'; this.timeLeft = 30; // Reset time this.timeDisplay.textContent = `Time: ${this.timeLeft}s`; this.startTimer(); } } gameLoop() { if (this.gameStarted) { this.update(); this.render(); } requestAnimationFrame(() => this.gameLoop()); } startTimer() { clearInterval(this.gameTimer); let lastTime = Date.now(); this.gameTimer = setInterval(() => { const now = Date.now(); const delta = (now - lastTime) / 1000; // Convert to seconds lastTime = now; // Calculate time decay based on worm size const decayRate = this.timeDecayRate + (this.timeDecayMultiplier * this.players[this.playerId]?.segments.length || 0); this.timeLeft = Math.max(0, this.timeLeft - (decayRate * delta)); this.timeDisplay.textContent = `Time: ${Math.floor(this.timeLeft)}s`; if (this.timeLeft <= 0) { this.gameOver(); } // Visual feedback for time status if (this.timeLeft <= 10) { this.timeDisplay.classList.add('warning'); this.timeDisplay.style.animationDuration = `${0.5 / (11 - this.timeLeft)}s`; } else if (this.timeLeft <= 30) { this.timeDisplay.classList.add('warning'); this.timeDisplay.style.animationDuration = '1s'; } else { this.timeDisplay.classList.remove('warning'); } }, 1000); } update() { // Move player worm const player = this.players[this.playerId]; if (!player) return; const head = player.segments[0]; let newX = head.x; let newY = head.y; if (this.keys.ArrowUp) newY -= 5; if (this.keys.ArrowDown) newY += 5; if (this.keys.ArrowLeft) newX -= 5; if (this.keys.ArrowRight) newX += 5; // Add new head position player.segments.unshift({ x: newX, y: newY, radius: head.radius }); // Remove tail segment player.segments.pop(); // Check food collision this.foods = this.foods.filter(food => { const distance = Math.sqrt( Math.pow(head.x - food.x, 2) + Math.pow(head.y - food.y, 2) ); if (distance < head.radius + food.radius) { // Eat food - grow worm const tail = player.segments[player.segments.length - 1]; for (let i = 0; i < 5; i++) { player.segments.push({ x: tail.x, y: tail.y, radius: tail.radius * 0.95 }); } // Add time for eating food with diminishing returns based on worm size const timeGain = Math.max(1, food.time - (player.segments.length * 0.1)); this.timeLeft = Math.min(this.maxTime, this.timeLeft + timeGain); // Increase time decay as worm grows (harder to maintain) this.timeDecayMultiplier = 0.01 + (player.segments.length * 0.0005); player.score += 1; this.score = player.score; this.scoreDisplay.textContent = `Score: ${this.score}`; this.timeDisplay.textContent = `Time: ${this.timeLeft}s`; return false; // Remove food } return true; // Keep food }); // Add new food if needed if (this.foods.length < 30 && Math.random() < 0.1) { const foodTypes = [ { type: 'lollipop', radius: 12, color: '#FF5252', points: 10, time: 2 }, { type: 'gummybear', radius: 10, color: '#FF4081', points: 5, time: 2 }, { type: 'candycorn', radius: 8, color: '#FFD740', points: 3, time: 2 }, { type: 'chocolate', radius: 14, color: '#7B3F00', points: 15, time: 2 }, { type: 'marshmallow', radius: 9, color: '#FFFFFF', points: 8, time: 2 } ]; const randomFood = foodTypes[Math.floor(Math.random() * foodTypes.length)]; this.foods.push({ x: Math.random() * this.canvas.width, y: Math.random() * this.canvas.height, radius: randomFood.radius, color: randomFood.color, type: randomFood.type, points: randomFood.points }); } // Check wall collision if ( head.x < -head.radius || head.x > this.canvas.width + head.radius || head.y < -head.radius || head.y > this.canvas.height + head.radius ) { this.gameOver(); } // Update leaderboard this.updateLeaderboard(); } render() { // Clear canvas this.ctx.fillStyle = '#1a1a2e'; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); // Draw foods this.foods.forEach(food => { this.ctx.save(); this.ctx.translate(food.x, food.y); switch(food.type) { case 'lollipop': // Lollipop head this.ctx.fillStyle = food.color; this.ctx.beginPath(); this.ctx.arc(0, 0, food.radius, 0, Math.PI * 2); this.ctx.fill(); // Lollipop stick this.ctx.strokeStyle = '#FFFFFF'; this.ctx.lineWidth = 2; this.ctx.beginPath(); this.ctx.moveTo(0, food.radius); this.ctx.lineTo(0, food.radius * 2); this.ctx.stroke(); break; case 'gummybear': // Gummy bear body this.ctx.fillStyle = food.color; this.ctx.beginPath(); this.ctx.ellipse(0, 0, food.radius, food.radius * 1.2, 0, 0, Math.PI * 2); this.ctx.fill(); // Eyes this.ctx.fillStyle = '#000000'; this.ctx.beginPath(); this.ctx.arc(-food.radius/3, -food.radius/3, 2, 0, Math.PI * 2); this.ctx.arc(food.radius/3, -food.radius/3, 2, 0, Math.PI * 2); this.ctx.fill(); break; case 'candycorn': // Candy corn triangle this.ctx.fillStyle = food.color; this.ctx.beginPath(); this.ctx.moveTo(0, -food.radius); this.ctx.lineTo(-food.radius, food.radius); this.ctx.lineTo(food.radius, food.radius); this.ctx.closePath(); this.ctx.fill(); break; case 'chocolate': // Chocolate bar this.ctx.fillStyle = food.color; this.ctx.fillRect(-food.radius, -food.radius/2, food.radius*2, food.radius); break; case 'marshmallow': // Marshmallow this.ctx.fillStyle = food.color; this.ctx.beginPath(); this.ctx.arc(0, 0, food.radius, 0, Math.PI * 2); this.ctx.fill(); // Shading this.ctx.fillStyle = 'rgba(0,0,0,0.1)'; this.ctx.beginPath(); this.ctx.arc(0, -food.radius/3, food.radius/3, 0, Math.PI * 2); this.ctx.fill(); break; } this.ctx.restore(); }); // Draw players Object.values(this.players).forEach(player => { // Draw worm segments player.segments.forEach((segment, i) => { const gradient = this.ctx.createRadialGradient( segment.x, segment.y, 0, segment.x, segment.y, segment.radius ); gradient.addColorStop(0, player.color); gradient.addColorStop(1, this.darkenColor(player.color, 0.3)); this.ctx.fillStyle = gradient; this.ctx.beginPath(); this.ctx.arc(segment.x, segment.y, segment.radius, 0, Math.PI * 2); this.ctx.fill(); // Draw eyes on head if (i === 0 && player.segments.length > 1) { const angle = Math.atan2( player.segments[1].y - segment.y, player.segments[1].x - segment.x ); const eyeRadius = segment.radius * 0.3; const eyeOffset = segment.radius * 0.6; // Eyes this.ctx.fillStyle = 'white'; this.ctx.beginPath(); this.ctx.arc( segment.x + Math.cos(angle + Math.PI/2) * eyeOffset, segment.y + Math.sin(angle + Math.PI/2) * eyeOffset, eyeRadius, 0, Math.PI * 2 ); this.ctx.fill(); this.ctx.beginPath(); this.ctx.arc( segment.x + Math.cos(angle - Math.PI/2) * eyeOffset, segment.y + Math.sin(angle - Math.PI/2) * eyeOffset, eyeRadius, 0, Math.PI * 2 ); this.ctx.fill(); // Pupils this.ctx.fillStyle = 'black'; this.ctx.beginPath(); this.ctx.arc( segment.x + Math.cos(angle + Math.PI/2) * eyeOffset + Math.cos(angle) * eyeRadius/2, segment.y + Math.sin(angle + Math.PI/2) * eyeOffset + Math.sin(angle) * eyeRadius/2, eyeRadius/2, 0, Math.PI * 2 ); this.ctx.fill(); this.ctx.beginPath(); this.ctx.arc( segment.x + Math.cos(angle - Math.PI/2) * eyeOffset + Math.cos(angle) * eyeRadius/2, segment.y + Math.sin(angle - Math.PI/2) * eyeOffset + Math.sin(angle) * eyeRadius/2, eyeRadius/2, 0, Math.PI * 2 ); this.ctx.fill(); } }); // Draw player name if (player.segments.length > 0) { const head = player.segments[0]; this.ctx.fillStyle = 'white'; this.ctx.font = '12px "Press Start 2P", cursive'; this.ctx.textAlign = 'center'; this.ctx.fillText(player.name, head.x, head.y - head.radius - 10); } }); } updateLeaderboard() { const sortedPlayers = Object.values(this.players).sort((a, b) => b.score - a.score); this.leaderboard.innerHTML = '