Spaces:
Running
Running
| // Wormate.io clone game logic | |
| const socket = io('https://wormate.io'); // Connect to wormate.io server | |
| const canvas = document.getElementById('gameCanvas'); | |
| const ctx = canvas.getContext('2d'); | |
| const scoreDisplay = document.getElementById('score-display'); | |
| const leaderboard = document.getElementById('leaderboard'); | |
| const playBtn = document.getElementById('play-btn'); | |
| // Set canvas to full window size | |
| function resizeCanvas() { | |
| canvas.width = window.innerWidth; | |
| canvas.height = window.innerHeight; | |
| } | |
| window.addEventListener('resize', resizeCanvas); | |
| resizeCanvas(); | |
| // Game state | |
| let gameStarted = false; | |
| let playerId = null; | |
| let players = {}; | |
| let foods = [ | |
| { | |
| x: Math.random() * canvas.width, | |
| y: Math.random() * canvas.height, | |
| radius: 10, | |
| color: getRandomColor() | |
| } | |
| ]; | |
| let powerUps = []; | |
| let score = 0; | |
| // Player controls | |
| const keys = { | |
| ArrowUp: false, | |
| ArrowDown: false, | |
| ArrowLeft: false, | |
| ArrowRight: false | |
| }; | |
| // Handle keyboard input | |
| window.addEventListener('keydown', (e) => { | |
| if (keys.hasOwnProperty(e.key)) { | |
| keys[e.key] = true; | |
| e.preventDefault(); | |
| } | |
| }); | |
| window.addEventListener('keyup', (e) => { | |
| if (keys.hasOwnProperty(e.key)) { | |
| keys[e.key] = false; | |
| e.preventDefault(); | |
| } | |
| }); | |
| // Socket event handlers | |
| socket.on('connect', () => { | |
| playerId = socket.id; | |
| console.log('Connected to server with ID:', playerId); | |
| }); | |
| socket.on('gameState', (gameState) => { | |
| players = gameState.players; | |
| foods = gameState.foods; | |
| powerUps = gameState.powerUps; | |
| if (players[playerId]) { | |
| score = players[playerId].score; | |
| scoreDisplay.textContent = `Score: ${score}`; | |
| } | |
| updateLeaderboard(); | |
| renderGame(); | |
| }); | |
| socket.on('playerDied', (data) => { | |
| if (data.playerId === playerId) { | |
| gameStarted = false; | |
| playBtn.style.display = 'block'; | |
| alert(`Game Over! Your score: ${score}`); | |
| } | |
| }); | |
| // Game loop | |
| function gameLoop() { | |
| if (gameStarted) { | |
| const direction = getDirection(); | |
| socket.emit('playerInput', direction); | |
| } | |
| requestAnimationFrame(gameLoop); | |
| } | |
| gameLoop(); | |
| // Helper functions | |
| function getDirection() { | |
| if (keys.ArrowUp) return 'up'; | |
| if (keys.ArrowDown) return 'down'; | |
| if (keys.ArrowLeft) return 'left'; | |
| if (keys.ArrowRight) return 'right'; | |
| return null; | |
| } | |
| function updateLeaderboard() { | |
| const sortedPlayers = Object.values(players).sort((a, b) => b.score - a.score); | |
| leaderboard.innerHTML = '<h3>Leaderboard</h3>' + | |
| sortedPlayers.map(p => | |
| `<div class="player-row ${p.id === playerId ? 'you' : ''}"> | |
| <span class="player-name">${p.name}</span> | |
| <span class="player-score">${p.score}</span> | |
| </div>` | |
| ).join(''); | |
| } | |
| function renderGame() { | |
| // Clear canvas | |
| ctx.fillStyle = '#1a1a2e'; | |
| ctx.fillRect(0, 0, canvas.width, canvas.height); | |
| // Draw foods | |
| foods.forEach(food => { | |
| ctx.fillStyle = food.color; | |
| ctx.beginPath(); | |
| ctx.arc(food.x, food.y, food.radius, 0, Math.PI * 2); | |
| ctx.fill(); | |
| }); | |
| // Draw power-ups | |
| powerUps.forEach(powerUp => { | |
| ctx.save(); | |
| ctx.translate(powerUp.x, powerUp.y); | |
| ctx.rotate(Date.now() / 200); | |
| ctx.fillStyle = powerUp.color; | |
| ctx.fillRect(-powerUp.size/2, -powerUp.size/2, powerUp.size, powerUp.size); | |
| ctx.restore(); | |
| }); | |
| // Draw players | |
| Object.values(players).forEach(player => { | |
| // Draw worm segments | |
| player.segments.forEach((segment, i) => { | |
| const gradient = ctx.createRadialGradient( | |
| segment.x, segment.y, 0, | |
| segment.x, segment.y, segment.radius | |
| ); | |
| gradient.addColorStop(0, player.color); | |
| gradient.addColorStop(1, darkenColor(player.color, 0.3)); | |
| ctx.fillStyle = gradient; | |
| ctx.beginPath(); | |
| ctx.arc(segment.x, segment.y, segment.radius, 0, Math.PI * 2); | |
| ctx.fill(); | |
| // Draw eyes on head | |
| if (i === 0) { | |
| 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; | |
| // Left eye | |
| ctx.fillStyle = 'white'; | |
| ctx.beginPath(); | |
| 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 | |
| ); | |
| ctx.fill(); | |
| // Right eye | |
| ctx.beginPath(); | |
| 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 | |
| ); | |
| ctx.fill(); | |
| // Pupils | |
| ctx.fillStyle = 'black'; | |
| ctx.beginPath(); | |
| 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 | |
| ); | |
| ctx.fill(); | |
| ctx.beginPath(); | |
| 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 | |
| ); | |
| ctx.fill(); | |
| } | |
| }); | |
| // Draw player name | |
| if (player.segments.length > 0) { | |
| const head = player.segments[0]; | |
| ctx.fillStyle = 'white'; | |
| ctx.font = '12px "Press Start 2P", cursive'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText(player.name, head.x, head.y - head.radius - 10); | |
| } | |
| }); | |
| } | |
| function darkenColor(color, amount) { | |
| // Convert hex to RGB | |
| let r = parseInt(color.substr(1, 2), 16); | |
| let g = parseInt(color.substr(3, 2), 16); | |
| let b = parseInt(color.substr(5, 2), 16); | |
| // Darken each component | |
| r = Math.max(0, Math.floor(r * (1 - amount))); | |
| g = Math.max(0, Math.floor(g * (1 - amount))); | |
| b = Math.max(0, Math.floor(b * (1 - amount))); | |
| // Convert back to hex | |
| return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`; | |
| } | |
| // Start game | |
| playBtn.addEventListener('click', () => { | |
| const playerName = prompt('Enter your name:', 'Player' + Math.floor(Math.random() * 1000)); | |
| if (playerName) { | |
| socket.emit('joinGame', { | |
| name: playerName, | |
| color: getRandomColor() | |
| }); | |
| gameStarted = true; | |
| playBtn.style.display = 'none'; | |
| } | |
| }); | |
| function getRandomColor() { | |
| const colors = [ | |
| '#FF5252', '#FF4081', '#E040FB', '#7C4DFF', | |
| '#536DFE', '#448AFF', '#40C4FF', '#18FFFF', | |
| '#64FFDA', '#69F0AE', '#B2FF59', '#EEFF41', | |
| '#FFFF00', '#FFD740', '#FFAB40', '#FF6E40' | |
| ]; | |
| return colors[Math.floor(Math.random() * colors.length)]; | |
| } |