Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Cosmic Defender - Space Shooter Game</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); | |
| body { | |
| font-family: 'Press Start 2P', cursive; | |
| overflow: hidden; | |
| background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); | |
| } | |
| #gameCanvas { | |
| background-color: #000; | |
| border-radius: 8px; | |
| box-shadow: 0 0 30px rgba(0, 255, 255, 0.3); | |
| } | |
| .particle { | |
| position: absolute; | |
| width: 2px; | |
| height: 2px; | |
| background-color: white; | |
| border-radius: 50%; | |
| pointer-events: none; | |
| } | |
| .explosion { | |
| position: absolute; | |
| width: 30px; | |
| height: 30px; | |
| background: radial-gradient(circle, rgba(255,255,255,1) 0%, rgba(255,215,0,1) 30%, rgba(255,69,0,1) 70%, rgba(0,0,0,0) 100%); | |
| border-radius: 50%; | |
| pointer-events: none; | |
| animation: explode 0.5s forwards; | |
| } | |
| @keyframes explode { | |
| 0% { transform: scale(0.1); opacity: 1; } | |
| 100% { transform: scale(3); opacity: 0; } | |
| } | |
| .powerup { | |
| position: absolute; | |
| width: 20px; | |
| height: 20px; | |
| background: radial-gradient(circle, rgba(0,255,255,1) 0%, rgba(0,100,255,1) 100%); | |
| border-radius: 50%; | |
| box-shadow: 0 0 10px cyan; | |
| animation: pulse 1s infinite alternate; | |
| } | |
| @keyframes pulse { | |
| 0% { transform: scale(1); } | |
| 100% { transform: scale(1.2); } | |
| } | |
| .game-overlay { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| } | |
| .star { | |
| position: absolute; | |
| width: 2px; | |
| height: 2px; | |
| background-color: white; | |
| border-radius: 50%; | |
| animation: twinkle 2s infinite alternate; | |
| } | |
| @keyframes twinkle { | |
| 0% { opacity: 0.2; } | |
| 100% { opacity: 1; } | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen flex flex-col items-center justify-center text-white p-4"> | |
| <div class="relative w-full max-w-4xl"> | |
| <!-- Game Title --> | |
| <h1 class="text-4xl md:text-5xl text-center mb-4 text-cyan-400 drop-shadow-lg">COSMIC DEFENDER</h1> | |
| <!-- Game Stats --> | |
| <div class="flex justify-between mb-4 text-sm md:text-base"> | |
| <div class="bg-black bg-opacity-50 p-2 rounded"> | |
| <span class="text-green-400">SCORE:</span> <span id="score">0</span> | |
| </div> | |
| <div class="bg-black bg-opacity-50 p-2 rounded"> | |
| <span class="text-red-400">LIVES:</span> <span id="lives">3</span> | |
| </div> | |
| <div class="bg-black bg-opacity-50 p-2 rounded"> | |
| <span class="text-yellow-400">LEVEL:</span> <span id="level">1</span> | |
| </div> | |
| <div class="bg-black bg-opacity-50 p-2 rounded"> | |
| <span class="text-blue-400">POWER:</span> <span id="power">NORMAL</span> | |
| </div> | |
| </div> | |
| <!-- Game Canvas --> | |
| <div class="relative"> | |
| <canvas id="gameCanvas" width="800" height="500" class="w-full h-auto border-4 border-cyan-500"></canvas> | |
| <!-- Game Overlay (stars, particles, etc) --> | |
| <div id="gameOverlay" class="game-overlay"></div> | |
| <!-- Start Screen --> | |
| <div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-80"> | |
| <h2 class="text-3xl md:text-4xl mb-6 text-cyan-400">COSMIC DEFENDER</h2> | |
| <p class="mb-8 text-center px-4">Defend Earth from alien invaders!</p> | |
| <button id="startButton" class="bg-cyan-600 hover:bg-cyan-500 text-white font-bold py-3 px-8 rounded-full transition-all duration-300 transform hover:scale-105 pointer-events-auto"> | |
| START MISSION | |
| </button> | |
| <div class="mt-8 text-xs text-gray-400"> | |
| <p>CONTROLS: Arrow Keys to Move | Space to Shoot</p> | |
| <p class="mt-2">Collect power-ups for special abilities</p> | |
| </div> | |
| </div> | |
| <!-- Game Over Screen --> | |
| <div id="gameOverScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-80 hidden"> | |
| <h2 class="text-3xl md:text-4xl mb-6 text-red-400">MISSION FAILED</h2> | |
| <p class="mb-4">Final Score: <span id="finalScore" class="text-yellow-400">0</span></p> | |
| <p class="mb-8">Level Reached: <span id="finalLevel" class="text-blue-400">1</span></p> | |
| <button id="restartButton" class="bg-red-600 hover:bg-red-500 text-white font-bold py-3 px-8 rounded-full transition-all duration-300 transform hover:scale-105 pointer-events-auto"> | |
| TRY AGAIN | |
| </button> | |
| </div> | |
| <!-- Level Complete Screen --> | |
| <div id="levelCompleteScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-80 hidden"> | |
| <h2 class="text-3xl md:text-4xl mb-6 text-green-400">LEVEL COMPLETE!</h2> | |
| <p class="mb-8 text-xl">Preparing for level <span id="nextLevel" class="text-yellow-400">2</span></p> | |
| <div class="w-3/4 h-2 bg-gray-800 rounded-full overflow-hidden"> | |
| <div id="levelProgress" class="h-full bg-green-500 rounded-full" style="width: 0%"></div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Mobile Controls (visible only on mobile) --> | |
| <div id="mobileControls" class="mt-4 w-full max-w-md hidden"> | |
| <div class="grid grid-cols-3 gap-2"> | |
| <button id="leftBtn" class="bg-gray-800 hover:bg-gray-700 text-white p-4 rounded-lg col-start-1">←</button> | |
| <button id="shootBtn" class="bg-red-600 hover:bg-red-500 text-white p-4 rounded-lg col-start-2">FIRE</button> | |
| <button id="rightBtn" class="bg-gray-800 hover:bg-gray-700 text-white p-4 rounded-lg col-start-3">→</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Audio Elements --> | |
| <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-achievement-bell-600.mp3" preload="auto"></audio> | |
| <audio id="gameOverSound" src="https://assets.mixkit.co/sfx/preview/mixkit-retro-arcade-lose-2027.mp3" preload="auto"></audio> | |
| <audio id="levelCompleteSound" src="https://assets.mixkit.co/sfx/preview/mixkit-unlock-game-notification-253.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 levelCompleteScreen = document.getElementById('levelCompleteScreen'); | |
| const startButton = document.getElementById('startButton'); | |
| const restartButton = document.getElementById('restartButton'); | |
| const scoreElement = document.getElementById('score'); | |
| const livesElement = document.getElementById('lives'); | |
| const levelElement = document.getElementById('level'); | |
| const powerElement = document.getElementById('power'); | |
| const finalScoreElement = document.getElementById('finalScore'); | |
| const finalLevelElement = document.getElementById('finalLevel'); | |
| const nextLevelElement = document.getElementById('nextLevel'); | |
| const levelProgressElement = document.getElementById('levelProgress'); | |
| const mobileControls = document.getElementById('mobileControls'); | |
| const gameOverlay = document.getElementById('gameOverlay'); | |
| // Audio elements | |
| const shootSound = document.getElementById('shootSound'); | |
| const explosionSound = document.getElementById('explosionSound'); | |
| const powerupSound = document.getElementById('powerupSound'); | |
| const gameOverSound = document.getElementById('gameOverSound'); | |
| const levelCompleteSound = document.getElementById('levelCompleteSound'); | |
| // Game state | |
| let gameRunning = false; | |
| let score = 0; | |
| let lives = 3; | |
| let level = 1; | |
| let powerMode = 'NORMAL'; | |
| let powerTimer = 0; | |
| let enemySpeed = 1; | |
| let enemySpawnRate = 120; | |
| let powerupSpawnRate = 500; | |
| let frameCount = 0; | |
| // Player | |
| const player = { | |
| x: canvas.width / 2 - 25, | |
| y: canvas.height - 60, | |
| width: 50, | |
| height: 30, | |
| speed: 5, | |
| color: '#00FFFF', | |
| bullets: [], | |
| lastShot: 0, | |
| shootDelay: 300, | |
| isShooting: false | |
| }; | |
| // Enemies | |
| let enemies = []; | |
| // Powerups | |
| let powerups = []; | |
| // Explosions | |
| let explosions = []; | |
| // Stars for background | |
| let stars = []; | |
| // Create stars | |
| function createStars() { | |
| stars = []; | |
| for (let i = 0; i < 100; i++) { | |
| stars.push({ | |
| x: Math.random() * canvas.width, | |
| y: Math.random() * canvas.height, | |
| size: Math.random() * 2 + 1, | |
| opacity: Math.random() | |
| }); | |
| } | |
| } | |
| // Draw stars | |
| function drawStars() { | |
| ctx.save(); | |
| stars.forEach(star => { | |
| ctx.globalAlpha = star.opacity; | |
| ctx.fillStyle = 'white'; | |
| ctx.fillRect(star.x, star.y, star.size, star.size); | |
| }); | |
| ctx.restore(); | |
| } | |
| // Create a star DOM element for the overlay | |
| function createStarElement() { | |
| const star = document.createElement('div'); | |
| star.className = 'star'; | |
| star.style.left = `${Math.random() * 100}%`; | |
| star.style.top = `${Math.random() * 100}%`; | |
| star.style.animationDelay = `${Math.random() * 2}s`; | |
| star.style.width = `${Math.random() * 3 + 1}px`; | |
| star.style.height = star.style.width; | |
| gameOverlay.appendChild(star); | |
| // Remove star after animation completes | |
| setTimeout(() => { | |
| star.remove(); | |
| }, 2000); | |
| } | |
| // Create star elements for overlay | |
| function createStarElements() { | |
| setInterval(createStarElement, 100); | |
| } | |
| // Player bullet | |
| function createBullet() { | |
| const now = Date.now(); | |
| if (now - player.lastShot < player.shootDelay) return; | |
| player.bullets.push({ | |
| x: player.x + player.width / 2 - 2.5, | |
| y: player.y, | |
| width: 5, | |
| height: 15, | |
| speed: 7, | |
| color: '#00FF00' | |
| }); | |
| player.lastShot = now; | |
| // Play shoot sound | |
| shootSound.currentTime = 0; | |
| shootSound.play(); | |
| } | |
| // Enemy | |
| function createEnemy() { | |
| const size = Math.random() * 30 + 20; | |
| enemies.push({ | |
| x: Math.random() * (canvas.width - size), | |
| y: -size, | |
| width: size, | |
| height: size, | |
| speed: Math.random() * enemySpeed + 1, | |
| color: `hsl(${Math.random() * 60 + 300}, 100%, 50%)`, | |
| health: Math.floor(size / 10) | |
| }); | |
| } | |
| // Powerup | |
| function createPowerup() { | |
| powerups.push({ | |
| x: Math.random() * (canvas.width - 20), | |
| y: -20, | |
| width: 20, | |
| height: 20, | |
| speed: 2, | |
| type: Math.random() > 0.5 ? 'rapid' : 'shield' | |
| }); | |
| } | |
| // Explosion | |
| function createExplosion(x, y) { | |
| explosions.push({ | |
| x: x, | |
| y: y, | |
| radius: 5, | |
| maxRadius: 30, | |
| alpha: 1 | |
| }); | |
| // Play explosion sound | |
| explosionSound.currentTime = 0; | |
| explosionSound.play(); | |
| } | |
| // Draw player | |
| function drawPlayer() { | |
| ctx.fillStyle = player.color; | |
| // Ship body | |
| ctx.beginPath(); | |
| ctx.moveTo(player.x + player.width / 2, player.y); | |
| ctx.lineTo(player.x + player.width, player.y + player.height); | |
| ctx.lineTo(player.x, player.y + player.height); | |
| ctx.closePath(); | |
| ctx.fill(); | |
| // Ship cockpit | |
| ctx.fillStyle = '#FFFFFF'; | |
| ctx.beginPath(); | |
| ctx.arc(player.x + player.width / 2, player.y + player.height / 2, 5, 0, Math.PI * 2); | |
| ctx.fill(); | |
| // Draw shield if in power mode | |
| if (powerMode === 'SHIELD') { | |
| ctx.strokeStyle = 'rgba(0, 255, 255, 0.5)'; | |
| ctx.lineWidth = 2; | |
| ctx.beginPath(); | |
| ctx.arc(player.x + player.width / 2, player.y + player.height / 2, 35, 0, Math.PI * 2); | |
| ctx.stroke(); | |
| } | |
| } | |
| // Draw bullets | |
| function drawBullets() { | |
| player.bullets.forEach(bullet => { | |
| ctx.fillStyle = bullet.color; | |
| ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height); | |
| }); | |
| } | |
| // Draw enemies | |
| function drawEnemies() { | |
| enemies.forEach(enemy => { | |
| ctx.fillStyle = enemy.color; | |
| ctx.beginPath(); | |
| ctx.arc(enemy.x + enemy.width / 2, enemy.y + enemy.height / 2, enemy.width / 2, 0, Math.PI * 2); | |
| ctx.fill(); | |
| // Draw health indicator | |
| if (enemy.health > 1) { | |
| ctx.fillStyle = 'white'; | |
| ctx.font = '10px Arial'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText(enemy.health, enemy.x + enemy.width / 2, enemy.y + enemy.height / 2 + 3); | |
| } | |
| }); | |
| } | |
| // Draw powerups | |
| function drawPowerups() { | |
| powerups.forEach(powerup => { | |
| ctx.fillStyle = powerup.type === 'rapid' ? '#FF00FF' : '#00FFFF'; | |
| ctx.beginPath(); | |
| ctx.arc(powerup.x + powerup.width / 2, powerup.y + powerup.height / 2, powerup.width / 2, 0, Math.PI * 2); | |
| ctx.fill(); | |
| // Draw icon | |
| ctx.fillStyle = 'white'; | |
| ctx.font = '12px Arial'; | |
| ctx.textAlign = 'center'; | |
| ctx.fillText(powerup.type === 'rapid' ? 'R' : 'S', powerup.x + powerup.width / 2, powerup.y + powerup.height / 2 + 4); | |
| }); | |
| } | |
| // Draw explosions | |
| function drawExplosions() { | |
| explosions.forEach((explosion, index) => { | |
| if (explosion.radius < explosion.maxRadius) { | |
| explosion.radius += 2; | |
| explosion.alpha -= 0.02; | |
| } else { | |
| explosions.splice(index, 1); | |
| return; | |
| } | |
| const gradient = ctx.createRadialGradient( | |
| explosion.x, explosion.y, 0, | |
| explosion.x, explosion.y, explosion.radius | |
| ); | |
| gradient.addColorStop(0, 'rgba(255, 255, 255, ' + explosion.alpha + ')'); | |
| gradient.addColorStop(0.3, 'rgba(255, 215, 0, ' + explosion.alpha + ')'); | |
| gradient.addColorStop(0.6, 'rgba(255, 69, 0, ' + explosion.alpha * 0.7 + ')'); | |
| gradient.addColorStop(1, 'rgba(0, 0, 0, 0)'); | |
| ctx.fillStyle = gradient; | |
| ctx.beginPath(); | |
| ctx.arc(explosion.x, explosion.y, explosion.radius, 0, Math.PI * 2); | |
| ctx.fill(); | |
| }); | |
| } | |
| // Update bullets | |
| function updateBullets() { | |
| player.bullets.forEach((bullet, index) => { | |
| bullet.y -= bullet.speed; | |
| // Remove bullets that are off screen | |
| if (bullet.y + bullet.height < 0) { | |
| player.bullets.splice(index, 1); | |
| } | |
| }); | |
| } | |
| // Update enemies | |
| function updateEnemies() { | |
| enemies.forEach((enemy, index) => { | |
| enemy.y += enemy.speed; | |
| // Remove enemies that are off screen | |
| if (enemy.y > canvas.height) { | |
| enemies.splice(index, 1); | |
| loseLife(); | |
| } | |
| }); | |
| } | |
| // Update powerups | |
| function updatePowerups() { | |
| powerups.forEach((powerup, index) => { | |
| powerup.y += powerup.speed; | |
| // Remove powerups that are off screen | |
| if (powerup.y > canvas.height) { | |
| powerups.splice(index, 1); | |
| } | |
| }); | |
| } | |
| // Check collisions | |
| function checkCollisions() { | |
| // Bullet-enemy collisions | |
| player.bullets.forEach((bullet, bulletIndex) => { | |
| enemies.forEach((enemy, enemyIndex) => { | |
| if ( | |
| bullet.x < enemy.x + enemy.width && | |
| bullet.x + bullet.width > enemy.x && | |
| bullet.y < enemy.y + enemy.height && | |
| bullet.y + bullet.height > enemy.y | |
| ) { | |
| // Hit enemy | |
| enemy.health--; | |
| // Remove bullet | |
| player.bullets.splice(bulletIndex, 1); | |
| // Create explosion | |
| createExplosion(enemy.x + enemy.width / 2, enemy.y + enemy.height / 2); | |
| // If enemy health is 0, remove it and add score | |
| if (enemy.health <= 0) { | |
| enemies.splice(enemyIndex, 1); | |
| addScore(Math.floor(enemy.width)); | |
| } | |
| return; | |
| } | |
| }); | |
| }); | |
| // Player-enemy collisions | |
| enemies.forEach((enemy, index) => { | |
| if ( | |
| player.x < enemy.x + enemy.width && | |
| player.x + player.width > enemy.x && | |
| player.y < enemy.y + enemy.height && | |
| player.y + player.height > enemy.y | |
| ) { | |
| // Only lose life if not in SHIELD mode | |
| if (powerMode !== 'SHIELD') { | |
| enemies.splice(index, 1); | |
| loseLife(); | |
| createExplosion(enemy.x + enemy.width / 2, enemy.y + enemy.height / 2); | |
| } else { | |
| // Just destroy the enemy if in SHIELD mode | |
| enemies.splice(index, 1); | |
| addScore(Math.floor(enemy.width)); | |
| createExplosion(enemy.x + enemy.width / 2, enemy.y + enemy.height / 2); | |
| } | |
| } | |
| }); | |
| // Player-powerup collisions | |
| powerups.forEach((powerup, index) => { | |
| if ( | |
| player.x < powerup.x + powerup.width && | |
| player.x + player.width > powerup.x && | |
| player.y < powerup.y + powerup.height && | |
| player.y + player.height > powerup.y | |
| ) { | |
| // Apply powerup | |
| if (powerup.type === 'rapid') { | |
| activatePowerMode('RAPID'); | |
| } else { | |
| activatePowerMode('SHIELD'); | |
| } | |
| // Remove powerup | |
| powerups.splice(index, 1); | |
| // Play powerup sound | |
| powerupSound.currentTime = 0; | |
| powerupSound.play(); | |
| } | |
| }); | |
| } | |
| // Add score | |
| function addScore(points) { | |
| score += points; | |
| scoreElement.textContent = score; | |
| } | |
| // Lose life | |
| function loseLife() { | |
| lives--; | |
| livesElement.textContent = lives; | |
| if (lives <= 0) { | |
| gameOver(); | |
| } else { | |
| // Reset player position | |
| player.x = canvas.width / 2 - 25; | |
| player.y = canvas.height - 60; | |
| } | |
| } | |
| // Activate power mode | |
| function activatePowerMode(type) { | |
| powerMode = type; | |
| powerTimer = 500; // 5 seconds at 60fps | |
| if (type === 'RAPID') { | |
| player.shootDelay = 100; | |
| powerElement.textContent = 'RAPID FIRE'; | |
| powerElement.className = 'text-purple-400'; | |
| } else { | |
| powerElement.textContent = 'SHIELD'; | |
| powerElement.className = 'text-cyan-400'; | |
| } | |
| } | |
| // Update power mode timer | |
| function updatePowerMode() { | |
| if (powerTimer > 0) { | |
| powerTimer--; | |
| if (powerTimer === 0) { | |
| powerMode = 'NORMAL'; | |
| player.shootDelay = 300; | |
| powerElement.textContent = 'NORMAL'; | |
| powerElement.className = 'text-white'; | |
| } | |
| } | |
| } | |
| // Level up | |
| function levelUp() { | |
| level++; | |
| levelElement.textContent = level; | |
| // Increase difficulty | |
| enemySpeed += 0.2; | |
| if (enemySpawnRate > 30) enemySpawnRate -= 10; | |
| // Show level complete screen | |
| showLevelComplete(); | |
| } | |
| // Show level complete screen | |
| function showLevelComplete() { | |
| gameRunning = false; | |
| levelCompleteScreen.classList.remove('hidden'); | |
| nextLevelElement.textContent = level; | |
| // Play level complete sound | |
| levelCompleteSound.currentTime = 0; | |
| levelCompleteSound.play(); | |
| // Animate progress bar | |
| let progress = 0; | |
| const interval = setInterval(() => { | |
| progress += 2; | |
| levelProgressElement.style.width = `${progress}%`; | |
| if (progress >= 100) { | |
| clearInterval(interval); | |
| setTimeout(() => { | |
| levelCompleteScreen.classList.add('hidden'); | |
| gameRunning = true; | |
| }, 500); | |
| } | |
| }, 50); | |
| } | |
| // Game over | |
| function gameOver() { | |
| gameRunning = false; | |
| gameOverScreen.classList.remove('hidden'); | |
| finalScoreElement.textContent = score; | |
| finalLevelElement.textContent = level - 1; | |
| // Play game over sound | |
| gameOverSound.currentTime = 0; | |
| gameOverSound.play(); | |
| } | |
| // Reset game | |
| function resetGame() { | |
| score = 0; | |
| lives = 3; | |
| level = 1; | |
| powerMode = 'NORMAL'; | |
| powerTimer = 0; | |
| enemySpeed = 1; | |
| enemySpawnRate = 120; | |
| player.x = canvas.width / 2 - 25; | |
| player.y = canvas.height - 60; | |
| player.bullets = []; | |
| player.lastShot = 0; | |
| player.shootDelay = 300; | |
| enemies = []; | |
| powerups = []; | |
| explosions = []; | |
| scoreElement.textContent = score; | |
| livesElement.textContent = lives; | |
| levelElement.textContent = level; | |
| powerElement.textContent = powerMode; | |
| powerElement.className = 'text-white'; | |
| gameOverScreen.classList.add('hidden'); | |
| startScreen.classList.add('hidden'); | |
| gameRunning = true; | |
| } | |
| // Game loop | |
| function gameLoop() { | |
| if (!gameRunning) return; | |
| // Clear canvas | |
| ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| // Draw background | |
| drawStars(); | |
| // Spawn enemies | |
| if (frameCount % enemySpawnRate === 0) { | |
| createEnemy(); | |
| } | |
| // Spawn powerups | |
| if (frameCount % powerupSpawnRate === 0) { | |
| createPowerup(); | |
| } | |
| // Auto-shoot if in rapid fire mode and player is shooting | |
| if (powerMode === 'RAPID' && player.isShooting && frameCount % 10 === 0) { | |
| createBullet(); | |
| } | |
| // Update game objects | |
| updateBullets(); | |
| updateEnemies(); | |
| updatePowerups(); | |
| updatePowerMode(); | |
| checkCollisions(); | |
| // Draw game objects | |
| drawBullets(); | |
| drawEnemies(); | |
| drawPowerups(); | |
| drawExplosions(); | |
| drawPlayer(); | |
| // Check for level up (every 10 enemies destroyed) | |
| if (score >= level * 1000) { | |
| levelUp(); | |
| } | |
| frameCount++; | |
| requestAnimationFrame(gameLoop); | |
| } | |
| // Event listeners | |
| startButton.addEventListener('click', () => { | |
| startScreen.classList.add('hidden'); | |
| resetGame(); | |
| gameLoop(); | |
| }); | |
| restartButton.addEventListener('click', resetGame); | |
| // Keyboard controls | |
| document.addEventListener('keydown', (e) => { | |
| if (!gameRunning) return; | |
| switch (e.key) { | |
| case 'ArrowLeft': | |
| player.x = Math.max(0, player.x - player.speed); | |
| break; | |
| case 'ArrowRight': | |
| player.x = Math.min(canvas.width - player.width, player.x + player.speed); | |
| break; | |
| case ' ': | |
| player.isShooting = true; | |
| createBullet(); | |
| break; | |
| } | |
| }); | |
| document.addEventListener('keyup', (e) => { | |
| if (e.key === ' ') { | |
| player.isShooting = false; | |
| } | |
| }); | |
| // Touch controls for mobile | |
| let touchStartX = 0; | |
| let touchEndX = 0; | |
| canvas.addEventListener('touchstart', (e) => { | |
| e.preventDefault(); | |
| touchStartX = e.touches[0].clientX; | |
| player.isShooting = true; | |
| createBullet(); | |
| }); | |
| canvas.addEventListener('touchmove', (e) => { | |
| e.preventDefault(); | |
| touchEndX = e.touches[0].clientX; | |
| if (touchEndX < touchStartX - 10) { | |
| // Swipe left | |
| player.x = Math.max(0, player.x - player.speed * 2); | |
| touchStartX = touchEndX; | |
| } else if (touchEndX > touchStartX + 10) { | |
| // Swipe right | |
| player.x = Math.min(canvas.width - player.width, player.x + player.speed * 2); | |
| touchStartX = touchEndX; | |
| } | |
| }); | |
| canvas.addEventListener('touchend', (e) => { | |
| e.preventDefault(); | |
| player.isShooting = false; | |
| }); | |
| // Mobile button controls | |
| leftBtn.addEventListener('touchstart', () => { | |
| player.x = Math.max(0, player.x - player.speed); | |
| }); | |
| rightBtn.addEventListener('touchstart', () => { | |
| player.x = Math.min(canvas.width - player.width, player.x + player.speed); | |
| }); | |
| shootBtn.addEventListener('touchstart', () => { | |
| player.isShooting = true; | |
| createBullet(); | |
| }); | |
| shootBtn.addEventListener('touchend', () => { | |
| player.isShooting = false; | |
| }); | |
| // Check if mobile device | |
| function isMobile() { | |
| return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); | |
| } | |
| // Initialize game | |
| function init() { | |
| createStars(); | |
| createStarElements(); | |
| if (isMobile()) { | |
| mobileControls.classList.remove('hidden'); | |
| } | |
| } | |
| // Start the game | |
| init(); | |
| </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=mycosoft/mycosoftgame" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |