Created
September 13, 2024 01:48
-
-
Save picatz/65226ef7507ab4aa2bae1bbba91a256e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>Space Man</title> | |
<!-- Tailwind CSS CDN --> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<!-- Meta tags for responsiveness --> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<style> | |
/* Common styling */ | |
body { | |
background-color: #0d1b2a; | |
color: white; | |
font-family: 'Arial', sans-serif; | |
overflow: hidden; /* Hide scrollbars */ | |
} | |
/* Landing page styling */ | |
#landingPage { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
height: 100vh; | |
text-align: center; | |
} | |
#landingPage h1 { | |
font-size: 4rem; | |
margin-bottom: 1rem; | |
} | |
#landingPage input { | |
padding: 0.5rem 1rem; | |
margin-bottom: 1rem; | |
border-radius: 0.5rem; | |
border: 1px solid #ccc; | |
outline: none; | |
width: 250px; | |
text-align: center; | |
background-color: white; | |
color: black; | |
} | |
#landingPage button { | |
padding: 0.5rem 1rem; | |
background-color: #3b82f6; /* Tailwind blue-500 */ | |
border: none; | |
border-radius: 0.5rem; | |
color: white; | |
cursor: pointer; | |
font-size: 1rem; | |
width: 150px; | |
} | |
/* Game container styling */ | |
#gameContainer { | |
position: relative; | |
width: 480px; | |
height: 320px; | |
background: radial-gradient(circle at bottom, #0d1b2a, #000000); | |
overflow: hidden; | |
border: 4px solid #4B5563; /* Tailwind gray-700 */ | |
border-radius: 12px; | |
margin-bottom: 20px; | |
} | |
/* Stars styling */ | |
#stars { | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 10000px; /* Extended width for scrolling background */ | |
height: 100%; | |
overflow: hidden; | |
z-index: 0; | |
} | |
/* Player styling */ | |
.player { | |
position: absolute; | |
width: 30px; | |
height: 50px; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
transition: transform 0.1s; | |
z-index: 3; | |
} | |
/* Head styling */ | |
.head { | |
width: 24px; | |
height: 24px; | |
background-color: #ffffff; /* White helmet */ | |
border-radius: 50%; | |
position: relative; | |
z-index: 2; | |
border: 2px solid #ccc; | |
overflow: hidden; | |
} | |
/* Visor styling */ | |
.visor { | |
width: 20px; | |
height: 12px; | |
background-color: #3B82F6; /* Blue visor */ | |
position: absolute; | |
top: 6px; | |
left: 2px; | |
border-radius: 6px; | |
} | |
/* Body styling */ | |
.body { | |
width: 26px; | |
height: 28px; | |
background-color: #ffffff; /* White suit */ | |
position: relative; | |
border-radius: 4px; | |
z-index: 1; | |
border: 2px solid #ccc; | |
display: flex; | |
justify-content: center; | |
} | |
/* Arm styling */ | |
.arm { | |
width: 6px; | |
height: 18px; | |
background-color: #ffffff; | |
position: absolute; | |
top: 2px; | |
border: 1px solid #ccc; | |
border-radius: 2px; | |
} | |
.arm.left { | |
left: -6px; | |
transform-origin: top left; | |
} | |
.arm.right { | |
right: -6px; | |
transform-origin: top right; | |
} | |
/* Leg styling */ | |
.leg { | |
width: 8px; | |
height: 16px; | |
background-color: #ffffff; | |
position: absolute; | |
bottom: -16px; | |
border-radius: 2px; | |
border: 1px solid #ccc; | |
} | |
.leg.left { | |
left: 3px; | |
transform-origin: top left; | |
} | |
.leg.right { | |
right: 3px; | |
transform-origin: top right; | |
} | |
/* Boot styling */ | |
.boot { | |
width: 8px; | |
height: 4px; | |
background-color: #4B5563; /* Gray boots */ | |
position: absolute; | |
bottom: 0; | |
border-radius: 1px; | |
} | |
/* Platform styling */ | |
.platform { | |
position: absolute; | |
background-color: rgba(59, 130, 246, 0.8); | |
border-radius: 8px; | |
box-shadow: 0 0 10px rgba(59, 130, 246, 0.6); | |
z-index: 1; | |
} | |
/* Enemy styling */ | |
.enemy { | |
position: absolute; | |
width: 30px; | |
height: 30px; | |
background-color: #10B981; /* Green aliens */ | |
border-radius: 50%; | |
z-index: 1; | |
overflow: hidden; | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
} | |
/* Alien eyes */ | |
.alien-eye { | |
width: 6px; | |
height: 6px; | |
background-color: #ffffff; | |
border-radius: 50%; | |
position: absolute; | |
top: 10px; | |
} | |
.alien-eye.left { | |
left: 8px; | |
} | |
.alien-eye.right { | |
right: 8px; | |
} | |
/* Antennas */ | |
.antenna { | |
width: 2px; | |
height: 10px; | |
background-color: #ffffff; | |
position: absolute; | |
top: -10px; | |
border-radius: 2px; | |
} | |
.antenna.left { | |
left: 8px; | |
} | |
.antenna.right { | |
right: 8px; | |
} | |
/* Enemy Bullet styling */ | |
.enemy-bullet { | |
position: absolute; | |
width: 5px; | |
height: 5px; | |
background-color: #EF4444; | |
border-radius: 50%; | |
z-index: 1; | |
} | |
/* Ammo styling */ | |
.ammo { | |
position: absolute; | |
width: 15px; | |
height: 15px; | |
background-color: #F59E0B; | |
border-radius: 50%; | |
box-shadow: 0 0 5px rgba(245, 158, 11, 0.8); | |
z-index: 1; | |
} | |
/* Bullet styling */ | |
.bullet { | |
position: absolute; | |
width: 5px; | |
height: 5px; | |
background-color: #FBBF24; | |
border-radius: 50%; | |
z-index: 2; | |
} | |
/* Planets styling */ | |
// .planet { | |
// position: absolute; | |
// background-color: #FBBF24; /* Base color */ | |
// border-radius: 50%; | |
// opacity: 0.8; | |
// z-index: 0; | |
// } | |
/* Galaxy styling */ | |
// .galaxy { | |
// position: absolute; | |
// background: radial-gradient(circle at center, #9D174D, transparent 70%); | |
// width: 200px; | |
// height: 200px; | |
// border-radius: 50%; | |
// opacity: 0.5; | |
// z-index: 0; | |
// } | |
/* Animation for running */ | |
@keyframes run { | |
0% { transform: rotate(0deg); } | |
50% { transform: rotate(20deg); } | |
100% { transform: rotate(0deg); } | |
} | |
/* Animation for jumping */ | |
@keyframes jump { | |
0% { transform: translateY(0); } | |
50% { transform: translateY(-15px); } | |
100% { transform: translateY(0); } | |
} | |
/* Star twinkle animation */ | |
@keyframes twinkle { | |
0%, 100% { opacity: 0.8; } | |
50% { opacity: 0.2; } | |
} | |
/* Star styling */ | |
.star { | |
position: absolute; | |
background-color: white; | |
border-radius: 50%; | |
animation: twinkle 2s infinite; | |
} | |
/* Score styling */ | |
#scoreBoard { | |
position: absolute; | |
top: 10px; | |
left: 10px; | |
color: white; | |
font-size: 16px; | |
font-weight: bold; | |
z-index: 4; | |
} | |
/* High Score Table */ | |
#highScoreSection { | |
margin-top: 20px; | |
color: white; | |
} | |
#highScoreTable { | |
color: white; | |
} | |
#highScoreTable th, #highScoreTable td { | |
padding: 5px 10px; | |
border: 1px solid #4B5563; | |
} | |
#highScoreTable th { | |
background-color: #1F2937; | |
} | |
#highScoreTable td { | |
background-color: #111827; | |
} | |
/* Game Over Screen */ | |
#gameOverScreen { | |
display: none; | |
position: absolute; | |
top: 0; | |
left: 0; | |
width: 100%; /* Adjusted width */ | |
height: 100%; /* Adjusted height */ | |
background-color: rgba(0, 0, 0, 0.8); | |
z-index: 5; | |
flex-direction: column; | |
align-items: center; | |
justify-content: center; | |
color: white; | |
text-align: center; | |
padding: 20px; | |
} | |
#gameOverScreen h2 { | |
font-size: 2rem; | |
margin-bottom: 1rem; | |
} | |
#gameOverScreen button { | |
padding: 0.5rem 1rem; | |
background-color: #3b82f6; | |
border: none; | |
border-radius: 0.5rem; | |
color: white; | |
cursor: pointer; | |
font-size: 1rem; | |
margin-top: 1rem; | |
} | |
/* Adjustments for always visible high scores */ | |
#gameScreen { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
} | |
#gameArea { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
} | |
#gameContent { | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
} | |
#highScoreSection { | |
width: 480px; | |
} | |
</style> | |
</head> | |
<body> | |
<!-- Landing Page --> | |
<div id="landingPage"> | |
<h1>Space Man</h1> | |
<input type="text" id="playerNameInput" placeholder="Enter your name" maxlength="15"> | |
<button id="startGameButton" disabled>Start Game</button> | |
</div> | |
<!-- Game Screen --> | |
<div id="gameScreen"> | |
<div id="gameArea"> | |
<div id="gameContainer" class="mt-4"> | |
<div id="stars"></div> | |
<div id="world"> | |
<!-- Game elements go here --> | |
</div> | |
<div id="scoreBoard">Score: 0 | Ammo: 5</div> | |
<!-- Game Over Screen --> | |
<div id="gameOverScreen"> | |
<h2>Game Over!</h2> | |
<p id="finalScore">Your score: 0</p> | |
<button id="playAgainButton">Play Again</button> | |
</div> | |
</div> | |
<!-- High Score Table --> | |
<div id="highScoreSection" class="mt-6"> | |
<h2 class="text-2xl font-bold text-white mb-2">High Scores</h2> | |
<table id="highScoreTable" class="w-full text-left"> | |
<thead> | |
<tr> | |
<th>Rank</th> | |
<th>Name</th> | |
<th>Score</th> | |
</tr> | |
</thead> | |
<tbody id="highScoreBody"> | |
<!-- High scores will be populated here --> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
</div> | |
<script> | |
const landingPage = document.getElementById('landingPage'); | |
const playerNameInput = document.getElementById('playerNameInput'); | |
const startGameButton = document.getElementById('startGameButton'); | |
const gameScreen = document.getElementById('gameScreen'); | |
const gameContainer = document.getElementById('gameContainer'); | |
const starsContainer = document.getElementById('stars'); | |
const world = document.getElementById('world'); | |
const scoreBoard = document.getElementById('scoreBoard'); | |
const highScoreBody = document.getElementById('highScoreBody'); | |
const gameOverScreen = document.getElementById('gameOverScreen'); | |
const finalScoreElement = document.getElementById('finalScore'); | |
const playAgainButton = document.getElementById('playAgainButton'); | |
let player = { | |
element: null, | |
x: 0, | |
y: 250, | |
width: 30, | |
height: 50, | |
speed: 3, | |
velocityX: 0, | |
velocityY: 0, | |
acceleration: 0.5, | |
maxSpeed: 6, | |
friction: 0.9, | |
jumping: false, | |
direction: 'right', | |
moving: false, | |
ammo: 5, | |
score: 0, | |
name: '' | |
}; | |
let camera = { | |
x: 0, | |
y: 0 | |
}; | |
let keys = {}; | |
let gravity = 0.5; | |
let platforms = []; | |
let enemies = []; | |
let bullets = []; | |
let enemyBullets = []; | |
let ammoDrops = []; | |
let gameInterval; | |
let enemySpawnInterval; | |
let ammoDropInterval; | |
let gameOverActive = false; | |
let planets = []; | |
let galaxies = []; | |
function createStars() { | |
// Clear existing stars | |
starsContainer.innerHTML = ''; | |
// Generate random stars | |
for (let i = 0; i < 500; i++) { | |
const star = document.createElement('div'); | |
star.classList.add('star'); | |
star.style.left = Math.random() * 10000 + 'px'; | |
star.style.top = Math.random() * gameContainer.offsetHeight + 'px'; | |
star.style.animationDelay = Math.random() * 2 + 's'; | |
const size = Math.random() * 2 + 1; | |
star.style.width = size + 'px'; | |
star.style.height = size + 'px'; | |
star.style.opacity = 0.8; | |
starsContainer.appendChild(star); | |
} | |
// // Create planets | |
// for (let i = 0; i < 20; i++) { | |
// const planet = document.createElement('div'); | |
// planet.classList.add('planet'); | |
// const size = Math.random() * 80 + 40; | |
// planet.style.width = size + 'px'; | |
// planet.style.height = size + 'px'; | |
// planet.style.left = Math.random() * 10000 + 'px'; | |
// planet.style.top = Math.random() * (gameContainer.offsetHeight - size) + 'px'; | |
// planet.style.backgroundColor = `hsl(${Math.random() * 360}, 70%, 50%)`; | |
// planet.style.animation = `movePlanet ${Math.random() * 50 + 50}s linear infinite`; | |
// starsContainer.appendChild(planet); | |
// planets.push(planet); | |
// } | |
// // Create galaxies | |
// for (let i = 0; i < 10; i++) { | |
// const galaxy = document.createElement('div'); | |
// galaxy.classList.add('galaxy'); | |
// const size = Math.random() * 150 + 100; | |
// galaxy.style.width = size + 'px'; | |
// galaxy.style.height = size + 'px'; | |
// galaxy.style.left = Math.random() * 10000 + 'px'; | |
// galaxy.style.top = Math.random() * (gameContainer.offsetHeight - size) + 'px'; | |
// galaxy.style.animation = `moveGalaxy ${Math.random() * 60 + 60}s linear infinite`; | |
// starsContainer.appendChild(galaxy); | |
// galaxies.push(galaxy); | |
// } | |
} | |
// Animations for planets and galaxies | |
const styleSheet = document.styleSheets[0]; | |
styleSheet.insertRule(` | |
@keyframes movePlanet { | |
0% { transform: translateX(0); } | |
100% { transform: translateX(-10000px); } | |
} | |
`, styleSheet.cssRules.length); | |
styleSheet.insertRule(` | |
@keyframes moveGalaxy { | |
0% { transform: translateX(0); } | |
100% { transform: translateX(-10000px); } | |
} | |
`, styleSheet.cssRules.length); | |
function createPlayer() { | |
// Create player container | |
player.element = document.createElement('div'); | |
player.element.classList.add('player'); | |
// Create head | |
const head = document.createElement('div'); | |
head.classList.add('head'); | |
// Create visor | |
const visor = document.createElement('div'); | |
visor.classList.add('visor'); | |
head.appendChild(visor); | |
player.element.appendChild(head); | |
// Create body | |
const body = document.createElement('div'); | |
body.classList.add('body'); | |
// Create arms | |
const armLeft = document.createElement('div'); | |
armLeft.classList.add('arm', 'left'); | |
const armRight = document.createElement('div'); | |
armRight.classList.add('arm', 'right'); | |
body.appendChild(armLeft); | |
body.appendChild(armRight); | |
// Create legs | |
const legLeft = document.createElement('div'); | |
legLeft.classList.add('leg', 'left'); | |
const bootLeft = document.createElement('div'); | |
bootLeft.classList.add('boot'); | |
legLeft.appendChild(bootLeft); | |
const legRight = document.createElement('div'); | |
legRight.classList.add('leg', 'right'); | |
const bootRight = document.createElement('div'); | |
bootRight.classList.add('boot'); | |
legRight.appendChild(bootRight); | |
body.appendChild(legLeft); | |
body.appendChild(legRight); | |
player.element.appendChild(body); | |
player.element.style.left = player.x + 'px'; | |
player.element.style.top = player.y + 'px'; | |
world.appendChild(player.element); | |
} | |
function createPlatforms() { | |
platforms = []; | |
// Generate initial platforms | |
for (let i = -10; i < 10; i++) { | |
const x = i * 400; | |
const y = 300 - Math.random() * 150; | |
const platformData = { | |
x: x, | |
y: y, | |
width: 300, | |
height: 20, | |
type: 'static', | |
element: null | |
}; | |
const platform = document.createElement('div'); | |
platform.classList.add('platform'); | |
platform.style.left = platformData.x + 'px'; | |
platform.style.top = platformData.y + 'px'; | |
platform.style.width = platformData.width + 'px'; | |
platform.style.height = platformData.height + 'px'; | |
world.appendChild(platform); | |
platformData.element = platform; | |
platforms.push(platformData); | |
} | |
// Ensure there's a platform at the starting position | |
const startPlatform = platforms.find(p => player.x >= p.x && player.x <= p.x + p.width); | |
if (!startPlatform) { | |
const platformData = { | |
x: player.x - 150, | |
y: 250, | |
width: 300, | |
height: 20, | |
type: 'static', | |
element: null | |
}; | |
const platform = document.createElement('div'); | |
platform.classList.add('platform'); | |
platform.style.left = platformData.x + 'px'; | |
platform.style.top = platformData.y + 'px'; | |
platform.style.width = platformData.width + 'px'; | |
platform.style.height = platformData.height + 'px'; | |
world.appendChild(platform); | |
platformData.element = platform; | |
platforms.push(platformData); | |
} | |
} | |
function spawnEnemy() { | |
const enemyType = Math.random() < 0.5 ? 'ground' : 'shooting'; | |
if (enemyType === 'ground') { | |
spawnGroundEnemy(); | |
} else { | |
spawnShootingEnemy(); | |
} | |
} | |
function spawnGroundEnemy() { | |
// Find a platform to place the ground enemy | |
const platform = platforms[Math.floor(Math.random() * platforms.length)]; | |
const enemy = { | |
x: platform.x + Math.random() * (platform.width - 30), | |
y: platform.y - 30, | |
width: 30, | |
height: 30, | |
speed: 1 + Math.random(), | |
direction: Math.random() < 0.5 ? -1 : 1, | |
type: 'ground', | |
element: document.createElement('div') | |
}; | |
enemy.element.classList.add('enemy'); | |
enemy.element.style.left = enemy.x + 'px'; | |
enemy.element.style.top = enemy.y + 'px'; | |
// Add alien features | |
const eyeLeft = document.createElement('div'); | |
eyeLeft.classList.add('alien-eye', 'left'); | |
const eyeRight = document.createElement('div'); | |
eyeRight.classList.add('alien-eye', 'right'); | |
const antennaLeft = document.createElement('div'); | |
antennaLeft.classList.add('antenna', 'left'); | |
const antennaRight = document.createElement('div'); | |
antennaRight.classList.add('antenna', 'right'); | |
enemy.element.appendChild(eyeLeft); | |
enemy.element.appendChild(eyeRight); | |
enemy.element.appendChild(antennaLeft); | |
enemy.element.appendChild(antennaRight); | |
world.appendChild(enemy.element); | |
enemies.push(enemy); | |
} | |
function spawnShootingEnemy() { | |
const enemy = { | |
x: player.x + (Math.random() * 800 + 400) * (Math.random() < 0.5 ? -1 : 1), | |
y: Math.random() * 100 + 50, | |
width: 30, | |
height: 30, | |
speed: 0, // Stationary | |
type: 'shooting', | |
shootCooldown: Math.random() * 100 + 50, | |
element: document.createElement('div') | |
}; | |
enemy.element.classList.add('enemy'); | |
enemy.element.style.left = enemy.x + 'px'; | |
enemy.element.style.top = enemy.y + 'px'; | |
// Add alien features | |
const eyeLeft = document.createElement('div'); | |
eyeLeft.classList.add('alien-eye', 'left'); | |
const eyeRight = document.createElement('div'); | |
eyeRight.classList.add('alien-eye', 'right'); | |
const antennaLeft = document.createElement('div'); | |
antennaLeft.classList.add('antenna', 'left'); | |
const antennaRight = document.createElement('div'); | |
antennaRight.classList.add('antenna', 'right'); | |
enemy.element.appendChild(eyeLeft); | |
enemy.element.appendChild(eyeRight); | |
enemy.element.appendChild(antennaLeft); | |
enemy.element.appendChild(antennaRight); | |
world.appendChild(enemy.element); | |
enemies.push(enemy); | |
} | |
function spawnAmmoDropAt(x, y) { | |
const ammo = { | |
x: x, | |
y: y, | |
width: 15, | |
height: 15, | |
speed: 0, | |
element: document.createElement('div') | |
}; | |
ammo.element.classList.add('ammo'); | |
ammo.element.style.left = ammo.x + 'px'; | |
ammo.element.style.top = ammo.y + 'px'; | |
world.appendChild(ammo.element); | |
ammoDrops.push(ammo); | |
} | |
function spawnAmmoDrop() { | |
const ammo = { | |
x: player.x + (Math.random() * 1000 + 500) * (Math.random() < 0.5 ? -1 : 1), | |
y: 0, | |
width: 15, | |
height: 15, | |
speed: 2, | |
element: document.createElement('div') | |
}; | |
ammo.element.classList.add('ammo'); | |
ammo.element.style.left = ammo.x + 'px'; | |
ammo.element.style.top = ammo.y + 'px'; | |
world.appendChild(ammo.element); | |
ammoDrops.push(ammo); | |
} | |
function shootBullet() { | |
if (player.ammo > 0) { | |
player.ammo--; | |
const bullet = { | |
x: player.x + player.width / 2 - 2.5, | |
y: player.y + player.height / 2, | |
width: 5, | |
height: 5, | |
speed: 7, | |
direction: player.direction, | |
element: document.createElement('div') | |
}; | |
bullet.element.classList.add('bullet'); | |
bullet.element.style.left = bullet.x + 'px'; | |
bullet.element.style.top = bullet.y + 'px'; | |
world.appendChild(bullet.element); | |
bullets.push(bullet); | |
updateScoreBoard(); | |
} | |
} | |
function resetGame() { | |
// Clear previous elements | |
world.innerHTML = ''; | |
createStars(); | |
player.x = 0; | |
// Ensure player spawns on a platform | |
createPlatforms(); | |
const startPlatform = platforms.find(p => player.x >= p.x && player.x <= p.x + p.width); | |
if (startPlatform) { | |
player.y = startPlatform.y - player.height; | |
} else { | |
player.y = 250; // Fallback position | |
} | |
player.velocityX = 0; | |
player.velocityY = 0; | |
player.jumping = false; | |
player.ammo = 5; | |
player.score = 0; | |
createPlayer(); | |
enemies = []; | |
bullets = []; | |
enemyBullets = []; | |
ammoDrops = []; | |
updateScoreBoard(); | |
gameOverScreen.style.display = 'none'; | |
gameOverActive = false; | |
// Append scoreBoard and gameOverScreen to gameContainer | |
gameContainer.appendChild(scoreBoard); | |
gameContainer.appendChild(gameOverScreen); | |
} | |
function updateScoreBoard() { | |
scoreBoard.innerText = `Score: ${player.score} | Ammo: ${player.ammo}`; | |
} | |
function saveHighScore() { | |
let highScores = JSON.parse(localStorage.getItem('highScores')) || []; | |
highScores.push({ name: player.name, score: player.score }); | |
highScores.sort((a, b) => b.score - a.score); | |
highScores = highScores.slice(0, 5); // Keep top 5 scores | |
localStorage.setItem('highScores', JSON.stringify(highScores)); | |
displayHighScores(); | |
} | |
function displayHighScores() { | |
const highScores = JSON.parse(localStorage.getItem('highScores')) || []; | |
highScoreBody.innerHTML = ''; | |
highScores.forEach((entry, index) => { | |
const row = document.createElement('tr'); | |
const rankCell = document.createElement('td'); | |
rankCell.innerText = index + 1; | |
const nameCell = document.createElement('td'); | |
nameCell.innerText = entry.name || 'Anonymous'; | |
const scoreCell = document.createElement('td'); | |
scoreCell.innerText = entry.score !== undefined ? entry.score : '0'; | |
row.appendChild(rankCell); | |
row.appendChild(nameCell); | |
row.appendChild(scoreCell); | |
highScoreBody.appendChild(row); | |
}); | |
} | |
function gameOver() { | |
clearInterval(gameInterval); | |
clearInterval(enemySpawnInterval); | |
clearInterval(ammoDropInterval); | |
// Save high score | |
saveHighScore(); | |
// Display Game Over Screen | |
finalScoreElement.innerText = `Your score: ${player.score}`; | |
gameOverScreen.style.display = 'flex'; | |
playAgainButton.focus(); | |
gameOverActive = true; | |
} | |
function gameLoop() { | |
update(); | |
render(); | |
} | |
function update() { | |
player.moving = false; | |
// Horizontal movement with acceleration and friction | |
if (keys['ArrowRight'] || keys['d']) { | |
player.velocityX += player.acceleration; | |
player.direction = 'right'; | |
player.moving = true; | |
} | |
if (keys['ArrowLeft'] || keys['a']) { | |
player.velocityX -= player.acceleration; | |
player.direction = 'left'; | |
player.moving = true; | |
} | |
// Apply friction when no keys are pressed | |
if (!keys['ArrowLeft'] && !keys['a'] && !keys['ArrowRight'] && !keys['d']) { | |
player.velocityX *= player.friction; | |
if (Math.abs(player.velocityX) < 0.1) { | |
player.velocityX = 0; | |
} | |
} | |
// Cap velocityX to maxSpeed | |
if (player.velocityX > player.maxSpeed) { | |
player.velocityX = player.maxSpeed; | |
} else if (player.velocityX < -player.maxSpeed) { | |
player.velocityX = -player.maxSpeed; | |
} | |
// Update player's position | |
player.x += player.velocityX; | |
// Apply gravity | |
player.velocityY += gravity; | |
player.y += player.velocityY; | |
// Collision detection with platforms | |
let onPlatform = false; | |
platforms.forEach(platform => { | |
if ( | |
player.x < platform.x + platform.width && | |
player.x + player.width > platform.x && | |
player.y + player.height > platform.y && | |
player.y + player.height < platform.y + platform.height && | |
player.velocityY >= 0 | |
) { | |
player.y = platform.y - player.height; | |
player.velocityY = 0; | |
player.jumping = false; | |
onPlatform = true; | |
} | |
}); | |
if (!onPlatform) { | |
player.jumping = true; | |
} | |
// Update camera position | |
camera.x = player.x - gameContainer.offsetWidth / 2 + player.width / 2; | |
camera.y = 0; | |
// Boundaries | |
if (player.y + player.height > gameContainer.offsetHeight) { | |
gameOver(); | |
return; | |
} | |
// Update enemies | |
enemies.forEach((enemy, index) => { | |
if (enemy.type === 'ground') { | |
// Move enemy along platform | |
enemy.x += enemy.speed * enemy.direction; | |
// Reverse direction at platform edges | |
const platform = platforms.find(p => | |
enemy.x + enemy.width > p.x && | |
enemy.x < p.x + p.width && | |
enemy.y + enemy.height === p.y | |
); | |
if ( | |
!platform || | |
enemy.x <= platform.x || | |
enemy.x + enemy.width >= platform.x + platform.width | |
) { | |
enemy.direction *= -1; | |
} | |
enemy.element.style.left = enemy.x + 'px'; | |
} else if (enemy.type === 'shooting') { | |
// Shooting enemy logic | |
enemy.shootCooldown--; | |
if (enemy.shootCooldown <= 0) { | |
enemy.shootCooldown = Math.random() * 100 + 50; | |
// Fire bullet towards player | |
const angle = Math.atan2(player.y - enemy.y, player.x - enemy.x); | |
const enemyBullet = { | |
x: enemy.x + enemy.width / 2, | |
y: enemy.y + enemy.height / 2, | |
width: 5, | |
height: 5, | |
speedX: Math.cos(angle) * 3, | |
speedY: Math.sin(angle) * 3, | |
element: document.createElement('div') | |
}; | |
enemyBullet.element.classList.add('enemy-bullet'); | |
enemyBullet.element.style.left = enemyBullet.x + 'px'; | |
enemyBullet.element.style.top = enemyBullet.y + 'px'; | |
world.appendChild(enemyBullet.element); | |
enemyBullets.push(enemyBullet); | |
} | |
} | |
// Enemy-player collision | |
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 | |
) { | |
// Check if jumping on enemy | |
if (player.velocityY > 0 && player.y + player.height - enemy.y < 20) { | |
// Remove enemy | |
eliminateEnemy(enemy, index); | |
player.velocityY = -10; | |
player.score += 20; | |
updateScoreBoard(); | |
} else { | |
// Game over | |
gameOver(); | |
return; | |
} | |
} | |
// Remove enemy if it goes off-screen | |
if (enemy.y > gameContainer.offsetHeight + 1000 || enemy.x < player.x - 2000 || enemy.x > player.x + 2000) { | |
world.removeChild(enemy.element); | |
enemies.splice(index, 1); | |
} | |
}); | |
// Update enemy bullets | |
enemyBullets.forEach((bullet, index) => { | |
bullet.x += bullet.speedX; | |
bullet.y += bullet.speedY; | |
bullet.element.style.left = bullet.x + 'px'; | |
bullet.element.style.top = bullet.y + 'px'; | |
// Bullet-player collision | |
if ( | |
bullet.x < player.x + player.width && | |
bullet.x + bullet.width > player.x && | |
bullet.y < player.y + player.height && | |
bullet.y + bullet.height > player.y | |
) { | |
// Game over | |
gameOver(); | |
return; | |
} | |
// Remove bullet if it goes off-screen | |
if ( | |
bullet.x < camera.x - 100 || bullet.x > camera.x + gameContainer.offsetWidth + 100 || | |
bullet.y > gameContainer.offsetHeight + 1000 || bullet.y < -1000 | |
) { | |
world.removeChild(bullet.element); | |
enemyBullets.splice(index, 1); | |
} | |
}); | |
// Update player bullets | |
bullets.forEach((bullet, index) => { | |
bullet.x += bullet.speed * (bullet.direction === 'right' ? 1 : -1); | |
bullet.element.style.left = bullet.x + 'px'; | |
// Bullet-enemy collision | |
enemies.forEach((enemy, eIndex) => { | |
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 | |
) { | |
// Remove enemy and bullet | |
eliminateEnemy(enemy, eIndex); | |
world.removeChild(bullet.element); | |
bullets.splice(index, 1); | |
player.score += 20; | |
updateScoreBoard(); | |
} | |
}); | |
// Remove bullet if it goes off-screen | |
if (bullet.x < camera.x - 100 || bullet.x > camera.x + gameContainer.offsetWidth + 100) { | |
world.removeChild(bullet.element); | |
bullets.splice(index, 1); | |
} | |
}); | |
// Update ammo drops | |
ammoDrops.forEach((ammo, index) => { | |
ammo.y += ammo.speed; | |
ammo.element.style.top = ammo.y + 'px'; | |
// Ammo-player collision | |
if ( | |
player.x < ammo.x + ammo.width && | |
player.x + player.width > ammo.x && | |
player.y < ammo.y + ammo.height && | |
player.y + player.height > ammo.y | |
) { | |
// Collect ammo | |
world.removeChild(ammo.element); | |
ammoDrops.splice(index, 1); | |
player.ammo += 5; | |
updateScoreBoard(); | |
} | |
// Remove ammo if it goes off-screen | |
if (ammo.y > gameContainer.offsetHeight + 1000) { | |
world.removeChild(ammo.element); | |
ammoDrops.splice(index, 1); | |
} | |
}); | |
// Generate new platforms ahead | |
generatePlatforms(); | |
// Remove platforms that are far behind | |
platforms = platforms.filter(platform => { | |
if (platform.x + platform.width < camera.x - 1000) { | |
world.removeChild(platform.element); | |
return false; | |
} | |
return true; | |
}); | |
} | |
function eliminateEnemy(enemy, index) { | |
world.removeChild(enemy.element); | |
enemies.splice(index, 1); | |
// 50% chance to drop ammo | |
if (Math.random() < 0.5) { | |
spawnAmmoDropAt(enemy.x + enemy.width / 2 - 7.5, enemy.y + enemy.height / 2 - 7.5); | |
} | |
} | |
function render() { | |
// Update player position | |
player.element.style.left = player.x + 'px'; | |
player.element.style.top = player.y + 'px'; | |
// Flip player based on direction | |
if (player.direction === 'left') { | |
player.element.style.transform = 'scaleX(-1)'; | |
} else { | |
player.element.style.transform = 'scaleX(1)'; | |
} | |
// Add animation classes | |
const legs = player.element.querySelectorAll('.leg'); | |
const arms = player.element.querySelectorAll('.arm'); | |
if (player.moving && !player.jumping) { | |
// Running animation | |
legs.forEach(leg => { | |
leg.style.animation = 'run 0.3s infinite'; | |
}); | |
arms.forEach(arm => { | |
arm.style.animation = 'run 0.3s infinite'; | |
}); | |
} else { | |
// Stop animation | |
legs.forEach(leg => { | |
leg.style.animation = ''; | |
}); | |
arms.forEach(arm => { | |
arm.style.animation = ''; | |
}); | |
} | |
if (player.jumping) { | |
player.element.style.animation = 'jump 0.5s'; | |
} else { | |
player.element.style.animation = ''; | |
} | |
// Update bullets position | |
bullets.forEach(bullet => { | |
bullet.element.style.left = bullet.x + 'px'; | |
}); | |
// Update enemy bullets position | |
enemyBullets.forEach(bullet => { | |
bullet.element.style.left = bullet.x + 'px'; | |
bullet.element.style.top = bullet.y + 'px'; | |
}); | |
// Update world position for camera scrolling | |
world.style.transform = `translateX(${-camera.x}px)`; | |
starsContainer.style.transform = `translateX(${-camera.x * 0.2}px)`; // Parallax effect for stars | |
} | |
function generatePlatforms() { | |
// Generate platforms ahead of the player | |
const lastPlatform = platforms[platforms.length - 1]; | |
if (lastPlatform && lastPlatform.x < player.x + 1000) { | |
const newPlatformX = lastPlatform.x + Math.random() * 200 + 200; | |
const newPlatformY = Math.random() * 200 + 50; | |
const platformData = { | |
x: newPlatformX, | |
y: newPlatformY, | |
width: Math.random() * 150 + 100, | |
height: 15, | |
type: 'static', | |
element: null | |
}; | |
const platform = document.createElement('div'); | |
platform.classList.add('platform'); | |
platform.style.left = platformData.x + 'px'; | |
platform.style.top = platformData.y + 'px'; | |
platform.style.width = platformData.width + 'px'; | |
platform.style.height = platformData.height + 'px'; | |
world.appendChild(platform); | |
platformData.element = platform; | |
platforms.push(platformData); | |
} | |
// Generate platforms behind the player | |
const firstPlatform = platforms[0]; | |
if (firstPlatform && firstPlatform.x > player.x - 1000) { | |
const newPlatformX = firstPlatform.x - (Math.random() * 200 + 200); | |
const newPlatformY = Math.random() * 200 + 50; | |
const platformData = { | |
x: newPlatformX, | |
y: newPlatformY, | |
width: Math.random() * 150 + 100, | |
height: 15, | |
type: 'static', | |
element: null | |
}; | |
const platform = document.createElement('div'); | |
platform.classList.add('platform'); | |
platform.style.left = platformData.x + 'px'; | |
platform.style.top = platformData.y + 'px'; | |
platform.style.width = platformData.width + 'px'; | |
platform.style.height = platformData.height + 'px'; | |
world.appendChild(platform); | |
platformData.element = platform; | |
platforms.unshift(platformData); | |
} | |
} | |
// Event listeners for controls | |
document.addEventListener('keydown', e => { | |
keys[e.key] = true; | |
if ((e.key === 'ArrowUp' || e.key === 'w' || e.key === ' ') && !player.jumping) { | |
player.velocityY = -12; | |
player.jumping = true; | |
} | |
if (e.key === 'Control' || e.key === 'f') { | |
shootBullet(); | |
} | |
// Restart game with Enter key on Game Over screen | |
if (gameOverActive && e.key === 'Enter') { | |
playAgainButton.click(); | |
} | |
}); | |
document.addEventListener('keyup', e => { | |
keys[e.key] = false; | |
}); | |
// Touch controls for mobile devices | |
let touchStartX = null; | |
let touchStartY = null; | |
gameContainer.addEventListener('touchstart', e => { | |
const touch = e.touches[0]; | |
touchStartX = touch.clientX; | |
touchStartY = touch.clientY; | |
}); | |
gameContainer.addEventListener('touchend', e => { | |
if (!touchStartX || !touchStartY) return; | |
const touchEndX = e.changedTouches[0].clientX; | |
const touchEndY = e.changedTouches[0].clientY; | |
const diffX = touchEndX - touchStartX; | |
const diffY = touchEndY - touchStartY; | |
if (Math.abs(diffX) > Math.abs(diffY)) { | |
// Horizontal swipe | |
if (diffX > 0) { | |
// Swipe right | |
player.velocityX += player.acceleration * 5; | |
player.direction = 'right'; | |
} else { | |
// Swipe left | |
player.velocityX -= player.acceleration * 5; | |
player.direction = 'left'; | |
} | |
} else { | |
// Vertical swipe | |
if (diffY < 0 && !player.jumping) { | |
// Swipe up | |
player.velocityY = -12; | |
player.jumping = true; | |
} | |
} | |
// Tap to shoot | |
if (Math.abs(diffX) < 10 && Math.abs(diffY) < 10) { | |
shootBullet(); | |
} | |
touchStartX = null; | |
touchStartY = null; | |
}); | |
// Landing page event listeners | |
playerNameInput.addEventListener('input', () => { | |
startGameButton.disabled = playerNameInput.value.trim() === ''; | |
}); | |
startGameButton.addEventListener('click', () => { | |
player.name = playerNameInput.value.trim().substring(0, 15); | |
landingPage.style.display = 'none'; | |
gameScreen.style.display = 'flex'; | |
startGame(); | |
}); | |
// Play Again button event listener | |
playAgainButton.addEventListener('click', () => { | |
startGame(); | |
}); | |
function startGame() { | |
resetGame(); | |
clearInterval(gameInterval); | |
clearInterval(enemySpawnInterval); | |
clearInterval(ammoDropInterval); | |
gameInterval = setInterval(gameLoop, 20); | |
enemySpawnInterval = setInterval(spawnEnemy, 3000); // Increased spawn rate | |
ammoDropInterval = setInterval(spawnAmmoDrop, 7000); | |
displayHighScores(); | |
} | |
// Initialize game on load | |
window.onload = () => { | |
displayHighScores(); | |
}; | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment