Spaces:
Running
Running
<html> | |
<head> | |
<style> | |
body { | |
margin: 0; | |
overflow: hidden; | |
background: #1a1a1a; | |
font-family: Arial; | |
} | |
#hud { | |
position: fixed; | |
top: 20px; | |
left: 20px; | |
color: white; | |
} | |
#healthBar { | |
width: 200px; | |
height: 20px; | |
background: #333; | |
border-radius: 10px; | |
overflow: hidden; | |
} | |
#health { | |
width: 100%; | |
height: 100%; | |
background: #2ecc71; | |
transition: width 0.2s; | |
} | |
#controls { | |
position: fixed; | |
top: 20px; | |
right: 20px; | |
color: white; | |
background: rgba(0,0,0,0.7); | |
padding: 15px; | |
border-radius: 5px; | |
} | |
#round { | |
position: fixed; | |
top: 20px; | |
left: 50%; | |
transform: translateX(-50%); | |
color: white; | |
font-size: 24px; | |
} | |
#gameOver { | |
position: fixed; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
background: rgba(0,0,0,0.8); | |
color: white; | |
padding: 20px; | |
border-radius: 10px; | |
text-align: center; | |
display: none; | |
} | |
#restart { | |
background: #2ecc71; | |
border: none; | |
color: white; | |
padding: 10px 20px; | |
border-radius: 5px; | |
cursor: pointer; | |
margin-top: 10px; | |
} | |
</style> | |
</head> | |
<body> | |
<canvas id="game"></canvas> | |
<div id="hud"> | |
<div id="healthBar"> | |
<div id="health"></div> | |
</div> | |
</div> | |
<div id="round">Round 1</div> | |
<div id="controls"> | |
WASD - Move<br> | |
Mouse - Aim<br> | |
Space - Shoot | |
</div> | |
<div id="gameOver"> | |
Game Over!<br> | |
<button id="restart" onclick="restartGame()">Restart Game</button> | |
</div> | |
<script> | |
const canvas = document.getElementById('game'); | |
const ctx = canvas.getContext('2d'); | |
canvas.width = window.innerWidth; | |
canvas.height = window.innerHeight; | |
const PLAYER_SIZE = 30; | |
const BULLET_SPEED = 10; | |
const ENEMY_SPEED = 2; | |
const SHOOTING_INTERVAL = 333; // 3 shots per second | |
let game = { | |
player: { | |
x: canvas.width/2, | |
y: canvas.height/2, | |
speed: 5, | |
health: 1000, | |
maxHealth: 1000, | |
bullets: [], | |
bulletsPerShot: 1 | |
}, | |
enemies: [], | |
items: [], | |
round: 1, | |
keys: {}, | |
mouse: {x: 0, y: 0}, | |
lastShot: 0, | |
shooting: false | |
}; | |
const gunSound = new Audio('data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YWoGAACBhYqFbF1pbphwX4WqY0J0zGYhdPV3I2vyd5qCvHKTlZJ0j6p+cJGYhIaHmH1li6R9YH6hooCEjaB0f4SOo3yLkZSCeq+bXY/FdWt9tnhcl69yZ5mfdGSSoYKEkZuBdYqgeIKEm4Joe6GJfX+VmnuBjZaFhqmQYY7Hc36QiXd6kpWLgpSieIeQnICEkZeEhJOcfH6Om4GBjZWEgpOYfICLl4OBjZSEgZKYfICKloKBjJODgZGXe3+JlYGAi5KCgJCWen+IlICAfoh+gY2ScICBjX+ChImFhYuJhYeHhoeHh4iIiIiIiIiIiIiIiIiIiIiIiIiHh4aGhYSCgYB/fn18fHx9fn+AgYKEhYeIiYuMjo+RkpSVl5iZmptcW15hZGhrbW5vcHFydHZ4enx+gIKEh4mMj5GSk5OTkpGQjo2LiomIiImKi4yOj5GSlJWXmJqbnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drbAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4CBgoOEhYaHiImKi4yNjo+QkZKTlJWWl5iZmpucnZ6foKGio6SlpqeoqaqrrK2ur7CxsrO0tba3uLm6u7y9vr/AwcLDxMXGx8jJysvMzc7P0NHS09TV1tfY2drb3N3e3+Dh4uPk5ebn6Onq6+zt7u/w8fLz9PX29/j5+vv8/f7/AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/wABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gIGCg4SFhoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnp+goaKjpKWmp6ipqqusra6vsLGys7S1tre4ubq7vL2+v8DBwsPExcbHyMnKy8zNzs/Q0dLT1NXW19jZ2tvc3d7f4OHi4+Tl5ufo6err7O3u7/Dx8vP09fb3+Pn6+/z9/v8='); | |
function spawnEnemies() { | |
const enemyCount = game.round * 5; | |
for(let i = 0; i < enemyCount; i++) { | |
game.enemies.push({ | |
x: Math.random() * canvas.width, | |
y: Math.random() * canvas.height, | |
health: 1000, | |
maxHealth: 1000, | |
bullets: [], | |
lastShot: 0, | |
direction: { | |
x: Math.random() * 2 - 1, | |
y: Math.random() * 2 - 1 | |
} | |
}); | |
} | |
} | |
function update() { | |
// Player movement | |
if(game.keys['w'] && game.player.y > PLAYER_SIZE) | |
game.player.y -= game.player.speed; | |
if(game.keys['s'] && game.player.y < canvas.height - PLAYER_SIZE) | |
game.player.y += game.player.speed; | |
if(game.keys['a'] && game.player.x > PLAYER_SIZE) | |
game.player.x -= game.player.speed; | |
if(game.keys['d'] && game.player.x < canvas.width - PLAYER_SIZE) | |
game.player.x += game.player.speed; | |
// Player shooting | |
if(game.shooting && Date.now() - game.lastShot > SHOOTING_INTERVAL) { | |
const angle = Math.atan2( | |
game.mouse.y - game.player.y, | |
game.mouse.x - game.player.x | |
); | |
for(let i = 0; i < game.player.bulletsPerShot; i++) { | |
game.player.bullets.push({ | |
x: game.player.x, | |
y: game.player.y, | |
angle: angle + (Math.random() - 0.5) * 0.2 | |
}); | |
} | |
gunSound.cloneNode().play(); | |
game.lastShot = Date.now(); | |
} | |
// Update bullets and check collisions | |
for(let i = game.player.bullets.length - 1; i >= 0; i--) { | |
const bullet = game.player.bullets[i]; | |
bullet.x += Math.cos(bullet.angle) * BULLET_SPEED; | |
bullet.y += Math.sin(bullet.angle) * BULLET_SPEED; | |
// Remove out of bounds bullets | |
if(bullet.x < 0 || bullet.x > canvas.width || | |
bullet.y < 0 || bullet.y > canvas.height) { | |
game.player.bullets.splice(i, 1); | |
} | |
// Check enemy hits | |
for(let j = game.enemies.length - 1; j >= 0; j--) { | |
const enemy = game.enemies[j]; | |
const dx = bullet.x - enemy.x; | |
const dy = bullet.y - enemy.y; | |
if(Math.sqrt(dx*dx + dy*dy) < PLAYER_SIZE/2) { | |
enemy.health -= 100; | |
game.player.bullets.splice(i, 1); | |
if(enemy.health <= 0) { | |
game.enemies.splice(j, 1); | |
game.player.bulletsPerShot++; | |
// Drop health item | |
game.items.push({ | |
x: enemy.x, | |
y: enemy.y, | |
type: 'health' | |
}); | |
} | |
break; | |
} | |
} | |
} | |
// Update enemies | |
game.enemies.forEach(enemy => { | |
// Movement | |
enemy.x += enemy.direction.x * ENEMY_SPEED; | |
enemy.y += enemy.direction.y * ENEMY_SPEED; | |
// Bounce off walls | |
if(enemy.x < 0 || enemy.x > canvas.width) enemy.direction.x *= -1; | |
if(enemy.y < 0 || enemy.y > canvas.height) enemy.direction.y *= -1; | |
// Shooting | |
if(Date.now() - enemy.lastShot > 1000) { | |
const angle = Math.atan2( | |
game.player.y - enemy.y, | |
game.player.x - enemy.x | |
); | |
enemy.bullets.push({ | |
x: enemy.x, | |
y: enemy.y, | |
angle: angle | |
}); | |
enemy.lastShot = Date.now(); | |
} | |
// Update enemy bullets | |
for(let i = enemy.bullets.length - 1; i >= 0; i--) { | |
const bullet = enemy.bullets[i]; | |
bullet.x += Math.cos(bullet.angle) * BULLET_SPEED/2; | |
bullet.y += Math.sin(bullet.angle) * BULLET_SPEED/2; | |
// Check player hit | |
const dx = bullet.x - game.player.x; | |
const dy = bullet.y - game.player.y; | |
if(Math.sqrt(dx*dx + dy*dy) < PLAYER_SIZE/2) { | |
game.player.health -= 50; | |
enemy.bullets.splice(i, 1); | |
document.getElementById('health').style.width = | |
(game.player.health/game.player.maxHealth * 100) + '%'; | |
if(game.player.health <= 0) { | |
document.getElementById('gameOver').style.display = 'block'; | |
} | |
} | |
// Remove out of bounds bullets | |
if(bullet.x < 0 || bullet.x > canvas.width || | |
bullet.y < 0 || bullet.y > canvas.height) { | |
enemy.bullets.splice(i, 1); | |
} | |
} | |
}); | |
// Update items | |
for(let i = game.items.length - 1; i >= 0; i--) { | |
const item = game.items[i]; | |
const dx = item.x - game.player.x; | |
const dy = item.y - game.player.y; | |
if(Math.sqrt(dx*dx + dy*dy) < PLAYER_SIZE) { | |
game.player.health = Math.min( | |
game.player.maxHealth, | |
game.player.health + 200 | |
); | |
document.getElementById('health').style.width = | |
(game.player.health/game.player.maxHealth * 100) + '%'; | |
game.items.splice(i, 1); | |
} | |
} | |
// Check round completion | |
if(game.enemies.length === 0) { | |
if(game.round < 10) { | |
game.round++; | |
document.getElementById('round').textContent = `Round ${game.round}`; | |
game.player.health = game.player.maxHealth; | |
document.getElementById('health').style.width = '100%'; | |
game.player.bulletsPerShot = 1; // Reset bullets per shot | |
spawnEnemies(); | |
} | |
} | |
} | |
function draw() { | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
// Draw player | |
ctx.fillStyle = 'white'; | |
ctx.font = '30px Arial'; | |
ctx.fillText('🐭', game.player.x - 15, game.player.y + 15); | |
// Draw player bullets | |
ctx.fillStyle = 'yellow'; | |
game.player.bullets.forEach(bullet => { | |
ctx.beginPath(); | |
ctx.arc(bullet.x, bullet.y, 3, 0, Math.PI * 2); | |
ctx.fill(); | |
}); | |
// Draw enemies | |
game.enemies.forEach(enemy => { | |
// Enemy | |
ctx.fillStyle = 'white'; | |
ctx.fillText('🐱', enemy.x - 15, enemy.y + 15); | |
// Health bar | |
ctx.fillStyle = '#600'; | |
ctx.fillRect(enemy.x - 25, enemy.y - 30, 50, 5); | |
ctx.fillStyle = '#f00'; | |
ctx.fillRect(enemy.x - 25, enemy.y - 30, | |
(enemy.health/enemy.maxHealth) * 50, 5); | |
// Enemy bullets | |
ctx.fillStyle = 'red'; | |
enemy.bullets.forEach(bullet => { | |
ctx.beginPath(); | |
ctx.arc(bullet.x, bullet.y, 3, 0, Math.PI * 2); | |
ctx.fill(); | |
}); | |
}); | |
// Draw items | |
ctx.fillStyle = 'green'; | |
game.items.forEach(item => { | |
ctx.fillText('✚', item.x - 10, item.y + 10); | |
}); | |
} | |
function gameLoop() { | |
if(game.player.health > 0) { | |
update(); | |
draw(); | |
} | |
requestAnimationFrame(gameLoop); | |
} | |
function restartGame() { | |
game = { | |
player: { | |
x: canvas.width/2, | |
y: canvas.height/2, | |
speed: 5, | |
health: 1000, | |
maxHealth: 1000, | |
bullets: [], | |
bulletsPerShot: 1 | |
}, | |
enemies: [], | |
items: [], | |
round: 1, | |
keys: game.keys, | |
mouse: game.mouse, | |
lastShot: 0, | |
shooting: false | |
}; | |
document.getElementById('health').style.width = '100%'; | |
document.getElementById('round').textContent = 'Round 1'; | |
document.getElementById('gameOver').style.display = 'none'; | |
spawnEnemies(); | |
} | |
// Event listeners | |
window.addEventListener('keydown', e => game.keys[e.key] = true); | |
window.addEventListener('keyup', e => game.keys[e.key] = false); | |
window.addEventListener('mousemove', e => { | |
game.mouse.x = e.clientX; | |
game.mouse.y = e.clientY; | |
}); | |
window.addEventListener('keydown', e => { | |
if(e.code === 'Space') game.shooting = true; | |
}); | |
window.addEventListener('keyup', e => { | |
if(e.code === 'Space') game.shooting = false; | |
}); | |
// Start game | |
spawnEnemies(); | |
gameLoop(); | |
</script> | |
</body> | |
</html><script async data-explicit-opt-in="true" data-cookie-opt-in="true" src="https://vercel.live/_next-live/feedback/feedback.js"></script> |