Spaces:
Running
Running
<html lang="pt-br"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Cosmic Defender PRO</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> | |
@keyframes pulse { | |
0%, 100% { transform: scale(1); } | |
50% { transform: scale(1.05); } | |
} | |
.pulse { animation: pulse 1.5s infinite; } | |
.star { | |
position: absolute; | |
background: white; | |
border-radius: 50%; | |
animation: twinkle var(--duration) infinite ease-in-out; | |
} | |
@keyframes twinkle { | |
0%, 100% { opacity: 0.2; } | |
50% { opacity: 1; } | |
} | |
#gameCanvas { | |
box-shadow: 0 0 30px rgba(0, 100, 255, 0.5); | |
border-radius: 10px; | |
} | |
.explosion { | |
position: absolute; | |
width: 40px; | |
height: 40px; | |
background: radial-gradient(circle, yellow, orange, red); | |
border-radius: 50%; | |
opacity: 0; | |
transform: scale(0); | |
animation: explode 0.5s forwards; | |
} | |
@keyframes explode { | |
0% { transform: scale(0); opacity: 1; } | |
100% { transform: scale(3); opacity: 0; } | |
} | |
.powerup { | |
position: absolute; | |
width: 20px; | |
height: 20px; | |
background: radial-gradient(circle, #10b981, #047857); | |
border-radius: 50%; | |
animation: float 2s infinite ease-in-out; | |
} | |
@keyframes float { | |
0%, 100% { transform: translateY(0); } | |
50% { transform: translateY(-10px); } | |
} | |
.shield { | |
position: absolute; | |
width: 60px; | |
height: 60px; | |
border: 2px dashed rgba(59, 130, 246, 0.7); | |
border-radius: 50%; | |
animation: shieldPulse 2s infinite; | |
} | |
@keyframes shieldPulse { | |
0%, 100% { transform: scale(1); opacity: 0.7; } | |
50% { transform: scale(1.1); opacity: 0.4; } | |
} | |
.boss-health { | |
height: 20px; | |
background: linear-gradient(to right, #ef4444, #f59e0b); | |
border-radius: 10px; | |
transition: width 0.3s; | |
} | |
</style> | |
</head> | |
<body class="bg-gray-900 text-white flex flex-col items-center justify-center min-h-screen p-4 overflow-hidden"> | |
<!-- Background Elements --> | |
<div class="absolute inset-0 overflow-hidden"> | |
<div id="stars"></div> | |
</div> | |
<!-- Game UI --> | |
<div class="z-10 w-full max-w-4xl"> | |
<!-- Header --> | |
<header class="flex justify-between items-center mb-4"> | |
<div class="text-2xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-600"> | |
COSMIC DEFENDER <span class="text-xs bg-blue-600 text-white px-2 py-1 rounded">PRO</span> | |
</div> | |
<div class="flex space-x-6"> | |
<div class="text-lg font-mono"> | |
<i class="fas fa-star text-yellow-400 mr-1"></i> <span id="score">0</span> | |
</div> | |
<div class="text-lg font-mono"> | |
<i class="fas fa-heart text-red-500 mr-1"></i> <span id="lives">3</span> | |
</div> | |
<div class="text-lg font-mono"> | |
<i class="fas fa-bolt text-yellow-400 mr-1"></i> <span id="level">1</span> | |
</div> | |
</div> | |
</header> | |
<!-- Game Container --> | |
<div class="relative"> | |
<canvas id="gameCanvas" width="800" height="600" class="w-full border-2 border-blue-500"></canvas> | |
<!-- Start Screen --> | |
<div id="startScreen" class="absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-90 rounded-lg p-6 text-center"> | |
<h2 class="text-5xl font-bold mb-2 bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-600"> | |
COSMIC DEFENDER PRO | |
</h2> | |
<p class="text-xl mb-8 text-gray-300">Defend the galaxy from alien invasion!</p> | |
<div class="grid grid-cols-2 gap-8 mb-8"> | |
<div class="bg-gray-800 p-4 rounded-lg"> | |
<h3 class="text-blue-400 font-semibold mb-2"><i class="fas fa-gamepad mr-2"></i>Controls</h3> | |
<ul class="text-left space-y-2"> | |
<li><i class="fas fa-arrow-left mr-2 text-blue-400"></i> Move Left</li> | |
<li><i class="fas fa-arrow-right mr-2 text-blue-400"></i> Move Right</li> | |
<li><i class="fas fa-space-shuttle mr-2 text-blue-400"></i> Shoot (Space)</li> | |
<li><i class="fas fa-bolt mr-2 text-blue-400"></i> Special (Shift)</li> | |
</ul> | |
</div> | |
<div class="bg-gray-800 p-4 rounded-lg"> | |
<h3 class="text-purple-400 font-semibold mb-2"><i class="fas fa-trophy mr-2"></i>Powerups</h3> | |
<ul class="text-left space-y-2"> | |
<li><span class="w-3 h-3 bg-green-500 rounded-full inline-block mr-2"></span> Health</li> | |
<li><span class="w-3 h-3 bg-blue-500 rounded-full inline-block mr-2"></span> Shield</li> | |
<li><span class="w-3 h-3 bg-yellow-500 rounded-full inline-block mr-2"></span> Rapid Fire</li> | |
<li><span class="w-3 h-3 bg-red-500 rounded-full inline-block mr-2"></span> Nuke</li> | |
</ul> | |
</div> | |
</div> | |
<div class="flex space-x-4"> | |
<button id="startBtn" class="px-8 py-3 bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 rounded-lg text-xl font-semibold transition-all pulse"> | |
<i class="fas fa-play mr-2"></i>Start Mission | |
</button> | |
<button id="settingsBtn" class="px-8 py-3 bg-gray-700 hover:bg-gray-600 rounded-lg text-xl font-semibold transition-all"> | |
<i class="fas fa-cog mr-2"></i>Settings | |
</button> | |
</div> | |
</div> | |
<!-- Game Over Screen --> | |
<div id="gameOver" class="hidden absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-90 rounded-lg p-6"> | |
<h2 class="text-5xl font-bold text-red-500 mb-4">MISSION FAILED</h2> | |
<div class="bg-gray-800 p-6 rounded-lg mb-6 w-64"> | |
<div class="text-2xl font-mono mb-2">Score: <span id="finalScore">0</span></div> | |
<div class="text-lg mb-4">Level Reached: <span id="finalLevel">1</span></div> | |
<div class="text-lg">Enemies Defeated: <span id="enemiesDefeated">0</span></div> | |
</div> | |
<div class="flex space-x-4"> | |
<button id="restartBtn" class="px-6 py-2 bg-blue-600 hover:bg-blue-700 rounded-lg text-lg font-semibold transition-all"> | |
<i class="fas fa-redo mr-2"></i>Retry | |
</button> | |
<button id="menuBtn" class="px-6 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg text-lg font-semibold transition-all"> | |
<i class="fas fa-home mr-2"></i>Main Menu | |
</button> | |
</div> | |
</div> | |
<!-- Pause Screen --> | |
<div id="pauseScreen" class="hidden absolute inset-0 flex flex-col items-center justify-center bg-black bg-opacity-90 rounded-lg"> | |
<h2 class="text-4xl font-bold text-yellow-400 mb-6">MISSION PAUSED</h2> | |
<button id="resumeBtn" class="px-8 py-3 bg-green-600 hover:bg-green-700 rounded-lg text-xl font-semibold transition-all mb-4"> | |
<i class="fas fa-play mr-2"></i>Resume | |
</button> | |
<button id="quitBtn" class="px-8 py-3 bg-gray-700 hover:bg-gray-600 rounded-lg text-xl font-semibold transition-all"> | |
<i class="fas fa-sign-out-alt mr-2"></i>Quit Mission | |
</button> | |
</div> | |
<!-- Boss Health Bar --> | |
<div id="bossHealthContainer" class="hidden absolute top-4 left-1/2 transform -translate-x-1/2 w-64 bg-gray-800 rounded-lg p-2"> | |
<div class="text-center text-yellow-400 font-bold mb-1">BOSS</div> | |
<div class="boss-health" id="bossHealthBar"></div> | |
</div> | |
</div> | |
<!-- Footer --> | |
<footer class="mt-4 text-center text-gray-500 text-sm"> | |
<p>Use arrow keys to move, SPACE to shoot, SHIFT for special attack | P to pause</p> | |
<p class="mt-1">© 2023 Cosmic Defender PRO | All rights reserved</p> | |
</footer> | |
</div> | |
<script> | |
// ============================================= | |
// GAME INITIALIZATION | |
// ============================================= | |
// Create stars background | |
const starsContainer = document.getElementById('stars'); | |
for (let i = 0; i < 200; i++) { | |
const star = document.createElement('div'); | |
star.className = 'star'; | |
star.style.left = `${Math.random() * 100}%`; | |
star.style.top = `${Math.random() * 100}%`; | |
star.style.width = `${Math.random() * 3}px`; | |
star.style.height = star.style.width; | |
star.style.setProperty('--duration', `${2 + Math.random() * 3}s`); | |
starsContainer.appendChild(star); | |
} | |
// Game canvas setup | |
const canvas = document.getElementById('gameCanvas'); | |
const ctx = canvas.getContext('2d'); | |
canvas.width = 800; | |
canvas.height = 600; | |
// UI Elements | |
const scoreDisplay = document.getElementById('score'); | |
const finalScoreDisplay = document.getElementById('finalScore'); | |
const livesDisplay = document.getElementById('lives'); | |
const levelDisplay = document.getElementById('level'); | |
const finalLevelDisplay = document.getElementById('finalLevel'); | |
const enemiesDefeatedDisplay = document.getElementById('enemiesDefeated'); | |
const gameOverScreen = document.getElementById('gameOver'); | |
const startScreen = document.getElementById('startScreen'); | |
const pauseScreen = document.getElementById('pauseScreen'); | |
const bossHealthContainer = document.getElementById('bossHealthContainer'); | |
const bossHealthBar = document.getElementById('bossHealthBar'); | |
// Buttons | |
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 settingsBtn = document.getElementById('settingsBtn'); | |
// ============================================= | |
// GAME STATE | |
// ============================================= | |
// Player | |
let player = { | |
x: canvas.width / 2 - 25, | |
y: canvas.height - 80, | |
w: 50, | |
h: 60, | |
speed: 5, | |
fireRate: 300, // ms between shots | |
lastShot: 0, | |
specialCharge: 0, | |
maxSpecial: 100, | |
hasShield: false, | |
shieldTimer: 0, | |
rapidFire: false, | |
rapidFireTimer: 0 | |
}; | |
// Game objects | |
let shots = []; | |
let enemies = []; | |
let powerups = []; | |
let explosions = []; | |
let particles = []; | |
let boss = null; | |
// Game state | |
let score = 0; | |
let lives = 3; | |
let level = 1; | |
let enemiesDefeated = 0; | |
let gameOver = false; | |
let gameStarted = false; | |
let gamePaused = false; | |
let keys = {}; | |
let enemySpawnRate = 1500; // ms | |
let enemySpeed = 2; | |
let lastEnemySpawn = 0; | |
let lastPowerupSpawn = 0; | |
let powerupSpawnRate = 10000; // ms | |
let backgroundOffset = 0; | |
let parallaxStars = []; | |
// Create parallax stars | |
for (let i = 0; i < 100; i++) { | |
parallaxStars.push({ | |
x: Math.random() * canvas.width, | |
y: Math.random() * canvas.height, | |
size: Math.random() * 3, | |
speed: 0.5 + Math.random() * 2, | |
alpha: 0.2 + Math.random() * 0.8 | |
}); | |
} | |
// ============================================= | |
// EVENT LISTENERS | |
// ============================================= | |
document.addEventListener('keydown', (e) => { | |
keys[e.key] = true; | |
// Start game with space if not started | |
if (e.key === ' ' && !gameStarted && !gameOver) { | |
startGame(); | |
} | |
// Pause game | |
if (e.key === 'p' && gameStarted && !gameOver) { | |
togglePause(); | |
} | |
}); | |
document.addEventListener('keyup', (e) => { | |
keys[e.key] = false; | |
}); | |
// Button event listeners | |
startBtn.addEventListener('click', startGame); | |
restartBtn.addEventListener('click', restartGame); | |
menuBtn.addEventListener('click', returnToMenu); | |
resumeBtn.addEventListener('click', togglePause); | |
quitBtn.addEventListener('click', returnToMenu); | |
settingsBtn.addEventListener('click', () => { | |
alert('Settings will be available in the next update!'); | |
}); | |
// ============================================= | |
// GAME FUNCTIONS | |
// ============================================= | |
function startGame() { | |
gameStarted = true; | |
gameOver = false; | |
startScreen.classList.add('hidden'); | |
resetGame(); | |
gameLoop(); | |
} | |
function restartGame() { | |
gameOver = false; | |
gameOverScreen.classList.add('hidden'); | |
resetGame(); | |
gameLoop(); | |
} | |
function returnToMenu() { | |
gameOver = false; | |
gamePaused = false; | |
gameOverScreen.classList.add('hidden'); | |
pauseScreen.classList.add('hidden'); | |
startScreen.classList.remove('hidden'); | |
} | |
function togglePause() { | |
gamePaused = !gamePaused; | |
if (gamePaused) { | |
pauseScreen.classList.remove('hidden'); | |
} else { | |
pauseScreen.classList.add('hidden'); | |
} | |
} | |
function resetGame() { | |
// Reset player | |
player = { | |
x: canvas.width / 2 - 25, | |
y: canvas.height - 80, | |
w: 50, | |
h: 60, | |
speed: 5, | |
fireRate: 300, | |
lastShot: 0, | |
specialCharge: 0, | |
maxSpecial: 100, | |
hasShield: false, | |
shieldTimer: 0, | |
rapidFire: false, | |
rapidFireTimer: 0 | |
}; | |
// Reset game objects | |
shots = []; | |
enemies = []; | |
powerups = []; | |
explosions = []; | |
particles = []; | |
boss = null; | |
bossHealthContainer.classList.add('hidden'); | |
// Reset game state | |
score = 0; | |
lives = 3; | |
level = 1; | |
enemiesDefeated = 0; | |
enemySpawnRate = 1500; | |
enemySpeed = 2; | |
lastEnemySpawn = 0; | |
lastPowerupSpawn = 0; | |
powerupSpawnRate = 10000; | |
// Update UI | |
updateUI(); | |
} | |
function updateUI() { | |
scoreDisplay.textContent = score; | |
livesDisplay.textContent = lives; | |
levelDisplay.textContent = level; | |
finalScoreDisplay.textContent = score; | |
finalLevelDisplay.textContent = level; | |
enemiesDefeatedDisplay.textContent = enemiesDefeated; | |
} | |
function createExplosion(x, y, size = 1) { | |
// Create visual explosion | |
const explosion = document.createElement('div'); | |
explosion.className = 'explosion'; | |
explosion.style.left = `${x - 20}px`; | |
explosion.style.top = `${y - 20}px`; | |
explosion.style.transform = `scale(${size})`; | |
document.body.appendChild(explosion); | |
// Remove after animation | |
setTimeout(() => { | |
explosion.remove(); | |
}, 500); | |
// Create particles | |
for (let i = 0; i < 15 * size; i++) { | |
particles.push({ | |
x: x, | |
y: y, | |
size: 2 + Math.random() * 3 * size, | |
color: `hsl(${Math.random() * 30 + 10}, 100%, 50%)`, | |
speedX: -5 + Math.random() * 10, | |
speedY: -5 + Math.random() * 10, | |
life: 30 + Math.random() * 30 | |
}); | |
} | |
} | |
function spawnEnemy() { | |
const now = Date.now(); | |
if (now - lastEnemySpawn < enemySpawnRate || gameOver || gamePaused) return; | |
lastEnemySpawn = now; | |
// Every 5 levels, spawn a boss | |
if (level % 5 === 0 && !boss) { | |
spawnBoss(); | |
return; | |
} | |
// Regular enemies | |
const colors = ['#ef4444', '#f97316', '#ec4899', '#8b5cf6']; | |
const types = ['basic', 'fast', 'tank', 'shooter']; | |
const type = types[Math.floor(Math.random() * types.length)]; | |
let enemy = { | |
x: Math.random() * (canvas.width - 40), | |
y: -40, | |
w: 40, | |
h: 40, | |
color: colors[Math.floor(Math.random() * colors.length)], | |
type: type, | |
health: type === 'tank' ? 3 : 1, | |
speed: enemySpeed * (type === 'fast' ? 1.5 : 1), | |
lastShot: 0, | |
canShoot: type === 'shooter' | |
}; | |
enemies.push(enemy); | |
} | |
function spawnBoss() { | |
boss = { | |
x: canvas.width / 2 - 100, | |
y: -150, | |
w: 200, | |
h: 150, | |
color: '#dc2626', | |
health: 10 + level * 2, | |
maxHealth: 10 + level * 2, | |
speed: enemySpeed * 0.7, | |
phase: 1, | |
lastShot: 0, | |
pattern: 'down', | |
moveTimer: 0 | |
}; | |
bossHealthContainer.classList.remove('hidden'); | |
updateBossHealth(); | |
} | |
function updateBossHealth() { | |
const percent = (boss.health / boss.maxHealth) * 100; | |
bossHealthBar.style.width = `${percent}%`; | |
} | |
function spawnPowerup() { | |
const now = Date.now(); | |
if (now - lastPowerupSpawn < powerupSpawnRate || gameOver || gamePaused) return; | |
lastPowerupSpawn = now; | |
const types = ['health', 'shield', 'rapidfire', 'nuke']; | |
const type = types[Math.floor(Math.random() * types.length)]; | |
powerups.push({ | |
x: Math.random() * (canvas.width - 20), | |
y: -20, | |
w: 20, | |
h: 20, | |
type: type, | |
color: type === 'health' ? '#10b981' : | |
type === 'shield' ? '#3b82f6' : | |
type === 'rapidfire' ? '#f59e0b' : '#ef4444', | |
speed: 2 | |
}); | |
} | |
function activatePowerup(powerup) { | |
switch(powerup.type) { | |
case 'health': | |
lives = Math.min(5, lives + 1); | |
createExplosion(powerup.x + powerup.w/2, powerup.y + powerup.h/2, 0.5); | |
break; | |
case 'shield': | |
player.hasShield = true; | |
player.shieldTimer = 10000; // 10 seconds | |
createExplosion(powerup.x + powerup.w/2, powerup.y + powerup.h/2, 0.5); | |
break; | |
case 'rapidfire': | |
player.rapidFire = true; | |
player.rapidFireTimer = 8000; // 8 seconds | |
player.fireRate = 100; | |
createExplosion(powerup.x + powerup.w/2, powerup.y + powerup.h/2, 0.5); | |
break; | |
case 'nuke': | |
// Destroy all enemies | |
enemies.forEach(enemy => { | |
createExplosion(enemy.x + enemy.w/2, enemy.y + enemy.h/2, 1.5); | |
score += 10 * level; | |
enemiesDefeated++; | |
}); | |
enemies = []; | |
if (boss) { | |
boss.health -= 5; | |
updateBossHealth(); | |
createExplosion(boss.x + boss.w/2, boss.y + boss.h/2, 3); | |
if (boss.health <= 0) { | |
defeatBoss(); | |
} | |
} | |
break; | |
} | |
updateUI(); | |
} | |
function defeatBoss() { | |
createExplosion(boss.x + boss.w/2, boss.y + boss.h/2, 4); | |
score += 500 * level; | |
enemiesDefeated++; | |
boss = null; | |
bossHealthContainer.classList.add('hidden'); | |
level++; | |
enemySpawnRate = Math.max(500, enemySpawnRate - 100); | |
enemySpeed = Math.min(6, enemySpeed + 0.3); | |
updateUI(); | |
} | |
function shoot() { | |
const now = Date.now(); | |
if (now - player.lastShot < player.fireRate) return; | |
player.lastShot = now; | |
// Main shot | |
shots.push({ | |
x: player.x + player.w/2 - 2, | |
y: player.y, | |
w: 4, | |
h: 15, | |
color: '#10b981', | |
speed: 8, | |
damage: 1 | |
}); | |
// Side shots when special is charged | |
if (player.specialCharge >= player.maxSpecial) { | |
player.specialCharge = 0; | |
// Left shot | |
shots.push({ | |
x: player.x + 10, | |
y: player.y + 20, | |
w: 6, | |
h: 20, | |
color: '#3b82f6', | |
speed: 10, | |
damage: 2, | |
angle: -0.2 | |
}); | |
// Right shot | |
shots.push({ | |
x: player.x + player.w - 16, | |
y: player.y + 20, | |
w: 6, | |
h: 20, | |
color: '#3b82f6', | |
speed: 10, | |
damage: 2, | |
angle: 0.2 | |
}); | |
} | |
} | |
function specialAttack() { | |
if (player.specialCharge < player.maxSpecial) return; | |
player.specialCharge = 0; | |
// Create a powerful wave attack | |
for (let i = 0; i < 10; i++) { | |
shots.push({ | |
x: player.x + player.w/2 - 3 + (i * 6) - 27, | |
y: player.y, | |
w: 6, | |
h: 25, | |
color: '#ec4899', | |
speed: 12, | |
damage: 3 | |
}); | |
} | |
} | |
function enemyShoot(enemy) { | |
const now = Date.now(); | |
if (now - enemy.lastShot < 2000 || !enemy.canShoot) return; | |
enemy.lastShot = now; | |
shots.push({ | |
x: enemy.x + enemy.w/2 - 3, | |
y: enemy.y + enemy.h, | |
w: 6, | |
h: 15, | |
color: '#ef4444', | |
speed: -5, | |
damage: 1, | |
isEnemy: true | |
}); | |
} | |
function bossShoot() { | |
const now = Date.now(); | |
if (now - boss.lastShot < 1000) return; | |
boss.lastShot = now; | |
// Pattern based on boss phase | |
if (boss.phase === 1) { | |
// Single shot | |
shots.push({ | |
x: boss.x + boss.w/2 - 5, | |
y: boss.y + boss.h, | |
w: 10, | |
h: 25, | |
color: '#ef4444', | |
speed: -4, | |
damage: 2, | |
isEnemy: true | |
}); | |
} else if (boss.phase === 2) { | |
// Triple shot | |
for (let i = 0; i < 3; i++) { | |
shots.push({ | |
x: boss.x + boss.w/2 - 5 + (i * 15) - 15, | |
y: boss.y + boss.h, | |
w: 10, | |
h: 25, | |
color: '#ef4444', | |
speed: -4, | |
damage: 2, | |
isEnemy: true, | |
angle: (i - 1) * 0.2 | |
}); | |
} | |
} else { | |
// Spread shot | |
for (let i = 0; i < 5; i++) { | |
shots.push({ | |
x: boss.x + boss.w/2 - 5 + (i * 15) - 30, | |
y: boss.y + boss.h, | |
w: 10, | |
h: 25, | |
color: '#ef4444', | |
speed: -4, | |
damage: 2, | |
isEnemy: true, | |
angle: (i - 2) * 0.15 | |
}); | |
} | |
} | |
} | |
function checkCollisions() { | |
// Player with enemies | |
enemies.forEach((enemy, i) => { | |
if (collision(player, enemy)) { | |
if (player.hasShield) { | |
player.hasShield = false; | |
player.shieldTimer = 0; | |
} else { | |
lives--; | |
if (lives <= 0) { | |
gameOver = true; | |
} | |
} | |
createExplosion(enemy.x + enemy.w/2, enemy.y + enemy.h/2); | |
enemies.splice(i, 1); | |
updateUI(); | |
} | |
}); | |
// Player with boss | |
if (boss && collision(player, boss)) { | |
if (player.hasShield) { | |
player.hasShield = false; | |
player.shieldTimer = 0; | |
} else { | |
lives -= 2; | |
if (lives <= 0) { | |
gameOver = true; | |
} | |
} | |
createExplosion(boss.x + boss.w/2, boss.y + boss.h/2, 2); | |
updateUI(); | |
} | |
// Player with enemy shots | |
shots.forEach((shot, i) => { | |
if (shot.isEnemy && collision(player, shot)) { | |
if (player.hasShield) { | |
player.hasShield = false; | |
player.shieldTimer = 0; | |
} else { | |
lives--; | |
if (lives <= 0) { | |
gameOver = true; | |
} | |
} | |
shots.splice(i, 1); | |
updateUI(); | |
} | |
}); | |
// Player with powerups | |
powerups.forEach((powerup, i) => { | |
if (collision(player, powerup)) { | |
activatePowerup(powerup); | |
powerups.splice(i, 1); | |
} | |
}); | |
// Player shots with enemies | |
shots.forEach((shot, i) => { | |
if (!shot.isEnemy) { | |
enemies.forEach((enemy, j) => { | |
if (collision(shot, enemy)) { | |
enemy.health -= shot.damage; | |
shots.splice(i, 1); | |
if (enemy.health <= 0) { | |
createExplosion(enemy.x + enemy.w/2, enemy.y + enemy.h/2); | |
enemies.splice(j, 1); | |
score += 10 * level; | |
enemiesDefeated++; | |
// Chance to drop powerup | |
if (Math.random() < 0.1) { | |
spawnPowerup(); | |
} | |
} | |
updateUI(); | |
} | |
}); | |
// Player shots with boss | |
if (boss && collision(shot, boss)) { | |
boss.health -= shot.damage; | |
shots.splice(i, 1); | |
updateBossHealth(); | |
// Boss phase changes | |
if (boss.health <= boss.maxHealth * 0.66 && boss.phase === 1) { | |
boss.phase = 2; | |
boss.speed *= 1.2; | |
} else if (boss.health <= boss.maxHealth * 0.33 && boss.phase === 2) { | |
boss.phase = 3; | |
boss.speed *= 1.2; | |
} else if (boss.health <= 0) { | |
defeatBoss(); | |
} | |
} | |
} | |
}); | |
} | |
function collision(a, b) { | |
return a.x < b.x + b.w && | |
a.x + a.w > b.x && | |
a.y < b.y + b.h && | |
a.y + a.h > b.y; | |
} | |
function updateGameObjects() { | |
// Update player | |
if (keys['ArrowLeft'] || keys['a']) player.x -= player.speed; | |
if (keys['ArrowRight'] || keys['d']) player.x += player.speed; | |
if (keys[' ']) shoot(); | |
if (keys['Shift']) specialAttack(); | |
// Keep player in bounds | |
player.x = Math.max(0, Math.min(canvas.width - player.w, player.x)); | |
// Update special charge | |
if (player.specialCharge < player.maxSpecial) { | |
player.specialCharge += 0.2; | |
} | |
// Update shield timer | |
if (player.hasShield) { | |
player.shieldTimer -= 16; // Assuming 60fps | |
if (player.shieldTimer <= 0) { | |
player.hasShield = false; | |
} | |
} | |
// Update rapid fire timer | |
if (player.rapidFire) { | |
player.rapidFireTimer -= 16; | |
if (player.rapidFireTimer <= 0) { | |
player.rapidFire = false; | |
player.fireRate = 300; | |
} | |
} | |
// Update shots | |
shots.forEach((shot, i) => { | |
// Apply angle if exists | |
if (shot.angle) { | |
shot.x += Math.sin(shot.angle) * 3; | |
} | |
shot.y -= shot.speed; | |
// Remove if out of bounds | |
if (shot.y < -shot.h || shot.y > canvas.height) { | |
shots.splice(i, 1); | |
} | |
}); | |
// Update enemies | |
enemies.forEach((enemy, i) => { | |
enemy.y += enemy.speed; | |
// Enemy shooting | |
if (enemy.canShoot && Math.random() < 0.02) { | |
enemyShoot(enemy); | |
} | |
// Remove if out of bounds | |
if (enemy.y > canvas.height + enemy.h) { | |
enemies.splice(i, 1); | |
} | |
}); | |
// Update boss | |
if (boss) { | |
// Boss movement pattern | |
boss.moveTimer += 1; | |
if (boss.pattern === 'down') { | |
boss.y += boss.speed; | |
if (boss.y > 50) { | |
boss.pattern = 'left'; | |
boss.moveTimer = 0; | |
} | |
} else if (boss.pattern === 'left') { | |
boss.x -= boss.speed; | |
if (boss.moveTimer > 120 || boss.x < 50) { | |
boss.pattern = 'right'; | |
boss.moveTimer = 0; | |
} | |
} else if (boss.pattern === 'right') { | |
boss.x += boss.speed; | |
if (boss.moveTimer > 120 || boss.x > canvas.width - boss.w - 50) { | |
boss.pattern = 'left'; | |
boss.moveTimer = 0; | |
} | |
} | |
// Boss shooting | |
if (Math.random() < 0.03) { | |
bossShoot(); | |
} | |
} | |
// Update powerups | |
powerups.forEach((powerup, i) => { | |
powerup.y += powerup.speed; | |
// Remove if out of bounds | |
if (powerup.y > canvas.height + powerup.h) { | |
powerups.splice(i, 1); | |
} | |
}); | |
// Update particles | |
particles.forEach((particle, i) => { | |
particle.x += particle.speedX; | |
particle.y += particle.speedY; | |
particle.life--; | |
if (particle.life <= 0) { | |
particles.splice(i, 1); | |
} | |
}); | |
} | |
function drawBackground() { | |
// Dark space background | |
ctx.fillStyle = 'rgba(10, 10, 20, 0.8)'; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
// Draw parallax stars | |
ctx.fillStyle = 'white'; | |
parallaxStars.forEach(star => { | |
ctx.globalAlpha = star.alpha; | |
star.y += star.speed; | |
if (star.y > canvas.height) { | |
star.y = 0; | |
star.x = Math.random() * canvas.width; | |
} | |
ctx.fillRect(star.x, star.y, star.size, star.size); | |
}); | |
ctx.globalAlpha = 1; | |
} | |
function drawPlayer() { | |
// Draw ship body | |
ctx.fillStyle = '#3b82f6'; | |
ctx.beginPath(); | |
ctx.moveTo(player.x + player.w / 2, player.y); | |
ctx.lineTo(player.x + player.w, player.y + player.h); | |
ctx.lineTo(player.x, player.y + player.h); | |
ctx.closePath(); | |
ctx.fill(); | |
// Draw ship details | |
ctx.fillStyle = '#93c5fd'; | |
ctx.fillRect(player.x + player.w / 2 - 5, player.y + 15, 10, 15); | |
// Draw engine glow | |
if (keys['ArrowLeft'] || keys['ArrowRight'] || keys['a'] || keys['d']) { | |
ctx.fillStyle = '#f59e0b'; | |
ctx.beginPath(); | |
ctx.moveTo(player.x + player.w / 2 - 10, player.y + player.h); | |
ctx.lineTo(player.x + player.w / 2 + 10, player.y + player.h); | |
ctx.lineTo(player.x + player.w / 2, player.y + player.h + 15); | |
ctx.closePath(); | |
ctx.fill(); | |
} | |
// Draw shield if active | |
if (player.hasShield) { | |
ctx.strokeStyle = 'rgba(59, 130, 246, 0.7)'; | |
ctx.lineWidth = 2; | |
ctx.setLineDash([5, 5]); | |
ctx.beginPath(); | |
ctx.arc(player.x + player.w/2, player.y + player.h/2, player.w + 10, 0, Math.PI * 2); | |
ctx.stroke(); | |
ctx.setLineDash([]); | |
} | |
} | |
function drawShots() { | |
shots.forEach(shot => { | |
ctx.fillStyle = shot.color; | |
ctx.fillRect(shot.x, shot.y, shot.w, shot.h); | |
// Add glow to special shots | |
if (shot.damage > 1) { | |
ctx.shadowColor = shot.color; | |
ctx.shadowBlur = 10; | |
ctx.fillRect(shot.x, shot.y, shot.w, shot.h); | |
ctx.shadowBlur = 0; | |
} | |
}); | |
} | |
function drawEnemies() { | |
enemies.forEach(enemy => { | |
ctx.fillStyle = enemy.color; | |
ctx.beginPath(); | |
ctx.moveTo(enemy.x + enemy.w / 2, enemy.y); | |
ctx.lineTo(enemy.x + enemy.w, enemy.y + enemy.h); | |
ctx.lineTo(enemy.x, enemy.y + enemy.h); | |
ctx.closePath(); | |
ctx.fill(); | |
// Draw health for tank enemies | |
if (enemy.type === 'tank' && enemy.health > 1) { | |
ctx.fillStyle = 'white'; | |
ctx.font = '10px Arial'; | |
ctx.fillText(`${enemy.health}`, enemy.x + enemy.w/2 - 5, enemy.y + enemy.h/2 + 3); | |
} | |
}); | |
// Draw boss | |
if (boss) { | |
ctx.fillStyle = boss.color; | |
ctx.beginPath(); | |
ctx.moveTo(boss.x + boss.w / 2, boss.y); | |
ctx.lineTo(boss.x + boss.w, boss.y + boss.h); | |
ctx.lineTo(boss.x, boss.y + boss.h); | |
ctx.closePath(); | |
ctx.fill(); | |
// Draw boss details | |
ctx.fillStyle = '#fca5a5'; | |
ctx.fillRect(boss.x + boss.w/2 - 20, boss.y + 30, 40, 10); | |
ctx.fillRect(boss.x + boss.w/2 - 15, boss.y + 50, 30, 20); | |
} | |
} | |
function drawPowerups() { | |
powerups.forEach(powerup => { | |
ctx.fillStyle = powerup.color; | |
ctx.beginPath(); | |
ctx.arc(powerup.x + powerup.w/2, powerup.y + powerup.h/2, powerup.w/2, 0, Math.PI * 2); | |
ctx.fill(); | |
// Add icon based on type | |
ctx.fillStyle = 'white'; | |
ctx.font = '12px Arial'; | |
ctx.textAlign = 'center'; | |
ctx.textBaseline = 'middle'; | |
if (powerup.type === 'health') { | |
ctx.fillText('+', powerup.x + powerup.w/2, powerup.y + powerup.h/2 + 1); | |
} else if (powerup.type === 'shield') { | |
ctx.fillText('⛉', powerup.x + powerup.w/2, powerup.y + powerup.h/2 + 1); | |
} else if (powerup.type === 'rapidfire') { | |
ctx.fillText('»', powerup.x + powerup.w/2, powerup.y + powerup.h/2 + 1); | |
} else { | |
ctx.fillText('!', powerup.x + powerup.w/2, powerup.y + powerup.h/2 + 1); | |
} | |
}); | |
} | |
function drawParticles() { | |
particles.forEach(particle => { | |
ctx.globalAlpha = particle.life / 60; | |
ctx.fillStyle = particle.color; | |
ctx.fillRect(particle.x, particle.y, particle.size, particle.size); | |
}); | |
ctx.globalAlpha = 1; | |
} | |
function drawUI() { | |
// Draw special charge bar | |
const chargeWidth = (player.specialCharge / player.maxSpecial) * 100; | |
ctx.fillStyle = '#3b82f6'; | |
ctx.fillRect(canvas.width - 110, 10, chargeWidth, 10); | |
ctx.strokeStyle = 'white'; | |
ctx.strokeRect(canvas.width - 110, 10, 100, 10); | |
// Draw special charge text | |
ctx.fillStyle = 'white'; | |
ctx.font = '12px Arial'; | |
ctx.fillText('SPECIAL', canvas.width - 105, 20); | |
// Draw rapid fire indicator | |
if (player.rapidFire) { | |
ctx.fillStyle = 'rgba(245, 158, 11, 0.3)'; | |
ctx.fillRect(0, 0, canvas.width, canvas.height); | |
} | |
} | |
// ============================================= | |
// GAME LOOP | |
// ============================================= | |
function gameLoop() { | |
if (gameOver) { | |
gameOverScreen.classList.remove('hidden'); | |
return; | |
} | |
if (gamePaused) { | |
requestAnimationFrame(gameLoop); | |
return; | |
} | |
// Clear canvas | |
ctx.clearRect(0, 0, canvas.width, canvas.height); | |
// Update game state | |
spawnEnemy(); | |
spawnPowerup(); | |
updateGameObjects(); | |
checkCollisions(); | |
// Draw everything | |
drawBackground(); | |
drawParticles(); | |
drawEnemies(); | |
drawShots(); | |
drawPowerups(); | |
drawPlayer(); | |
drawUI(); | |
// Continue the loop | |
requestAnimationFrame(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=Xacodavt/19-de-mar-o-de-2025" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
</html> |