2023-04-30 18:29:35 +02:00
|
|
|
function initializeGame() {
|
2024-02-16 22:49:06 +01:00
|
|
|
const gameContainer = document.getElementById("game-container");
|
|
|
|
const player = document.getElementById("player");
|
|
|
|
|
|
|
|
let playerSize = gameContainer.clientWidth * 0.0625; // 5% of container width
|
|
|
|
player.style.width = playerSize + "px";
|
|
|
|
player.style.height = playerSize + "px";
|
|
|
|
|
|
|
|
let playerX = gameContainer.clientWidth / 2 - playerSize / 2;
|
|
|
|
let playerY = gameContainer.clientHeight * 0.1;
|
|
|
|
const scoreElement = document.getElementById("score");
|
|
|
|
const levelElement = document.getElementById("level");
|
|
|
|
const livesElement = document.getElementById("lives");
|
|
|
|
const highScoreElement = document.getElementById("high-score");
|
|
|
|
|
|
|
|
let pdfSize = gameContainer.clientWidth * 0.0625; // 5% of container width
|
|
|
|
let projectileWidth = gameContainer.clientWidth * 0.00625; // 0.00625; // 0.5% of container width
|
|
|
|
let projectileHeight = gameContainer.clientHeight * 0.01667; // 1% of container height
|
|
|
|
|
|
|
|
let paused = false;
|
|
|
|
|
|
|
|
const fireRate = 200; // Time between shots in milliseconds
|
|
|
|
let lastProjectileTime = 0;
|
|
|
|
let lives = 3;
|
|
|
|
|
|
|
|
let highScore = localStorage.getItem("highScore") ? parseInt(localStorage.getItem("highScore")) : 0;
|
|
|
|
updateHighScore();
|
|
|
|
|
2024-02-18 10:45:50 +01:00
|
|
|
const PLAYER_MOVE_SPEED = 5;
|
2024-02-18 10:21:30 +01:00
|
|
|
const BASE_PDF_SPEED = 1;
|
|
|
|
const LEVEL_INCREASE_PDF_SPEED = 0.2;
|
|
|
|
const BASE_SPAWN_INTERVAL_MS = 1250; // milliseconds before a new enemy spawns
|
|
|
|
const LEVEL_INCREASE_FACTOR_MS = 25; // milliseconds to decrease the spawn interval per level
|
|
|
|
const MAX_SPAWN_RATE_REDUCTION_MS = 800; // Max milliseconds from the base spawn interval
|
|
|
|
|
|
|
|
|
2024-02-18 10:45:50 +01:00
|
|
|
let keysPressed = {};
|
2024-02-16 22:49:06 +01:00
|
|
|
const pdfs = [];
|
|
|
|
const projectiles = [];
|
|
|
|
let score = 0;
|
|
|
|
let level = 1;
|
2024-02-18 10:21:30 +01:00
|
|
|
let pdfSpeed = BASE_PDF_SPEED;
|
2024-02-16 22:49:06 +01:00
|
|
|
let gameOver = false;
|
|
|
|
|
2024-02-18 10:21:30 +01:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
function handleKeys() {
|
|
|
|
if (keysPressed["ArrowLeft"]) {
|
2024-02-18 10:45:50 +01:00
|
|
|
playerX -= PLAYER_MOVE_SPEED;
|
|
|
|
playerX = Math.max(0, playerX)
|
2023-04-29 23:26:16 +02:00
|
|
|
}
|
2024-02-16 22:49:06 +01:00
|
|
|
if (keysPressed["ArrowRight"]) {
|
2024-02-18 10:45:50 +01:00
|
|
|
playerX += PLAYER_MOVE_SPEED;
|
|
|
|
playerX = Math.min(gameContainer.clientWidth - playerSize, playerX);
|
2023-04-29 23:26:16 +02:00
|
|
|
}
|
2024-02-16 22:49:06 +01:00
|
|
|
if (keysPressed[" "] && !gameOver) {
|
|
|
|
const currentTime = new Date().getTime();
|
|
|
|
if (currentTime - lastProjectileTime >= fireRate) {
|
|
|
|
shootProjectile();
|
|
|
|
lastProjectileTime = currentTime;
|
|
|
|
}
|
2023-04-29 23:26:16 +02:00
|
|
|
}
|
2024-02-16 22:49:06 +01:00
|
|
|
updatePlayerPosition();
|
|
|
|
}
|
2023-04-29 23:26:16 +02:00
|
|
|
|
2024-02-18 10:45:50 +01:00
|
|
|
function onKeydown(event) {
|
2024-02-16 22:49:06 +01:00
|
|
|
if (event.key === " ") {
|
|
|
|
event.preventDefault();
|
2023-04-29 23:26:16 +02:00
|
|
|
}
|
2024-02-16 22:49:06 +01:00
|
|
|
keysPressed[event.key] = true;
|
|
|
|
handleKeys();
|
2024-02-18 10:45:50 +01:00
|
|
|
}
|
|
|
|
function onKeyUp(event) {
|
|
|
|
keysPressed[event.key] = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
document.removeEventListener("keydown", onKeydown);
|
|
|
|
document.removeEventListener("keyup", onKeyUp);
|
|
|
|
document.addEventListener("keydown", onKeydown);
|
|
|
|
document.addEventListener("keyup", onKeyUp);
|
2024-02-16 22:49:06 +01:00
|
|
|
|
|
|
|
function updatePlayerPosition() {
|
|
|
|
player.style.left = playerX + "px";
|
|
|
|
player.style.bottom = playerY + "px";
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateLives() {
|
|
|
|
livesElement.textContent = "Lives: " + lives;
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateHighScore() {
|
|
|
|
highScoreElement.textContent = "High Score: " + highScore;
|
|
|
|
}
|
|
|
|
|
|
|
|
function shootProjectile() {
|
|
|
|
const projectile = document.createElement("div");
|
|
|
|
projectile.classList.add("projectile");
|
|
|
|
projectile.style.backgroundColor = "black";
|
|
|
|
projectile.style.width = projectileWidth + "px";
|
|
|
|
projectile.style.height = projectileHeight + "px";
|
|
|
|
projectile.style.left = playerX + playerSize / 2 - projectileWidth / 2 + "px";
|
|
|
|
projectile.style.top = gameContainer.clientHeight - playerY - playerSize + "px";
|
|
|
|
gameContainer.appendChild(projectile);
|
|
|
|
projectiles.push(projectile);
|
|
|
|
}
|
|
|
|
|
|
|
|
function spawnPdf() {
|
|
|
|
const pdf = document.createElement("img");
|
|
|
|
pdf.src = "images/file-earmark-pdf.svg";
|
|
|
|
pdf.classList.add("pdf");
|
|
|
|
pdf.style.width = pdfSize + "px";
|
|
|
|
pdf.style.height = pdfSize + "px";
|
2024-02-18 10:45:50 +01:00
|
|
|
pdf.style.left = Math.floor(Math.random() * (gameContainer.clientWidth - (2*pdfSize))) + pdfSize + "px";
|
2024-02-16 22:49:06 +01:00
|
|
|
pdf.style.top = "0px";
|
|
|
|
gameContainer.appendChild(pdf);
|
|
|
|
pdfs.push(pdf);
|
|
|
|
}
|
|
|
|
|
|
|
|
function resetEnemies() {
|
|
|
|
pdfs.forEach((pdf) => gameContainer.removeChild(pdf));
|
|
|
|
pdfs.length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateGame() {
|
|
|
|
if (gameOver || paused) return;
|
|
|
|
|
2024-02-18 10:45:50 +01:00
|
|
|
handleKeys();
|
2024-02-16 22:49:06 +01:00
|
|
|
for (let pdfIndex = 0; pdfIndex < pdfs.length; pdfIndex++) {
|
|
|
|
const pdf = pdfs[pdfIndex];
|
|
|
|
const pdfY = parseFloat(pdf.style.top) + pdfSpeed;
|
|
|
|
if (pdfY + 50 > gameContainer.clientHeight) {
|
|
|
|
gameContainer.removeChild(pdf);
|
|
|
|
pdfs.splice(pdfIndex, 1);
|
|
|
|
|
|
|
|
// Deduct 2 points when a PDF gets past the player
|
|
|
|
score -= 0;
|
2023-05-03 23:07:51 +02:00
|
|
|
updateScore();
|
2023-04-29 23:26:16 +02:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
// Decrease lives and check if game over
|
|
|
|
lives--;
|
|
|
|
updateLives();
|
|
|
|
if (lives <= 0) {
|
|
|
|
endGame();
|
|
|
|
return;
|
2023-04-29 23:26:16 +02:00
|
|
|
}
|
2024-02-16 22:49:06 +01:00
|
|
|
} else {
|
|
|
|
pdf.style.top = pdfY + "px";
|
|
|
|
|
|
|
|
// Check for collision with player
|
|
|
|
if (collisionDetected(player, pdf)) {
|
|
|
|
lives--;
|
|
|
|
updateLives();
|
|
|
|
resetEnemies();
|
|
|
|
if (lives <= 0) {
|
|
|
|
endGame();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-04-29 23:26:16 +02:00
|
|
|
}
|
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
projectiles.forEach((projectile, projectileIndex) => {
|
|
|
|
const projectileY = parseInt(projectile.style.top) - 10;
|
|
|
|
if (projectileY < 0) {
|
|
|
|
gameContainer.removeChild(projectile);
|
|
|
|
projectiles.splice(projectileIndex, 1);
|
|
|
|
} else {
|
|
|
|
projectile.style.top = projectileY + "px";
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let pdfIndex = 0; pdfIndex < pdfs.length; pdfIndex++) {
|
|
|
|
const pdf = pdfs[pdfIndex];
|
|
|
|
if (collisionDetected(projectile, pdf)) {
|
|
|
|
gameContainer.removeChild(pdf);
|
|
|
|
gameContainer.removeChild(projectile);
|
|
|
|
pdfs.splice(pdfIndex, 1);
|
|
|
|
projectiles.splice(projectileIndex, 1);
|
|
|
|
score = score + 10;
|
|
|
|
updateScore();
|
|
|
|
break;
|
2023-04-29 23:26:16 +02:00
|
|
|
}
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
|
|
|
});
|
2023-04-29 23:26:16 +02:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
setTimeout(updateGame, 1000 / 60);
|
|
|
|
}
|
2023-04-29 23:26:16 +02:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
function resetGame() {
|
|
|
|
playerX = gameContainer.clientWidth / 2;
|
|
|
|
playerY = 50;
|
|
|
|
updatePlayerPosition();
|
2023-04-29 23:26:16 +02:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
pdfs.forEach((pdf) => gameContainer.removeChild(pdf));
|
|
|
|
projectiles.forEach((projectile) => gameContainer.removeChild(projectile));
|
2023-04-29 23:26:16 +02:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
pdfs.length = 0;
|
|
|
|
projectiles.length = 0;
|
2023-04-29 23:26:16 +02:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
score = 0;
|
|
|
|
level = 1;
|
|
|
|
lives = 3;
|
2024-02-06 01:00:49 +01:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
gameOver = false;
|
2023-04-29 23:26:16 +02:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
updateScore();
|
|
|
|
updateLives();
|
|
|
|
levelElement.textContent = "Level: " + level;
|
2024-02-18 10:21:30 +01:00
|
|
|
pdfSpeed = BASE_PDF_SPEED;
|
2024-02-16 22:49:06 +01:00
|
|
|
clearTimeout(spawnPdfTimeout); // Clear the existing spawnPdfTimeout
|
|
|
|
setTimeout(updateGame, 1000 / 60);
|
2023-04-29 23:26:16 +02:00
|
|
|
spawnPdfInterval();
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
function updateScore() {
|
|
|
|
scoreElement.textContent = "Score: " + score;
|
|
|
|
checkLevelUp();
|
|
|
|
}
|
|
|
|
|
|
|
|
function checkLevelUp() {
|
|
|
|
const newLevel = Math.floor(score / 100) + 1;
|
|
|
|
if (newLevel > level) {
|
|
|
|
level = newLevel;
|
|
|
|
levelElement.textContent = "Level: " + level;
|
2024-02-18 10:21:30 +01:00
|
|
|
pdfSpeed += LEVEL_INCREASE_PDF_SPEED;
|
2024-02-16 22:49:06 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function collisionDetected(a, b) {
|
|
|
|
const rectA = a.getBoundingClientRect();
|
|
|
|
const rectB = b.getBoundingClientRect();
|
|
|
|
return rectA.left < rectB.right && rectA.right > rectB.left && rectA.top < rectB.bottom && rectA.bottom > rectB.top;
|
|
|
|
}
|
|
|
|
|
|
|
|
function endGame() {
|
|
|
|
gameOver = true;
|
|
|
|
if (score > highScore) {
|
|
|
|
highScore = score;
|
|
|
|
localStorage.setItem("highScore", highScore);
|
|
|
|
updateHighScore();
|
|
|
|
}
|
|
|
|
alert("Game Over! Your final score is: " + score);
|
|
|
|
document.getElementById("game-container-wrapper").close();
|
|
|
|
}
|
|
|
|
|
|
|
|
let spawnPdfTimeout;
|
|
|
|
|
2024-02-18 10:21:30 +01:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
|
|
|
|
function spawnPdfInterval() {
|
|
|
|
console.log("spawnPdfInterval");
|
|
|
|
if (gameOver || paused) {
|
|
|
|
console.log("spawnPdfInterval 2");
|
|
|
|
clearTimeout(spawnPdfTimeout);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
console.log("spawnPdfInterval 3");
|
|
|
|
spawnPdf();
|
|
|
|
let spawnRateReduction = Math.min(level * LEVEL_INCREASE_FACTOR_MS, MAX_SPAWN_RATE_REDUCTION_MS);
|
|
|
|
let spawnRate = BASE_SPAWN_INTERVAL_MS - spawnRateReduction;
|
|
|
|
spawnPdfTimeout = setTimeout(spawnPdfInterval, spawnRate);
|
|
|
|
}
|
|
|
|
|
|
|
|
updatePlayerPosition();
|
|
|
|
updateGame();
|
|
|
|
spawnPdfInterval();
|
|
|
|
|
|
|
|
document.addEventListener("visibilitychange", function () {
|
|
|
|
if (document.hidden) {
|
|
|
|
paused = true;
|
|
|
|
} else {
|
|
|
|
paused = false;
|
|
|
|
updateGame();
|
|
|
|
spawnPdfInterval();
|
|
|
|
}
|
|
|
|
});
|
2023-04-29 23:26:16 +02:00
|
|
|
|
2024-02-16 22:49:06 +01:00
|
|
|
window.resetGame = resetGame;
|
2023-04-30 18:29:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
window.initializeGame = initializeGame;
|