space / index.html
Sushil kumar
Add 2 files
1ce99fc verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Galactic Defender</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
body {
margin: 0;
overflow: hidden;
background-color: #111;
font-family: 'Arial', sans-serif;
}
#gameCanvas {
display: block;
margin: 0 auto;
background-color: #000;
}
#gameContainer {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
#startScreen, #gameOverScreen, #pauseScreen {
position: absolute;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.8);
color: white;
z-index: 10;
}
#gameOverScreen, #pauseScreen {
display: none;
}
.btn {
background: linear-gradient(135deg, #6e8efb, #a777e3);
border: none;
color: white;
padding: 12px 24px;
margin: 10px;
border-radius: 30px;
font-size: 18px;
cursor: pointer;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
transition: all 0.3s ease;
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}
.btn:active {
transform: translateY(1px);
}
.title {
font-size: 48px;
margin-bottom: 20px;
background: linear-gradient(135deg, #6e8efb, #a777e3);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.score-display {
font-size: 24px;
margin-bottom: 30px;
}
.powerup {
position: absolute;
font-size: 24px;
animation: float 2s infinite ease-in-out;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
.leaderboard {
background: rgba(0, 0, 0, 0.6);
padding: 20px;
border-radius: 10px;
margin-top: 20px;
max-height: 200px;
overflow-y: auto;
}
.leaderboard-item {
display: flex;
justify-content: space-between;
padding: 5px 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.difficulty-selector {
margin: 20px 0;
display: flex;
gap: 10px;
}
.difficulty-btn {
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
transition: all 0.3s;
}
.difficulty-btn.active {
background: linear-gradient(135deg, #6e8efb, #a777e3);
box-shadow: 0 0 10px rgba(110, 142, 251, 0.5);
}
</style>
</head>
<body>
<div id="gameContainer">
<canvas id="gameCanvas"></canvas>
<div id="startScreen">
<h1 class="title">GALACTIC DEFENDER</h1>
<p class="text-white mb-8 text-lg">Defend the galaxy from alien invaders!</p>
<div class="difficulty-selector">
<div class="difficulty-btn active" data-difficulty="easy">Easy</div>
<div class="difficulty-btn" data-difficulty="medium">Medium</div>
<div class="difficulty-btn" data-difficulty="hard">Hard</div>
</div>
<button id="startBtn" class="btn">
<i class="fas fa-play mr-2"></i> START GAME
</button>
<div class="mt-8 text-gray-400 text-center">
<p class="font-bold mb-2">Controls:</p>
<p><i class="fas fa-arrow-left mr-2"></i> <i class="fas fa-arrow-right mr-2"></i> Move</p>
<p><i class="fas fa-arrow-up mr-2"></i> <i class="fas fa-arrow-down mr-2"></i> Move</p>
<p><i class="fas fa-space-shuttle mr-2"></i> Shoot</p>
<p><i class="fas fa-pause mr-2"></i> Pause</p>
</div>
<div class="leaderboard mt-8 w-64">
<h3 class="text-center mb-2">TOP SCORES</h3>
<div id="leaderboardList">
<!-- Leaderboard items will be added here -->
</div>
</div>
</div>
<div id="gameOverScreen">
<h1 class="title">MISSION FAILED</h1>
<p id="finalScore" class="score-display">Score: 0</p>
<p id="highScore" class="text-gray-400 mb-4">High Score: 0</p>
<button id="restartBtn" class="btn">
<i class="fas fa-redo mr-2"></i> TRY AGAIN
</button>
<button id="menuBtn" class="btn mt-4">
<i class="fas fa-home mr-2"></i> MAIN MENU
</button>
</div>
<div id="pauseScreen">
<h1 class="title">GAME PAUSED</h1>
<p class="text-white mb-8">Current Score: <span id="pauseScore">0</span></p>
<button id="resumeBtn" class="btn">
<i class="fas fa-play mr-2"></i> RESUME
</button>
<button id="quitBtn" class="btn mt-4">
<i class="fas fa-sign-out-alt mr-2"></i> QUIT
</button>
</div>
</div>
<audio id="shootSound" src="https://assets.mixkit.co/sfx/preview/mixkit-laser-weapon-shot-1681.mp3" preload="auto"></audio>
<audio id="explosionSound" src="https://assets.mixkit.co/sfx/preview/mixkit-explosion-impact-1684.mp3" preload="auto"></audio>
<audio id="powerupSound" src="https://assets.mixkit.co/sfx/preview/mixkit-power-up-electricity-2580.mp3" preload="auto"></audio>
<audio id="bgMusic" loop src="https://assets.mixkit.co/music/preview/mixkit-game-show-idea-229.mp3" preload="auto"></audio>
<script>
// Game variables
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
const startScreen = document.getElementById('startScreen');
const gameOverScreen = document.getElementById('gameOverScreen');
const pauseScreen = document.getElementById('pauseScreen');
const startBtn = document.getElementById('startBtn');
const restartBtn = document.getElementById('restartBtn');
const menuBtn = document.getElementById('menuBtn');
const resumeBtn = document.getElementById('resumeBtn');
const quitBtn = document.getElementById('quitBtn');
const finalScore = document.getElementById('finalScore');
const highScore = document.getElementById('highScore');
const pauseScore = document.getElementById('pauseScore');
const leaderboardList = document.getElementById('leaderboardList');
const difficultyBtns = document.querySelectorAll('.difficulty-btn');
// Sound elements
const shootSound = document.getElementById('shootSound');
const explosionSound = document.getElementById('explosionSound');
const powerupSound = document.getElementById('powerupSound');
const bgMusic = document.getElementById('bgMusic');
// Set canvas size
canvas.width = window.innerWidth * 0.9;
canvas.height = window.innerHeight * 0.9;
// Game state
let gameRunning = false;
let gamePaused = false;
let score = 0;
let lives = 3;
let level = 1;
let difficulty = 'easy';
let asteroids = [];
let bullets = [];
let explosions = [];
let powerups = [];
let enemies = [];
let stars = [];
let lastAsteroidTime = 0;
let asteroidInterval = 2000; // ms
let keys = {};
let leaderboard = JSON.parse(localStorage.getItem('spaceShooterLeaderboard')) || [];
let playerPowerups = {
rapidFire: false,
tripleShot: false,
shield: false
};
let powerupEndTime = 0;
// Player object
const player = {
x: canvas.width / 2,
y: canvas.height - 60,
width: 40,
height: 60,
speed: 5,
color: '#6e8efb',
lastShot: 0,
shootDelay: 300, // ms
draw() {
ctx.save();
// Draw shield if active
if (playerPowerups.shield) {
ctx.beginPath();
ctx.arc(this.x, this.y, this.width + 15, 0, Math.PI * 2);
ctx.strokeStyle = 'rgba(100, 200, 255, 0.7)';
ctx.lineWidth = 3;
ctx.stroke();
}
// Draw spaceship (triangle)
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.moveTo(this.x, this.y - this.height/2);
ctx.lineTo(this.x - this.width/2, this.y + this.height/2);
ctx.lineTo(this.x + this.width/2, this.y + this.height/2);
ctx.closePath();
ctx.fill();
// Draw cockpit
ctx.fillStyle = '#a777e3';
ctx.beginPath();
ctx.arc(this.x, this.y - 10, 10, 0, Math.PI * 2);
ctx.fill();
// Draw engine glow
if (keys['ArrowUp'] || keys['ArrowLeft'] || keys['ArrowRight'] || keys['ArrowDown']) {
ctx.fillStyle = '#ff5555';
ctx.beginPath();
ctx.moveTo(this.x - this.width/3, this.y + this.height/2);
ctx.lineTo(this.x + this.width/3, this.y + this.height/2);
ctx.lineTo(this.x, this.y + this.height/2 + 15);
ctx.closePath();
ctx.fill();
}
ctx.restore();
},
update() {
if (keys['ArrowLeft'] && this.x > this.width/2) {
this.x -= this.speed;
}
if (keys['ArrowRight'] && this.x < canvas.width - this.width/2) {
this.x += this.speed;
}
if (keys['ArrowUp'] && this.y > this.height/2) {
this.y -= this.speed;
}
if (keys['ArrowDown'] && this.y < canvas.height - this.height/2) {
this.y += this.speed;
}
},
shoot() {
const now = Date.now();
if (now - this.lastShot > (playerPowerups.rapidFire ? this.shootDelay / 2 : this.shootDelay)) {
if (playerPowerups.tripleShot) {
// Triple shot
bullets.push({
x: this.x - 15,
y: this.y - this.height/2,
radius: 5,
speed: 10,
color: '#ff5555',
angle: -0.2
});
bullets.push({
x: this.x,
y: this.y - this.height/2,
radius: 5,
speed: 10,
color: '#ff5555',
angle: 0
});
bullets.push({
x: this.x + 15,
y: this.y - this.height/2,
radius: 5,
speed: 10,
color: '#ff5555',
angle: 0.2
});
} else {
// Single shot
bullets.push({
x: this.x,
y: this.y - this.height/2,
radius: 5,
speed: 10,
color: '#ff5555',
angle: 0
});
}
this.lastShot = now;
// Play shoot sound
playSound('shoot');
}
}
};
// Create stars for background
function createStars() {
stars = [];
for (let i = 0; i < 200; i++) {
stars.push({
x: Math.random() * canvas.width,
y: Math.random() * canvas.height,
radius: Math.random() * 2,
speed: Math.random() * 0.5 + 0.1,
alpha: Math.random() * 0.5 + 0.5
});
}
}
// Draw stars
function drawStars() {
ctx.save();
stars.forEach(star => {
ctx.beginPath();
ctx.arc(star.x, star.y, star.radius, 0, Math.PI * 2);
ctx.fillStyle = `rgba(255, 255, 255, ${star.alpha})`;
ctx.fill();
// Move stars
star.y += star.speed;
if (star.y > canvas.height) {
star.y = 0;
star.x = Math.random() * canvas.width;
}
});
ctx.restore();
}
// Create asteroid
function createAsteroid() {
const size = Math.random() * 30 + 20;
const x = Math.random() * (canvas.width - size * 2) + size;
asteroids.push({
x: x,
y: -size,
size: size,
speed: Math.random() * 2 + 1 + level * 0.2,
rotation: 0,
rotationSpeed: Math.random() * 0.02 - 0.01,
vertices: Math.floor(Math.random() * 5) + 5,
offset: Array.from({length: Math.floor(Math.random() * 5) + 5}, () => Math.random() * 10 - 5),
health: Math.floor(size / 10)
});
}
// Create enemy ship
function createEnemyShip() {
const types = ['basic', 'shooter', 'fast'];
const type = types[Math.floor(Math.random() * types.length)];
const size = 40;
const x = Math.random() * (canvas.width - size * 2) + size;
let enemy = {
x: x,
y: -size,
width: size,
height: size,
speed: 1 + level * 0.1,
type: type,
lastShot: 0,
shootDelay: 2000,
health: type === 'basic' ? 1 : (type === 'shooter' ? 2 : 1),
color: type === 'basic' ? '#ff5555' : (type === 'shooter' ? '#55ff55' : '#5555ff')
};
// Adjust properties based on type
if (type === 'fast') {
enemy.speed *= 2;
}
enemies.push(enemy);
}
// Create powerup
function createPowerup(x, y) {
const types = ['rapidFire', 'tripleShot', 'shield', 'extraLife'];
const type = types[Math.floor(Math.random() * types.length)];
powerups.push({
x: x,
y: y,
type: type,
radius: 15,
speed: 2,
color: getPowerupColor(type)
});
}
// Get powerup color by type
function getPowerupColor(type) {
switch(type) {
case 'rapidFire': return '#ffcc00';
case 'tripleShot': return '#00ccff';
case 'shield': return '#00ffcc';
case 'extraLife': return '#ff00cc';
default: return '#ffffff';
}
}
// Get powerup icon by type
function getPowerupIcon(type) {
switch(type) {
case 'rapidFire': return 'fa-bolt';
case 'tripleShot': return 'fa-crosshairs';
case 'shield': return 'fa-shield-alt';
case 'extraLife': return 'fa-heart';
default: return 'fa-star';
}
}
// Draw asteroid
function drawAsteroid(asteroid) {
ctx.save();
ctx.translate(asteroid.x, asteroid.y);
ctx.rotate(asteroid.rotation);
ctx.beginPath();
for (let i = 0; i < asteroid.vertices; i++) {
const angle = (i / asteroid.vertices) * Math.PI * 2;
const radius = asteroid.size + asteroid.offset[i % asteroid.offset.length];
const x = Math.cos(angle) * radius;
const y = Math.sin(angle) * radius;
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.closePath();
ctx.fillStyle = '#888';
ctx.fill();
ctx.strokeStyle = '#555';
ctx.lineWidth = 2;
ctx.stroke();
// Draw health bar if asteroid has health > 1
if (asteroid.health > 1) {
const healthPercent = asteroid.health / Math.floor(asteroid.size / 10);
ctx.fillStyle = 'red';
ctx.fillRect(-asteroid.size/2, -asteroid.size/2 - 10, asteroid.size, 5);
ctx.fillStyle = 'lime';
ctx.fillRect(-asteroid.size/2, -asteroid.size/2 - 10, asteroid.size * healthPercent, 5);
}
ctx.restore();
}
// Draw enemy ship
function drawEnemyShip(enemy) {
ctx.save();
ctx.translate(enemy.x, enemy.y);
// Draw different enemy types
if (enemy.type === 'basic') {
// Basic triangle enemy
ctx.fillStyle = enemy.color;
ctx.beginPath();
ctx.moveTo(0, -enemy.height/2);
ctx.lineTo(-enemy.width/2, enemy.height/2);
ctx.lineTo(enemy.width/2, enemy.height/2);
ctx.closePath();
ctx.fill();
// Draw cockpit
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(0, -5, 5, 0, Math.PI * 2);
ctx.fill();
}
else if (enemy.type === 'shooter') {
// Shooter enemy (rectangle with turret)
ctx.fillStyle = enemy.color;
ctx.fillRect(-enemy.width/2, -enemy.height/2, enemy.width, enemy.height);
// Draw turret
ctx.fillStyle = '#ffffff';
ctx.fillRect(-5, -enemy.height/2 - 10, 10, 10);
}
else if (enemy.type === 'fast') {
// Fast enemy (small and sleek)
ctx.fillStyle = enemy.color;
ctx.beginPath();
ctx.moveTo(0, -enemy.height/2);
ctx.lineTo(-enemy.width/3, enemy.height/2);
ctx.lineTo(enemy.width/3, enemy.height/2);
ctx.closePath();
ctx.fill();
// Draw engine glow
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.moveTo(-enemy.width/4, enemy.height/2);
ctx.lineTo(enemy.width/4, enemy.height/2);
ctx.lineTo(0, enemy.height/2 + 8);
ctx.closePath();
ctx.fill();
}
// Draw health bar if enemy has health > 1
if (enemy.health > 1) {
const healthPercent = enemy.type === 'shooter' ? enemy.health / 2 : enemy.health;
ctx.fillStyle = 'red';
ctx.fillRect(-enemy.width/2, -enemy.height/2 - 10, enemy.width, 5);
ctx.fillStyle = 'lime';
ctx.fillRect(-enemy.width/2, -enemy.height/2 - 10, enemy.width * healthPercent, 5);
}
ctx.restore();
}
// Draw bullet
function drawBullet(bullet) {
ctx.save();
ctx.fillStyle = bullet.color;
// If bullet has angle (for triple shot), adjust position
if (bullet.angle) {
bullet.x += Math.sin(bullet.angle) * 2;
}
ctx.beginPath();
ctx.arc(bullet.x, bullet.y, bullet.radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
// Draw enemy bullet
function drawEnemyBullet(bullet) {
ctx.save();
ctx.fillStyle = '#ff00ff';
ctx.beginPath();
ctx.arc(bullet.x, bullet.y, bullet.radius, 0, Math.PI * 2);
ctx.fill();
// Add glow effect
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.beginPath();
ctx.arc(bullet.x, bullet.y, bullet.radius + 2, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
// Draw powerup
function drawPowerup(powerup) {
ctx.save();
// Draw outer circle
ctx.beginPath();
ctx.arc(powerup.x, powerup.y, powerup.radius, 0, Math.PI * 2);
ctx.fillStyle = powerup.color;
ctx.fill();
// Draw inner circle
ctx.beginPath();
ctx.arc(powerup.x, powerup.y, powerup.radius - 5, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.fill();
ctx.restore();
// Draw icon using DOM element (since we can't draw Font Awesome icons on canvas)
const icon = document.createElement('div');
icon.className = `powerup fas ${getPowerupIcon(powerup.type)}`;
icon.style.color = 'white';
icon.style.left = `${powerup.x - 12}px`;
icon.style.top = `${powerup.y - 12}px`;
icon.style.zIndex = '5';
document.getElementById('gameContainer').appendChild(icon);
// Remove icon after animation frame
setTimeout(() => {
if (icon.parentNode) {
icon.parentNode.removeChild(icon);
}
}, 16);
}
// Draw explosion
function drawExplosion(explosion) {
ctx.save();
ctx.globalAlpha = explosion.alpha;
ctx.fillStyle = explosion.color;
for (let i = 0; i < explosion.particles; i++) {
const angle = (i / explosion.particles) * Math.PI * 2;
const distance = explosion.radius * (1 - explosion.progress);
const x = explosion.x + Math.cos(angle) * distance;
const y = explosion.y + Math.sin(angle) * distance;
ctx.beginPath();
ctx.arc(x, y, explosion.particleSize, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
}
// Check collision between two objects
function checkCollision(obj1, obj2) {
// Simple circle collision detection
const dx = obj1.x - obj2.x;
const dy = obj1.y - obj2.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (obj1.radius && obj2.radius) {
return distance < obj1.radius + obj2.radius;
}
// For player (triangle) vs asteroid/enemy
if (obj1 === player || obj2 === player) {
const playerObj = obj1 === player ? obj1 : obj2;
const otherObj = obj1 === player ? obj2 : obj1;
// Simplified collision - check if object center is within player bounds
return (
otherObj.x > playerObj.x - playerObj.width/2 &&
otherObj.x < playerObj.x + playerObj.width/2 &&
otherObj.y > playerObj.y - playerObj.height/2 &&
otherObj.y < playerObj.y + playerObj.height/2
);
}
// For bullet vs asteroid/enemy
if (obj1.radius && (obj2.size || obj2.width)) {
const bullet = obj1.radius ? obj1 : obj2;
const target = obj1.radius ? obj2 : obj1;
if (target.size) {
// Asteroid
return distance < bullet.radius + target.size;
} else {
// Enemy ship
return (
bullet.x > target.x - target.width/2 &&
bullet.x < target.x + target.width/2 &&
bullet.y > target.y - target.height/2 &&
bullet.y < target.y + target.height/2
);
}
}
return false;
}
// Create explosion
function createExplosion(x, y, color = '#ff5555', size = 30) {
explosions.push({
x: x,
y: y,
radius: size,
particles: 12,
particleSize: 3,
color: color,
alpha: 1,
progress: 0,
speed: 0.05
});
// Play explosion sound
playSound('explosion');
}
// Play sound
function playSound(type) {
try {
switch(type) {
case 'shoot':
shootSound.currentTime = 0;
shootSound.play();
break;
case 'explosion':
explosionSound.currentTime = 0;
explosionSound.play();
break;
case 'powerup':
powerupSound.currentTime = 0;
powerupSound.play();
break;
}
} catch(e) {
console.log('Error playing sound:', e);
}
}
// Draw HUD
function drawHUD() {
ctx.save();
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(10, 10, 200, 110);
ctx.fillStyle = 'white';
ctx.font = '20px Arial';
ctx.textAlign = 'left';
ctx.fillText(`Score: ${score}`, 20, 40);
ctx.fillText(`Lives: ${lives}`, 20, 70);
ctx.fillText(`Level: ${level}`, 20, 100);
// Draw powerup indicators
if (playerPowerups.rapidFire || playerPowerups.tripleShot || playerPowerups.shield) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(canvas.width - 160, 10, 150, 50);
ctx.fillStyle = 'white';
ctx.font = '14px Arial';
ctx.textAlign = 'right';
const remainingTime = Math.max(0, Math.ceil((powerupEndTime - Date.now()) / 1000));
if (playerPowerups.rapidFire) {
ctx.fillStyle = '#ffcc00';
ctx.fillText(`Rapid Fire: ${remainingTime}s`, canvas.width - 20, 30);
}
if (playerPowerups.tripleShot) {
ctx.fillStyle = '#00ccff';
ctx.fillText(`Triple Shot: ${remainingTime}s`, canvas.width - 20, 50);
}
if (playerPowerups.shield) {
ctx.fillStyle = '#00ffcc';
ctx.fillText(`Shield: ${remainingTime}s`, canvas.width - 20, 30);
}
}
ctx.restore();
}
// Update game state
function update() {
if (!gameRunning || gamePaused) return;
// Update player
player.update();
// Check powerup expiration
if (playerPowerups.rapidFire || playerPowerups.tripleShot || playerPowerups.shield) {
if (Date.now() > powerupEndTime) {
playerPowerups.rapidFire = false;
playerPowerups.tripleShot = false;
playerPowerups.shield = false;
}
}
// Create asteroids and enemies
const now = Date.now();
if (now - lastAsteroidTime > asteroidInterval) {
if (Math.random() < 0.7) {
createAsteroid();
} else {
createEnemyShip();
}
lastAsteroidTime = now;
// Decrease interval over time to make game harder
asteroidInterval = Math.max(500, 2000 - level * 100);
// Random chance to spawn powerup
if (Math.random() < 0.1) {
createPowerup(Math.random() * (canvas.width - 30) + 15, -30);
}
}
// Update asteroids
asteroids.forEach((asteroid, index) => {
asteroid.y += asteroid.speed;
asteroid.rotation += asteroid.rotationSpeed;
// Check if asteroid is out of screen
if (asteroid.y > canvas.height + asteroid.size) {
asteroids.splice(index, 1);
}
// Check collision with player
if (checkCollision(player, asteroid)) {
if (playerPowerups.shield) {
playerPowerups.shield = false;
createExplosion(asteroid.x, asteroid.y, '#ffff55', asteroid.size);
asteroids.splice(index, 1);
} else {
asteroids.splice(index, 1);
lives--;
createExplosion(asteroid.x, asteroid.y, '#ff5555', asteroid.size);
if (lives <= 0) {
gameOver();
}
}
}
});
// Update enemies
enemies.forEach((enemy, index) => {
enemy.y += enemy.speed;
// Enemy specific behavior
if (enemy.type === 'shooter' && now - enemy.lastShot > enemy.shootDelay) {
// Shoot at player
bullets.push({
x: enemy.x,
y: enemy.y + enemy.height/2,
radius: 5,
speed: -7,
color: '#ff00ff',
isEnemy: true
});
enemy.lastShot = now;
}
// Check if enemy is out of screen
if (enemy.y > canvas.height + enemy.height) {
enemies.splice(index, 1);
}
// Check collision with player
if (checkCollision(player, enemy)) {
if (playerPowerups.shield) {
playerPowerups.shield = false;
createExplosion(enemy.x, enemy.y, '#ffff55', enemy.width);
enemies.splice(index, 1);
} else {
enemies.splice(index, 1);
lives--;
createExplosion(enemy.x, enemy.y, '#ff5555', enemy.width);
if (lives <= 0) {
gameOver();
}
}
}
});
// Update bullets
bullets.forEach((bullet, index) => {
if (bullet.isEnemy) {
// Enemy bullets move down
bullet.y -= bullet.speed;
} else {
// Player bullets move up
bullet.y += bullet.speed;
}
// Check if bullet is out of screen
if (bullet.y < 0 || bullet.y > canvas.height) {
bullets.splice(index, 1);
return;
}
// Check collision with asteroids
asteroids.forEach((asteroid, aIndex) => {
if (checkCollision(bullet, asteroid)) {
// Reduce asteroid health
asteroid.health--;
if (asteroid.health <= 0) {
// Remove asteroid
asteroids.splice(aIndex, 1);
// Add score
score += Math.floor(asteroid.size);
// Random chance to drop powerup
if (Math.random() < 0.2) {
createPowerup(asteroid.x, asteroid.y);
}
// Create explosion
createExplosion(asteroid.x, asteroid.y, '#ffff55', asteroid.size);
}
// Remove bullet unless it's a piercing shot (future feature)
bullets.splice(index, 1);
}
});
// Check collision with enemies
enemies.forEach((enemy, eIndex) => {
if (checkCollision(bullet, enemy)) {
// Reduce enemy health
enemy.health--;
if (enemy.health <= 0) {
// Remove enemy
enemies.splice(eIndex, 1);
// Add score based on enemy type
score += enemy.type === 'basic' ? 50 : (enemy.type === 'shooter' ? 100 : 75);
// Random chance to drop powerup
if (Math.random() < 0.3) {
createPowerup(enemy.x, enemy.y);
}
// Create explosion
createExplosion(enemy.x, enemy.y, '#ffff55', enemy.width);
}
// Remove bullet
bullets.splice(index, 1);
}
});
});
// Update powerups
powerups.forEach((powerup, index) => {
powerup.y += powerup.speed;
// Check if powerup is out of screen
if (powerup.y > canvas.height + powerup.radius) {
powerups.splice(index, 1);
return;
}
// Check collision with player
if (checkCollision(player, powerup)) {
// Apply powerup effect
applyPowerup(powerup.type);
powerups.splice(index, 1);
// Play powerup sound
playSound('powerup');
}
});
// Update explosions
explosions.forEach((explosion, index) => {
explosion.progress += explosion.speed;
explosion.alpha = 1 - explosion.progress;
if (explosion.progress >= 1) {
explosions.splice(index, 1);
}
});
// Level up every 500 points
if (score > 0 && score % 500 === 0 && score / 500 >= level) {
level++;
createExplosion(canvas.width / 2, canvas.height / 2, '#ffffff', 100);
}
}
// Apply powerup effect
function applyPowerup(type) {
// Set powerup duration (10 seconds)
powerupEndTime = Date.now() + 10000;
switch(type) {
case 'rapidFire':
playerPowerups.rapidFire = true;
break;
case 'tripleShot':
playerPowerups.tripleShot = true;
break;
case 'shield':
playerPowerups.shield = true;
break;
case 'extraLife':
lives++;
break;
}
}
// Draw game
function draw() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw background
drawStars();
// Draw game objects
player.draw();
asteroids.forEach(drawAsteroid);
enemies.forEach(drawEnemyShip);
bullets.forEach(bullet => bullet.isEnemy ? drawEnemyBullet(bullet) : drawBullet(bullet));
powerups.forEach(drawPowerup);
explosions.forEach(drawExplosion);
// Draw HUD
drawHUD();
}
// Game loop
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
// Update leaderboard
function updateLeaderboard() {
leaderboardList.innerHTML = '';
// Add current score to leaderboard
if (score > 0) {
leaderboard.push({
score: score,
date: new Date().toLocaleDateString(),
difficulty: difficulty
});
}
// Sort by score (descending)
leaderboard.sort((a, b) => b.score - a.score);
// Keep only top 10 scores
leaderboard = leaderboard.slice(0, 10);
// Save to localStorage
localStorage.setItem('spaceShooterLeaderboard', JSON.stringify(leaderboard));
// Display leaderboard
leaderboard.forEach((entry, index) => {
const item = document.createElement('div');
item.className = 'leaderboard-item';
item.innerHTML = `
<span>${index + 1}. ${entry.score}</span>
<span class="text-gray-400 text-sm">${entry.difficulty}</span>
`;
leaderboardList.appendChild(item);
});
}
// Start game
function startGame() {
// Reset game state
score = 0;
lives = difficulty === 'easy' ? 5 : (difficulty === 'medium' ? 3 : 1);
level = 1;
asteroids = [];
bullets = [];
explosions = [];
powerups = [];
enemies = [];
playerPowerups = {
rapidFire: false,
tripleShot: false,
shield: false
};
// Reposition player
player.x = canvas.width / 2;
player.y = canvas.height - 60;
// Set asteroid interval based on difficulty
asteroidInterval = difficulty === 'easy' ? 2000 : (difficulty === 'medium' ? 1500 : 1000);
// Hide screens
startScreen.style.display = 'none';
gameOverScreen.style.display = 'none';
pauseScreen.style.display = 'none';
// Start game
gameRunning = true;
gamePaused = false;
// Create stars
createStars();
// Play background music
try {
bgMusic.volume = 0.3;
bgMusic.currentTime = 0;
bgMusic.play();
} catch(e) {
console.log('Error playing music:', e);
}
// Start game loop
gameLoop();
}
// Game over
function gameOver() {
gameRunning = false;
finalScore.textContent = `Score: ${score}`;
// Update high score display
const highScoreValue = leaderboard.length > 0 ? Math.max(...leaderboard.map(e => e.score)) : 0;
highScore.textContent = `High Score: ${highScoreValue}`;
// Update leaderboard
updateLeaderboard();
// Show game over screen
gameOverScreen.style.display = 'flex';
// Stop background music
try {
bgMusic.pause();
} catch(e) {
console.log('Error stopping music:', e);
}
}
// Pause game
function pauseGame() {
if (!gameRunning) return;
gamePaused = !gamePaused;
if (gamePaused) {
pauseScreen.style.display = 'flex';
pauseScore.textContent = score;
// Pause background music
try {
bgMusic.pause();
} catch(e) {
console.log('Error pausing music:', e);
}
} else {
pauseScreen.style.display = 'none';
// Resume background music
try {
bgMusic.play();
} catch(e) {
console.log('Error resuming music:', e);
}
}
}
// Event listeners
startBtn.addEventListener('click', startGame);
restartBtn.addEventListener('click', startGame);
menuBtn.addEventListener('click', () => {
gameOverScreen.style.display = 'none';
startScreen.style.display = 'flex';
updateLeaderboard();
});
resumeBtn.addEventListener('click', pauseGame);
quitBtn.addEventListener('click', () => {
pauseScreen.style.display = 'none';
startScreen.style.display = 'flex';
gameRunning = false;
updateLeaderboard();
});
// Difficulty selection
difficultyBtns.forEach(btn => {
btn.addEventListener('click', () => {
difficultyBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
difficulty = btn.dataset.difficulty;
});
});
window.addEventListener('keydown', (e) => {
keys[e.key] = true;
if (e.key === ' ' && gameRunning && !gamePaused) {
player.shoot();
}
if (e.key === 'Escape') {
pauseGame();
}
// Prevent default for arrow keys and space
if (['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', ' '].includes(e.key)) {
e.preventDefault();
}
});
window.addEventListener('keyup', (e) => {
keys[e.key] = false;
});
// Handle window resize
window.addEventListener('resize', () => {
canvas.width = window.innerWidth * 0.9;
canvas.height = window.innerHeight * 0.9;
// Reposition player
player.x = canvas.width / 2;
player.y = canvas.height - 60;
// Recreate stars
createStars();
});
// Initialize leaderboard
updateLeaderboard();
// Start game loop (even when game not running to show start screen)
gameLoop();
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=SushilAI/space" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>