2024-12-17 14:42:17 +01:00
|
|
|
"use strict";
|
2024-12-17 12:44:19 +01:00
|
|
|
// Canvas setup
|
|
|
|
const canvas = document.getElementById('gameCanvas');
|
|
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
canvas.width = window.innerWidth;
|
|
|
|
canvas.height = window.innerHeight;
|
|
|
|
|
|
|
|
// Game elements
|
|
|
|
const player = {
|
|
|
|
x: canvas.width / 2,
|
|
|
|
y: canvas.height - 60,
|
|
|
|
width: 40,
|
|
|
|
height: 40,
|
|
|
|
color: 'white',
|
|
|
|
speed: 5,
|
|
|
|
dx: 0
|
|
|
|
};
|
|
|
|
|
|
|
|
let bullets = [];
|
|
|
|
let asteroids = [];
|
|
|
|
let items = [];
|
|
|
|
let score = 0;
|
|
|
|
let totalBulletsFired = 0;
|
|
|
|
let isGameOver = false;
|
|
|
|
let lastBulletTime = 0;
|
|
|
|
let ammo = 100; // Ammo counter
|
|
|
|
|
|
|
|
// Difficulty control
|
|
|
|
let asteroidSpawnInterval = 800; // Faster spawn rate
|
|
|
|
let asteroidSpeedMultiplier = 1.5; // Faster asteroids from the start
|
|
|
|
let difficultyIncreaseRate = 0.2; // Faster scaling every 10 seconds
|
|
|
|
|
|
|
|
// Controls
|
|
|
|
let canShoot = true; // Flag to control shooting
|
|
|
|
let rapidFireActive = false;
|
|
|
|
let shotgunActive = false;
|
|
|
|
let rainbowActive = false;
|
|
|
|
let lastShotgunTime = 0;
|
|
|
|
|
|
|
|
// Controls for sphere effects
|
|
|
|
let blueSphereCooldown = 0;
|
|
|
|
let yellowSphereCooldown = 0;
|
|
|
|
let greenSphereCooldown = 0;
|
|
|
|
let rainbowSphereCooldown = 0;
|
|
|
|
|
|
|
|
// Sphere types
|
|
|
|
const sphereTypes = ['blue', 'yellow', 'green', 'rainbow'];
|
|
|
|
|
2024-12-17 14:42:17 +01:00
|
|
|
/// Control for left button press and release
|
|
|
|
function btnMoveLeft(isPressed) {
|
|
|
|
if (isPressed) {
|
|
|
|
player.dx = -player.speed; // Start moving left
|
|
|
|
} else {
|
|
|
|
player.dx = 0; // Stop moving when button is released
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Control for shoot button click (simulates spacebar press)
|
|
|
|
function btnShoot() {
|
|
|
|
if (canShoot && !isGameOver) {
|
|
|
|
shootBullet();
|
|
|
|
canShoot = false; // Prevent shooting until the button is "released"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Control for right button press and release
|
|
|
|
function btnMoveRight(isPressed) {
|
|
|
|
if (isPressed) {
|
|
|
|
player.dx = player.speed; // Start moving right
|
|
|
|
} else {
|
|
|
|
player.dx = 0; // Stop moving when button is released
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
document.getElementById('shootBtn').addEventListener('mouseup', () => {
|
|
|
|
canShoot = true; // Allow shooting again when button is released
|
|
|
|
});
|
|
|
|
|
2024-12-17 12:44:19 +01:00
|
|
|
window.addEventListener('keydown', (e) => {
|
|
|
|
if (e.key === 'ArrowLeft' || e.key === 'a') player.dx = -player.speed;
|
|
|
|
if (e.key === 'ArrowRight' || e.key === 'd') player.dx = player.speed;
|
|
|
|
|
|
|
|
// Shoot only if it's not a hold, and we can shoot
|
|
|
|
if (e.key === ' ' && canShoot && !isGameOver) {
|
|
|
|
shootBullet();
|
|
|
|
canShoot = false; // Prevent shooting until the key is released
|
|
|
|
}
|
|
|
|
|
|
|
|
if (e.key === 'r' && isGameOver) restartGame();
|
|
|
|
});
|
|
|
|
|
|
|
|
window.addEventListener('keyup', (e) => {
|
|
|
|
// Stop moving when either the arrow keys or the 'a'/'d' keys are released
|
|
|
|
if (e.key === 'ArrowLeft' || e.key === 'ArrowRight' || e.key === 'a' || e.key === 'd') {
|
|
|
|
player.dx = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allow shooting again when the space key is released
|
|
|
|
if (e.key === ' ') {
|
|
|
|
canShoot = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Bullet mechanics with cooldown
|
|
|
|
function shootBullet() {
|
|
|
|
const now = Date.now();
|
|
|
|
if (now - lastBulletTime < 100) return; // Enforce cooldown of 0.1 seconds
|
|
|
|
if (ammo <= 0) return; // Prevent shooting if ammo is empty
|
|
|
|
lastBulletTime = now;
|
|
|
|
ammo--; // Decrease ammo
|
|
|
|
|
2024-12-17 14:42:17 +01:00
|
|
|
totalBulletsFired++; // Increment total bullets fired
|
|
|
|
|
2024-12-17 12:44:19 +01:00
|
|
|
if (rapidFireActive) {
|
|
|
|
// If rapid fire is active, fire bullets continuously with a short delay
|
|
|
|
for (let i = 0; i < 3; i++) {
|
|
|
|
setTimeout(() => {
|
|
|
|
bullets.push({
|
|
|
|
x: player.x + player.width / 2 - 2.5,
|
|
|
|
y: player.y,
|
|
|
|
width: 5,
|
|
|
|
height: 10,
|
|
|
|
color: 'yellow',
|
|
|
|
speed: 7
|
|
|
|
});
|
|
|
|
}, i * 50); // Fire bullets with 50ms delay between shots
|
|
|
|
}
|
|
|
|
} else if (shotgunActive) {
|
|
|
|
// Shotgun effect, firing 3 bullets with a spread
|
|
|
|
for (let i = -1; i <= 1; i++) {
|
|
|
|
bullets.push({
|
|
|
|
x: player.x + player.width / 2 - 2.5,
|
|
|
|
y: player.y,
|
|
|
|
width: 5,
|
|
|
|
height: 10,
|
|
|
|
color: 'yellow',
|
|
|
|
speed: 7,
|
|
|
|
angle: i * 10 // Spray the bullets at different angles
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Normal bullet
|
|
|
|
bullets.push({
|
|
|
|
x: player.x + player.width / 2 - 2.5,
|
|
|
|
y: player.y,
|
|
|
|
width: 5,
|
|
|
|
height: 10,
|
|
|
|
color: 'yellow',
|
|
|
|
speed: 7
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate random color
|
|
|
|
function getRandomColor() {
|
|
|
|
const colors = ['red', 'blue', 'green', 'orange', 'purple', 'pink'];
|
|
|
|
return colors[Math.floor(Math.random() * colors.length)];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Asteroid mechanics
|
|
|
|
function createAsteroid() {
|
|
|
|
const size = Math.random() * 40 + 30; // Bigger asteroids (min: 30, max: 70)
|
|
|
|
asteroids.push({
|
|
|
|
x: Math.random() * canvas.width,
|
|
|
|
y: -size,
|
|
|
|
width: size,
|
|
|
|
height: size,
|
|
|
|
color: getRandomColor(),
|
|
|
|
speed: (Math.random() * 3 + 2) * asteroidSpeedMultiplier // Faster initial speed
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Item mechanics
|
|
|
|
function createItem() {
|
|
|
|
const randomType = sphereTypes[Math.floor(Math.random() * sphereTypes.length)];
|
|
|
|
const size = 30;
|
|
|
|
const x = Math.random() * canvas.width;
|
|
|
|
items.push({
|
|
|
|
x: x,
|
|
|
|
y: -size,
|
|
|
|
width: size,
|
|
|
|
height: size,
|
|
|
|
type: randomType,
|
|
|
|
color: randomType === 'blue' ? 'blue' : randomType === 'yellow' ? 'yellow' : randomType === 'green' ? 'green' : 'rainbow',
|
|
|
|
speed: 3
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update game elements
|
|
|
|
function updatePlayer() {
|
|
|
|
player.x += player.dx;
|
|
|
|
if (player.x < 0) player.x = 0;
|
|
|
|
if (player.x + player.width > canvas.width) player.x = canvas.width - player.width;
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateBullets() {
|
|
|
|
bullets.forEach((bullet, index) => {
|
|
|
|
bullet.y -= bullet.speed;
|
|
|
|
if (bullet.y + bullet.height < 0) bullets.splice(index, 1);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateAsteroids() {
|
|
|
|
asteroids.forEach((asteroid, index) => {
|
|
|
|
asteroid.y += asteroid.speed;
|
|
|
|
if (asteroid.y > canvas.height) asteroids.splice(index, 1);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateItems() {
|
|
|
|
items.forEach((item, index) => {
|
|
|
|
item.y += item.speed;
|
|
|
|
if (item.y > canvas.height) items.splice(index, 1);
|
|
|
|
// Check if player collects the item
|
|
|
|
if (
|
|
|
|
player.x < item.x + item.width &&
|
|
|
|
player.x + player.width > item.x &&
|
|
|
|
player.y < item.y + item.height &&
|
|
|
|
player.y + player.height > item.y
|
|
|
|
) {
|
|
|
|
applyItemEffect(item.type);
|
|
|
|
items.splice(index, 1); // Remove the item after it is collected
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function applyItemEffect(type) {
|
2024-12-17 14:42:17 +01:00
|
|
|
let points = Math.floor(Math.random() * 5) + 1; // Random points between 1 and 5
|
2024-12-17 12:44:19 +01:00
|
|
|
if (type === 'blue') {
|
|
|
|
rapidFireActive = true;
|
|
|
|
setTimeout(() => rapidFireActive = false, 15000); // 15 seconds of rapid fire
|
|
|
|
} else if (type === 'yellow') {
|
|
|
|
shotgunActive = true;
|
|
|
|
setTimeout(() => shotgunActive = false, 30000); // 30 seconds of shotgun
|
|
|
|
} else if (type === 'green') {
|
|
|
|
ammo = 100; // Refill ammo
|
|
|
|
} else if (type === 'rainbow') {
|
|
|
|
rapidFireActive = true;
|
|
|
|
shotgunActive = true;
|
|
|
|
setTimeout(() => {
|
|
|
|
rapidFireActive = false;
|
|
|
|
shotgunActive = false;
|
|
|
|
}, 15000); // 15 seconds with all effects
|
|
|
|
}
|
2024-12-17 14:42:17 +01:00
|
|
|
score += points; // Add points when an item is collected
|
2024-12-17 12:44:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Collision detection
|
|
|
|
function checkCollisions() {
|
|
|
|
bullets.forEach((bullet, bIndex) => {
|
|
|
|
asteroids.forEach((asteroid, aIndex) => {
|
|
|
|
if (
|
|
|
|
bullet.x < asteroid.x + asteroid.width &&
|
|
|
|
bullet.x + bullet.width > asteroid.x &&
|
|
|
|
bullet.y < asteroid.y + asteroid.height &&
|
|
|
|
bullet.y + bullet.height > asteroid.y
|
|
|
|
) {
|
|
|
|
bullets.splice(bIndex, 1);
|
|
|
|
asteroids.splice(aIndex, 1);
|
2024-12-17 14:42:17 +01:00
|
|
|
score += Math.floor(Math.random() * 5) + 1; // Add points when an asteroid is destroyed
|
|
|
|
// Visual feedback for destroyed asteroid
|
|
|
|
createExplosion(asteroid.x, asteroid.y);
|
2024-12-17 12:44:19 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
asteroids.forEach((asteroid) => {
|
|
|
|
if (
|
|
|
|
player.x < asteroid.x + asteroid.width &&
|
|
|
|
player.x + player.width > asteroid.x &&
|
|
|
|
player.y < asteroid.y + asteroid.height &&
|
|
|
|
player.y + player.height > asteroid.y
|
|
|
|
) {
|
|
|
|
isGameOver = true;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-12-17 14:42:17 +01:00
|
|
|
// Explosion effect
|
|
|
|
function createExplosion(x, y) {
|
|
|
|
ctx.fillStyle = 'yellow';
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(x, y, 20, 0, Math.PI * 2);
|
|
|
|
ctx.fill();
|
|
|
|
setTimeout(() => ctx.clearRect(x - 20, y - 20, 40, 40), 200); // Clear explosion after 200ms
|
|
|
|
}
|
|
|
|
|
2024-12-17 12:44:19 +01:00
|
|
|
// Draw elements
|
|
|
|
function drawPlayer() {
|
|
|
|
ctx.fillStyle = player.color;
|
|
|
|
ctx.fillRect(player.x, player.y, player.width, player.height);
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawBullets() {
|
|
|
|
bullets.forEach((bullet) => {
|
|
|
|
ctx.fillStyle = bullet.color;
|
|
|
|
ctx.fillRect(bullet.x, bullet.y, bullet.width, bullet.height);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawAsteroids() {
|
|
|
|
asteroids.forEach((asteroid) => {
|
|
|
|
ctx.fillStyle = asteroid.color;
|
|
|
|
ctx.fillRect(asteroid.x, asteroid.y, asteroid.width, asteroid.height);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawItems() {
|
|
|
|
items.forEach((item) => {
|
|
|
|
ctx.fillStyle = item.color;
|
|
|
|
ctx.beginPath();
|
|
|
|
ctx.arc(item.x + item.width / 2, item.y + item.height / 2, item.width / 2, 0, Math.PI * 2);
|
|
|
|
ctx.fill();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawScore() {
|
|
|
|
ctx.fillStyle = 'white';
|
|
|
|
ctx.font = '24px Arial';
|
|
|
|
ctx.fillText(`Score: ${score}`, 20, 40); // Score at top-left corner
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawAmmo() {
|
|
|
|
ctx.fillStyle = 'white';
|
|
|
|
ctx.font = '24px Arial';
|
|
|
|
ctx.fillText(`Ammo: ${ammo}`, 20, 70); // Ammo at top-left corner
|
|
|
|
}
|
|
|
|
|
|
|
|
function drawGameOver() {
|
|
|
|
if (isGameOver) {
|
|
|
|
ctx.fillStyle = 'white';
|
|
|
|
ctx.font = '40px Arial';
|
|
|
|
ctx.textAlign = 'center';
|
|
|
|
ctx.fillText('Game Over!', canvas.width / 2, canvas.height / 2 - 40);
|
|
|
|
ctx.font = '24px Arial';
|
|
|
|
ctx.fillText(`Total Score: ${score}`, canvas.width / 2, canvas.height / 2);
|
|
|
|
ctx.fillText(`Bullets Fired: ${totalBulletsFired}`, canvas.width / 2, canvas.height / 2 + 40);
|
|
|
|
ctx.fillText('Press "R" to Restart', canvas.width / 2, canvas.height / 2 + 80);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Restart game
|
|
|
|
function restartGame() {
|
|
|
|
window.location.reload();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Main game loop
|
|
|
|
function gameLoop() {
|
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
|
|
|
|
|
if (!isGameOver) {
|
|
|
|
updatePlayer();
|
|
|
|
updateBullets();
|
|
|
|
updateAsteroids();
|
|
|
|
updateItems();
|
|
|
|
checkCollisions();
|
|
|
|
}
|
|
|
|
|
|
|
|
drawPlayer();
|
|
|
|
drawBullets();
|
|
|
|
drawAsteroids();
|
|
|
|
drawItems();
|
|
|
|
drawScore();
|
|
|
|
drawAmmo();
|
|
|
|
drawGameOver();
|
|
|
|
|
|
|
|
if (!isGameOver) {
|
2024-12-17 14:42:17 +01:00
|
|
|
if (Math.random() < 0.01) createAsteroid(); // 1% chance every frame to spawn an asteroid
|
|
|
|
if (Math.random() < 0.005) createItem(); // 0.5% chance to spawn an item
|
2024-12-17 12:44:19 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
requestAnimationFrame(gameLoop);
|
|
|
|
}
|
|
|
|
|
2024-12-17 14:42:17 +01:00
|
|
|
// Start game loop
|
2024-12-17 12:44:19 +01:00
|
|
|
gameLoop();
|