app-eqmnnk-92 / assets /js /game.js
AiCoderv2's picture
Upload folder using huggingface_hub
efd65f7 verified
class Game {
constructor() {
this.canvas = document.getElementById('gameCanvas');
this.ctx = this.canvas.getContext('2d');
this.canvas.width = 900;
this.canvas.height = 600;
this.player = null;
this.enemies = [];
this.bullets = [];
this.powerUps = [];
this.particles = [];
this.stars = [];
this.score = 0;
this.lives = 3;
this.level = 1;
this.powerLevel = 1;
this.highScore = localStorage.getItem('highScore') || 0;
this.gameState = 'menu';
this.difficulty = 'normal';
this.soundEnabled = true;
this.isPaused = false;
this.keys = {};
this.enemySpawnTimer = 0;
this.powerUpSpawnTimer = 0;
this.init();
}
init() {
this.setupEventListeners();
this.createStarfield();
this.updateHighScore();
}
setupEventListeners() {
// Menu buttons
document.getElementById('startGameBtn').addEventListener('click', () => this.startGame());
document.getElementById('playBtn').addEventListener('click', () => this.startGame());
document.getElementById('restartBtn').addEventListener('click', () => this.startGame());
document.getElementById('nextLevelBtn').addEventListener('click', () => this.nextLevel());
document.getElementById('difficultyBtn').addEventListener('click', () => this.toggleDifficulty());
document.getElementById('soundBtn').addEventListener('click', () => this.toggleSound());
// Screen navigation
document.getElementById('instructionsBtn').addEventListener('click', () => this.showScreen('instructionsScreen'));
document.getElementById('leaderboardBtn').addEventListener('click', () => this.showScreen('leaderboardScreen'));
document.getElementById('backFromInstructions').addEventListener('click', () => this.showScreen('startScreen'));
document.getElementById('backFromLeaderboard').addEventListener('click', () => this.showScreen('startScreen'));
// Keyboard controls
window.addEventListener('keydown', (e) => {
this.keys[e.key] = true;
if (e.key === 'p' || e.key === 'P') {
this.togglePause();
}
if (e.key === 'm' || e.key === 'M') {
this.toggleSound();
}
});
window.addEventListener('keyup', (e) => {
this.keys[e.key] = false;
});
}
createStarfield() {
for (let i = 0; i < 100; i++) {
this.stars.push({
x: Math.random() * this.canvas.width,
y: Math.random() * this.canvas.height,
size: Math.random() * 2,
speed: Math.random() * 0.5 + 0.1
});
}
}
startGame() {
this.showScreen('gameScreen');
this.gameState = 'playing';
this.score = 0;
this.lives = 3;
this.level = 1;
this.powerLevel = 1;
this.enemies = [];
this.bullets = [];
this.powerUps = [];
this.particles = [];
this.player = new Player(this.canvas.width / 2, this.canvas.height - 100);
this.updateUI();
this.gameLoop();
}
nextLevel() {
this.level++;
this.enemies = [];
this.powerUps = [];
this.player.x = this.canvas.width / 2;
this.player.y = this.canvas.height - 100;
document.getElementById('levelCompleteOverlay').classList.add('hidden');
this.updateUI();
this.gameLoop();
}
gameLoop() {
if (this.gameState !== 'playing') return;
if (!this.isPaused) {
this.update();
this.render();
}
requestAnimationFrame(() => this.gameLoop());
}
update() {
// Update stars
this.stars.forEach(star => {
star.y += star.speed;
if (star.y > this.canvas.height) {
star.y = 0;
star.x = Math.random() * this.canvas.width;
}
});
// Player movement
if (this.keys['ArrowLeft'] && this.player.x > 30) {
this.player.x -= this.player.speed;
}
if (this.keys['ArrowRight'] && this.player.x < this.canvas.width - 30) {
this.player.x += this.player.speed;
}
if (this.keys[' ']) {
this.player.shoot(this.bullets, this.powerLevel);
}
// Update bullets
this.bullets = this.bullets.filter(bullet => {
bullet.y -= bullet.speed;
return bullet.y > -10;
});
// Spawn enemies
this.enemySpawnTimer++;
const spawnRate = Math.max(30, 60 - this.level * 5);
if (this.enemySpawnTimer > spawnRate) {
this.spawnEnemy();
this.enemySpawnTimer = 0;
}
// Update enemies
this.enemies = this.enemies.filter(enemy => {
enemy.y += enemy.speed;
enemy.x += Math.sin(enemy.y * 0.02) * enemy.wobble;
// Enemy shooting
if (Math.random() < 0.005 * this.level) {
enemy.shoot(this.bullets);
}
return enemy.y < this.canvas.height + 50;
});
// Spawn power-ups
this.powerUpSpawnTimer++;
if (this.powerUpSpawnTimer > 500) {
this.spawnPowerUp();
this.powerUpSpawnTimer = 0;
}
// Update power-ups
this.powerUps = this.powerUps.filter(powerUp => {
powerUp.y += powerUp.speed;
powerUp.rotation += 0.05;
return powerUp.y < this.canvas.height + 50;
});
// Update particles
this.particles = this.particles.filter(particle => {
particle.x += particle.vx;
particle.y += particle.vy;
particle.life -= 0.02;
return particle.life > 0;
});
// Check collisions
this.checkCollisions();
// Check level complete
if (this.score >= this.level * 1000) {
this.levelComplete();
}
}
spawnEnemy() {
const types = ['basic', 'fast', 'tank'];
const type = types[Math.floor(Math.random() * Math.min(types.length, this.level))];
this.enemies.push(new Enemy(
Math.random() * (this.canvas.width - 60) + 30,
-50,
type
));
}
spawnPowerUp() {
const types = ['rapidFire', 'tripleShot', 'shield', 'life'];
const type = types[Math.floor(Math.random() * types.length)];
this.powerUps.push(new PowerUp(
Math.random() * (this.canvas.width - 40) + 20,
-30,
type
));
}
checkCollisions() {
// Bullet-Enemy collisions
this.bullets.forEach((bullet, bulletIndex) => {
if (bullet.isPlayer) {
this.enemies.forEach((enemy, enemyIndex) => {
if (this.isColliding(bullet, enemy)) {
this.createExplosion(enemy.x, enemy.y);
this.enemies.splice(enemyIndex, 1);
this.bullets.splice(bulletIndex, 1);
this.score += enemy.points;
this.updateUI();
}
});
}
});
// Enemy-Player collisions
this.enemies.forEach((enemy, index) => {
if (this.isColliding(enemy, this.player)) {
if (!this.player.shielded) {
this.lives--;
this.updateUI();
this.createExplosion(this.player.x, this.player.y);
if (this.lives <= 0) {
this.gameOver();
}
}
this.enemies.splice(index, 1);
}
});
// Bullet-Player collisions
this.bullets.forEach((bullet, index) => {
if (!bullet.isPlayer && this.isColliding(bullet, this.player)) {
if (!this.player.shielded) {
this.lives--;
this.updateUI();
this.createExplosion(this.player.x, this.player.y);
if (this.lives <= 0) {
this.gameOver();
}
}
this.bullets.splice(index, 1);
}
});
// PowerUp-Player collisions
this.powerUps.forEach((powerUp, index) => {
if (this.isColliding(powerUp, this.player)) {
this.applyPowerUp(powerUp.type);
this.powerUps.splice(index, 1);
}
});
}
isColliding(obj1, obj2) {
const dist = Math.sqrt(
Math.pow(obj1.x - obj2.x, 2) +
Math.pow(obj1.y - obj2.y, 2)
);
return dist < (obj1.radius || 20) + (obj2.radius || 20);
}
createExplosion(x, y) {
for (let i = 0; i < 20; i++) {
this.particles.push({
x: x,
y: y,
vx: (Math.random() - 0.5) * 8,
vy: (Math.random() - 0.5) * 8,
life: 1,
color: `hsl(${Math.random() * 60}, 100%, 50%)`
});
}
}
applyPowerUp(type) {
switch(type) {
case 'rapidFire':
this.player.fireRate = 5;
setTimeout(() => this.player.fireRate = 10, 5000);
break;
case 'tripleShot':
this.powerLevel = 3;
setTimeout(() => this.powerLevel = 1, 5000);
break;
case 'shield':
this.player.shielded = true;
setTimeout(() => this.player.shielded = false, 3000);
break;
case 'life':
this.lives++;
this.updateUI();
break;
}
}
render() {
// Clear canvas
this.ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
// Draw stars
this.ctx.fillStyle = '#fff';
this.stars.forEach(star => {
this.ctx.globalAlpha = star.size / 2;
this.ctx.fillRect(star.x, star.y, star.size, star.size);
});
this.ctx.globalAlpha = 1;
// Draw game objects
this.player.draw(this.ctx);
this.enemies.forEach(enemy => enemy.draw(this.ctx));
this.bullets.forEach(bullet => bullet.draw(this.ctx));
this.powerUps.forEach(powerUp => powerUp.draw(this.ctx));
this.particles.forEach(particle => {
this.ctx.globalAlpha = particle.life;
this.ctx.fillStyle = particle.color;
this.ctx.fillRect(particle.x - 2, particle.y - 2, 4, 4);
});
this.ctx.globalAlpha = 1;
// Draw pause overlay
if (this.isPaused) {
this.ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.fillStyle = '#fff';
this.ctx.font = '48px Arial';
this.ctx.textAlign = 'center';
this.ctx.fillText('PAUSED', this.canvas.width / 2, this.canvas.height / 2);
}
}
togglePause() {
if (this.gameState === 'playing') {
this.isPaused = !this.isPaused;
}
}
toggleDifficulty() {
const difficulties = ['easy', 'normal', 'hard'];
const currentIndex = difficulties.indexOf(this.difficulty);
this.difficulty = difficulties[(currentIndex + 1) % difficulties.length];
document.getElementById('difficultyBtn').textContent = `Difficulty: ${this.difficulty.charAt(0).toUpperCase() + this.difficulty.slice(1)}`;
}
toggleSound() {
this.soundEnabled = !this.soundEnabled;
document.getElementById('soundBtn').textContent = `Sound: ${this.soundEnabled ? 'ON' : 'OFF'}`;
}
updateUI() {
document.getElementById('score').textContent = this.score;
document.getElementById('lives').textContent = this.lives;
document.getElementById('level').textContent = this.level;
document.getElementById('powerLevel').textContent = this.powerLevel;
}
updateHighScore() {
document.getElementById('highScoreValue').textContent = this.highScore;
}
levelComplete() {
this.gameState = 'levelComplete';
document.getElementById('levelScore').textContent = this.score;
document.getElementById('levelCompleteOverlay').classList.remove('hidden');
}
gameOver() {
this.gameState = 'gameOver';
if (this.score > this.highScore) {
this.highScore = this.score;
localStorage.setItem('highScore', this.highScore);
this.updateHighScore();
}
this.saveToLeaderboard();
document.getElementById('finalScore').textContent = this.score;
document.getElementById('gameOverOverlay').classList.remove('hidden');
}
saveToLeaderboard() {
let leaderboard = JSON.parse(localStorage.getItem('leaderboard')) || [];
leaderboard.push({
score: this.score,
level: this.level,
date: new Date().toLocaleDateString()
});
leaderboard.sort((a, b) => b.score - a.score);
leaderboard = leaderboard.slice(0, 10);
localStorage.setItem('leaderboard', JSON.stringify(leaderboard));
}
showScreen(screenId) {
document.querySelectorAll('.screen').forEach(screen => {
screen.classList.add('hidden');
});
document.getElementById(screenId).classList.remove('hidden');
if (screenId === 'leaderboardScreen') {
this.displayLeaderboard();
}
}
displayLeaderboard() {
const leaderboard = JSON.parse(localStorage.getItem('leaderboard')) || [];
const leaderboardList = document.getElementById('leaderboardList');
if (leaderboard.length === 0) {
leaderboardList.innerHTML = '<p style="text-align: center; opacity: 0.6;">No scores yet. Be the first!</p>';
return;
}
leaderboardList.innerHTML = leaderboard.map((entry, index) => `
<div class="leaderboard-entry">
<span>${index + 1}. Level ${entry.level}</span>
<span>${entry.score}</span>
</div>
`).join('');
}
}
class Player {
constructor(x, y) {
this.x = x;
this.y = y;
this.speed = 5;
this.fireRate = 10;
this.lastShot = 0;
this.shielded = false;
}
shoot(bullets, powerLevel) {
const now = Date.now();
if (now - this.lastShot > 1000 / this.fireRate) {
if (powerLevel === 3) {
bullets.push(new Bullet(this.x - 15, this.y, -10, true));
bullets.push(new Bullet(this.x, this.y, -10, true));
bullets.push(new Bullet(this.x + 15, this.y, -10, true));
} else {
bullets.push(new Bullet(this.x, this.y, -10, true));
}
this.lastShot = now;
}
}
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
// Draw shield if active
if (this.shielded) {
ctx.strokeStyle = 'rgba(0, 255, 136, 0.5)';
ctx.lineWidth = 3;
ctx.beginPath();
ctx.arc(0, 0, 35, 0, Math.PI * 2);
ctx.stroke();
}
// Draw spaceship
ctx.fillStyle = '#00ff88';
ctx.beginPath();
ctx.moveTo(0, -20);
ctx.lineTo(-15, 20);
ctx.lineTo(0, 10);
ctx.lineTo(15, 20);
ctx.closePath();
ctx.fill();
// Draw cockpit
ctx.fillStyle = '#00bbff';
ctx.beginPath();
ctx.arc(0, 0, 5, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
class Enemy {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
this.lastShot = 0;
switch(type) {
case 'basic':
this.speed = 2;
this.health = 1;
this.points = 10;
this.color = '#ff006e';
this.wobble = 0;
break;
case 'fast':
this.speed = 4;
this.health = 1;
this.points = 20;
this.color = '#ff4458';
this.wobble = 2;
break;
case 'tank':
this.speed = 1;
this.health = 3;
this.points = 50;
this.color = '#8338ec';
this.wobble = 0;
break;
}
}
shoot(bullets) {
const now = Date.now();
if (now - this.lastShot > 2000) {
bullets.push(new Bullet(this.x, this.y + 20, 5, false));
this.lastShot = now;
}
}
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.moveTo(0, 20);
ctx.lineTo(-15, -20);
ctx.lineTo(0, -10);
ctx.lineTo(15, -20);
ctx.closePath();
ctx.fill();
ctx.fillStyle = '#000';
ctx.beginPath();
ctx.arc(0, 0, 3, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
class Bullet {
constructor(x, y, speed, isPlayer) {
this.x = x;
this.y = y;
this.speed = speed;
this.isPlayer = isPlayer;
this.radius = 3;
}
draw(ctx) {
ctx.fillStyle = this.isPlayer ? '#00ff88' : '#ff006e';
ctx.fillRect(this.x - 2, this.y - 5, 4, 10);
}
}
class PowerUp {
constructor(x, y, type) {
this.x = x;
this.y = y;
this.type = type;
this.speed = 1;
this.rotation = 0;
this.radius = 15;
switch(type) {
case 'rapidFire':
this.color = '#ff006e';
break;
case 'tripleShot':
this.color = '#3a86ff';
break;
case 'shield':
this.color = '#00ff88';
break;
case 'life':
this.color = '#ff6b6b';
break;
}
}
draw(ctx) {
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.rotation);
ctx.fillStyle = this.color;
ctx.fillRect(-10, -10, 20, 20);
ctx.fillStyle = '#fff';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
const symbols = {
rapidFire: '⚡',
tripleShot: '⫸',
shield: '🛡',
life: '❤'
};
ctx.fillText(symbols[this.type] || '?', 0, 0);
ctx.restore();
}
}
// Initialize game when page loads
window.addEventListener('DOMContentLoaded', () => {
const game = new Game();
});