drop-dodger-game / index.html
LukasBe's picture
Add 3 files
1b7ed30 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Drop Dodger - Rhythm Dodge Game</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>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=Rubik:wght@400;700;900&display=swap');
:root {
--primary: #6c5ce7;
--secondary: #00cec9;
--danger: #ff7675;
--dark: #2d3436;
--light: #f5f6fa;
}
body {
font-family: 'Rubik', sans-serif;
background-color: var(--dark);
color: var(--light);
overflow: hidden;
touch-action: manipulation;
}
.game-font {
font-family: 'Press Start 2P', cursive;
}
.game-container {
perspective: 1000px;
}
.game-lane {
transition: transform 0.1s ease-out;
}
.player-dot {
box-shadow: 0 0 15px 5px rgba(108, 92, 231, 0.7);
animation: pulse 1.5s infinite alternate;
}
.obstacle {
transition: transform 0.05s linear;
will-change: transform;
}
.beat-explosion {
position: absolute;
border-radius: 50%;
background: radial-gradient(circle, var(--secondary), transparent 70%);
transform: translate(-50%, -50%);
pointer-events: none;
z-index: 10;
}
@keyframes pulse {
0% { transform: scale(1); opacity: 1; }
100% { transform: scale(1.2); opacity: 0.8; }
}
@keyframes laneSwitch {
0% { transform: translateX(0); }
50% { transform: translateX(5px); }
100% { transform: translateX(0); }
}
.lane-switch-animation {
animation: laneSwitch 0.2s ease-out;
}
.progress-bar {
background: linear-gradient(90deg, var(--primary), var(--secondary));
height: 5px;
transition: width 0.1s linear;
}
.combo-pop {
animation: comboPop 0.3s ease-out;
transform-origin: center;
}
@keyframes comboPop {
0% { transform: scale(1); }
50% { transform: scale(1.5); }
100% { transform: scale(1); }
}
.game-over-screen {
background: rgba(45, 52, 54, 0.9);
backdrop-filter: blur(5px);
transition: opacity 0.5s ease;
}
.intro-screen {
background: rgba(45, 52, 54, 0.95);
backdrop-filter: blur(5px);
transition: opacity 0.5s ease;
}
.demo-obstacle {
animation: demoObstacle 2s linear infinite;
}
@keyframes demoObstacle {
0% { transform: translateY(-50px); }
100% { transform: translateY(calc(100vh - 100px)); }
}
.demo-player {
animation: demoPlayer 2s ease-in-out infinite;
}
@keyframes demoPlayer {
0%, 100% { left: 25%; }
50% { left: 75%; }
}
.countdown {
animation: countdownPop 0.5s ease-out;
}
@keyframes countdownPop {
0% { transform: scale(0.5); opacity: 0; }
80% { transform: scale(1.2); opacity: 1; }
100% { transform: scale(1); opacity: 1; }
}
.tutorial-highlight {
animation: tutorialHighlight 1.5s ease-in-out infinite alternate;
}
@keyframes tutorialHighlight {
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(108, 92, 231, 0); }
100% { transform: scale(1.05); box-shadow: 0 0 20px 10px rgba(108, 92, 231, 0.4); }
}
</style>
</head>
<body class="h-screen flex flex-col">
<!-- Header Section -->
<header class="p-4 flex justify-between items-center bg-gray-900/50 border-b border-gray-700">
<div class="flex items-center space-x-2">
<i class="fas fa-gamepad text-2xl text-purple-500"></i>
<h1 class="game-font text-xl md:text-2xl text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-cyan-400">
DROP DODGER
</h1>
</div>
<div class="flex items-center space-x-4">
<div class="hidden md:flex items-center space-x-2">
<i class="fas fa-volume-up text-cyan-400"></i>
<input type="range" min="0" max="100" value="70" class="w-20 accent-cyan-400">
</div>
<button id="pause-btn" class="px-3 py-1 rounded-full bg-gray-700 hover:bg-gray-600 transition">
<i class="fas fa-pause"></i>
</button>
</div>
</header>
<!-- Game Stats -->
<div class="p-4 flex justify-between items-center">
<div class="flex items-center space-x-4">
<div class="flex items-center space-x-2">
<i class="fas fa-bolt text-yellow-400"></i>
<span id="score" class="game-font">0</span>
</div>
<div class="flex items-center space-x-2">
<i class="fas fa-fire text-red-400"></i>
<span id="combo" class="game-font">0x</span>
</div>
</div>
<div class="flex items-center space-x-2">
<i class="fas fa-tachometer-alt text-green-400"></i>
<span id="speed" class="game-font">1.0x</span>
</div>
</div>
<!-- Progress Bar -->
<div class="px-4">
<div class="h-1 bg-gray-700 rounded-full overflow-hidden">
<div id="progress-bar" class="progress-bar rounded-full" style="width: 0%"></div>
</div>
</div>
<!-- Game Container -->
<div class="game-container flex-1 relative overflow-hidden">
<div id="game-area" class="h-full w-full relative">
<!-- Lanes -->
<div class="absolute top-0 left-0 w-full h-full flex">
<div id="left-lane" class="game-lane w-1/2 h-full border-r border-gray-700/50"></div>
<div id="right-lane" class="game-lane w-1/2 h-full"></div>
</div>
<!-- Player -->
<div id="player" class="player-dot absolute w-6 h-6 rounded-full bg-gradient-to-br from-purple-500 to-cyan-400 left-1/4 bottom-20 transform -translate-x-1/2 z-10"></div>
<!-- Obstacles will be added here dynamically -->
</div>
<!-- Intro Screen -->
<div id="intro-screen" class="intro-screen absolute inset-0 flex flex-col items-center justify-center z-30">
<div class="text-center p-8 rounded-xl bg-gray-800/90 border border-gray-700 max-w-md w-full">
<h2 class="game-font text-3xl mb-6 text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-cyan-400">
DROP DODGER
</h2>
<!-- Demo Area -->
<div class="relative h-40 mb-6 bg-gray-900 rounded-lg overflow-hidden">
<div class="absolute top-0 left-0 w-full h-full flex">
<div class="w-1/2 h-full border-r border-gray-700/50"></div>
<div class="w-1/2 h-full"></div>
</div>
<!-- Demo Player -->
<div class="demo-player absolute w-4 h-4 rounded-full bg-gradient-to-br from-purple-500 to-cyan-400 bottom-5 transform -translate-x-1/2 z-10"></div>
<!-- Demo Obstacles -->
<div class="demo-obstacle absolute w-1/2 h-4 left-0 top-0 bg-gradient-to-b from-red-500 to-pink-500 rounded-b-lg" style="animation-delay: 0.5s;"></div>
<div class="demo-obstacle absolute w-1/2 h-4 right-0 top-0 bg-gradient-to-b from-blue-500 to-cyan-400 rounded-b-lg" style="animation-delay: 1s;"></div>
<div class="demo-obstacle absolute w-1/2 h-4 left-0 top-0 bg-gradient-to-b from-red-500 to-pink-500 rounded-b-lg" style="animation-delay: 1.5s;"></div>
</div>
<div class="mb-6 text-left space-y-3">
<div class="flex items-start space-x-3">
<div class="flex-shrink-0 mt-1">
<i class="fas fa-hand-pointer text-purple-400"></i>
</div>
<p>TAP anywhere to switch lanes and dodge obstacles</p>
</div>
<div class="flex items-start space-x-3">
<div class="flex-shrink-0 mt-1">
<i class="fas fa-bolt text-yellow-400"></i>
</div>
<p>Score increases as you dodge obstacles</p>
</div>
<div class="flex items-start space-x-3">
<div class="flex-shrink-0 mt-1">
<i class="fas fa-tachometer-alt text-green-400"></i>
</div>
<p>Game speeds up as you progress</p>
</div>
</div>
<button id="start-btn" class="tutorial-highlight px-6 py-3 rounded-full bg-gradient-to-r from-purple-500 to-cyan-500 hover:from-purple-600 hover:to-cyan-600 transition transform hover:scale-105 w-full">
<i class="fas fa-play mr-2"></i> START GAME
</button>
</div>
</div>
<!-- Countdown Screen -->
<div id="countdown-screen" class="absolute inset-0 bg-gray-900/80 flex items-center justify-center opacity-0 pointer-events-none z-20">
<div class="text-center">
<div id="countdown" class="game-font text-8xl text-transparent bg-clip-text bg-gradient-to-r from-purple-400 to-cyan-400">3</div>
</div>
</div>
<!-- Game Over Screen -->
<div id="game-over-screen" class="game-over-screen absolute inset-0 flex flex-col items-center justify-center opacity-0 pointer-events-none z-20">
<div class="text-center p-8 rounded-xl bg-gray-800/90 border border-gray-700 max-w-md w-full">
<h2 class="game-font text-3xl mb-4 text-transparent bg-clip-text bg-gradient-to-r from-red-400 to-purple-500">
GAME OVER
</h2>
<div class="space-y-4 mb-6">
<div class="flex justify-between">
<span class="text-gray-400">Score:</span>
<span id="final-score" class="game-font text-xl">0</span>
</div>
<div class="flex justify-between">
<span class="text-gray-400">Max Combo:</span>
<span id="final-combo" class="game-font text-xl">0x</span>
</div>
<div class="flex justify-between">
<span class="text-gray-400">Difficulty:</span>
<span id="final-difficulty" class="game-font text-xl">1.0x</span>
</div>
</div>
<div class="flex justify-center space-x-4">
<button id="restart-btn" class="px-6 py-2 rounded-full bg-gradient-to-r from-purple-500 to-cyan-500 hover:from-purple-600 hover:to-cyan-600 transition transform hover:scale-105">
<i class="fas fa-redo mr-2"></i> Play Again
</button>
<button id="menu-btn" class="px-6 py-2 rounded-full bg-gray-700 hover:bg-gray-600 transition">
<i class="fas fa-home mr-2"></i> Menu
</button>
</div>
</div>
</div>
<!-- Pause Screen -->
<div id="pause-screen" class="absolute inset-0 bg-gray-900/80 flex items-center justify-center opacity-0 pointer-events-none z-20">
<div class="text-center p-8 rounded-xl bg-gray-800 border border-gray-700 max-w-md w-full">
<h2 class="game-font text-3xl mb-6 text-transparent bg-clip-text bg-gradient-to-r from-yellow-400 to-cyan-400">
PAUSED
</h2>
<div class="flex justify-center space-x-4">
<button id="resume-btn" class="px-6 py-2 rounded-full bg-gradient-to-r from-purple-500 to-cyan-500 hover:from-purple-600 hover:to-cyan-600 transition transform hover:scale-105">
<i class="fas fa-play mr-2"></i> Resume
</button>
<button id="quit-btn" class="px-6 py-2 rounded-full bg-gray-700 hover:bg-gray-600 transition">
<i class="fas fa-sign-out-alt mr-2"></i> Quit
</button>
</div>
</div>
</div>
</div>
<!-- Controls Info -->
<div class="p-4 text-center text-sm text-gray-400 border-t border-gray-700/50">
<p>TAP anywhere to switch lanes • Dodge the obstacles to the beat!</p>
</div>
<script>
// Game State
const gameState = {
score: 0,
combo: 0,
maxCombo: 0,
speed: 1.0,
isPlaying: false,
isPaused: false,
gameOver: false,
playerPosition: 'left', // 'left' or 'right'
obstacleInterval: null,
speedInterval: null,
lastObstacleTime: 0,
obstacles: [],
beatExplosions: [],
difficultyCurve: [
{ threshold: 0, speed: 1.0, spawnRate: 1500 },
{ threshold: 20, speed: 1.2, spawnRate: 1300 },
{ threshold: 50, speed: 1.5, spawnRate: 1000 },
{ threshold: 100, speed: 1.8, spawnRate: 800 },
{ threshold: 200, speed: 2.2, spawnRate: 600 },
{ threshold: 500, speed: 2.8, spawnRate: 400 }
],
currentDifficulty: 0,
isIntro: true
};
// DOM Elements
const elements = {
gameArea: document.getElementById('game-area'),
leftLane: document.getElementById('left-lane'),
rightLane: document.getElementById('right-lane'),
player: document.getElementById('player'),
scoreEl: document.getElementById('score'),
comboEl: document.getElementById('combo'),
speedEl: document.getElementById('speed'),
progressBar: document.getElementById('progress-bar'),
gameOverScreen: document.getElementById('game-over-screen'),
finalScore: document.getElementById('final-score'),
finalCombo: document.getElementById('final-combo'),
finalDifficulty: document.getElementById('final-difficulty'),
pauseScreen: document.getElementById('pause-screen'),
pauseBtn: document.getElementById('pause-btn'),
resumeBtn: document.getElementById('resume-btn'),
quitBtn: document.getElementById('quit-btn'),
restartBtn: document.getElementById('restart-btn'),
menuBtn: document.getElementById('menu-btn'),
introScreen: document.getElementById('intro-screen'),
startBtn: document.getElementById('start-btn'),
countdownScreen: document.getElementById('countdown-screen'),
countdown: document.getElementById('countdown')
};
// Initialize game
function initGame() {
resetGameState();
setupEventListeners();
// Show intro screen by default
elements.introScreen.style.display = 'flex';
gameState.isIntro = true;
}
// Reset game state
function resetGameState() {
gameState.score = 0;
gameState.combo = 0;
gameState.maxCombo = 0;
gameState.speed = 1.0;
gameState.isPlaying = false;
gameState.isPaused = false;
gameState.gameOver = false;
gameState.playerPosition = 'left';
gameState.obstacles = [];
gameState.beatExplosions = [];
gameState.currentDifficulty = 0;
// Clear all obstacles
document.querySelectorAll('.obstacle').forEach(el => el.remove());
document.querySelectorAll('.beat-explosion').forEach(el => el.remove());
// Reset UI
updateScore();
updateCombo();
updateSpeed();
elements.progressBar.style.width = '0%';
elements.gameOverScreen.style.opacity = '0';
elements.gameOverScreen.style.pointerEvents = 'none';
elements.pauseScreen.style.opacity = '0';
elements.pauseScreen.style.pointerEvents = 'none';
elements.countdownScreen.style.opacity = '0';
elements.countdownScreen.style.pointerEvents = 'none';
// Reset player position and visibility
elements.player.style.left = '25%';
elements.player.style.opacity = '1';
elements.player.classList.remove('lane-switch-animation');
void elements.player.offsetWidth; // Trigger reflow
}
// Start game with countdown
function startGameWithCountdown() {
gameState.isIntro = false;
elements.introScreen.style.display = 'none';
elements.countdownScreen.style.opacity = '1';
elements.countdownScreen.style.pointerEvents = 'auto';
let count = 3;
elements.countdown.textContent = count;
elements.countdown.classList.add('countdown');
const countdownInterval = setInterval(() => {
count--;
if (count > 0) {
elements.countdown.textContent = count;
elements.countdown.classList.remove('countdown');
void elements.countdown.offsetWidth;
elements.countdown.classList.add('countdown');
} else {
clearInterval(countdownInterval);
elements.countdownScreen.style.opacity = '0';
elements.countdownScreen.style.pointerEvents = 'none';
startGame();
}
}, 1000);
}
// Start game
function startGame() {
gameState.isPlaying = true;
// Start obstacle spawner
gameState.obstacleInterval = setInterval(spawnObstacle, gameState.difficultyCurve[0].spawnRate);
// Start difficulty progression
gameState.speedInterval = setInterval(updateDifficulty, 1000);
// Start game loop
requestAnimationFrame(gameLoop);
}
// Game loop
function gameLoop(timestamp) {
if (gameState.isPaused || gameState.gameOver) return;
// Move obstacles
moveObstacles();
// Check collisions
checkCollisions();
// Update beat explosions
updateBeatExplosions();
// Continue loop
requestAnimationFrame(gameLoop);
}
// Spawn obstacle
function spawnObstacle() {
if (gameState.isPaused || gameState.gameOver) return;
// Make first few obstacles easier (only one lane at a time)
if (gameState.score < 30 && Math.random() > 0.3) {
return;
}
const lane = Math.random() > 0.5 ? 'right' : 'left';
const obstacle = document.createElement('div');
obstacle.className = `obstacle absolute w-1/2 h-8 ${lane === 'left' ? 'left-0' : 'right-0'} top-0 bg-gradient-to-b ${lane === 'left' ? 'from-red-500 to-pink-500' : 'from-blue-500 to-cyan-400'} rounded-b-lg`;
obstacle.dataset.lane = lane;
elements.gameArea.appendChild(obstacle);
gameState.obstacles.push({
element: obstacle,
lane: lane,
y: 0,
speed: 5 * gameState.speed
});
// Create beat explosion at top
createBeatExplosion(lane === 'left' ? '25%' : '75%', '10%');
}
// Move obstacles
function moveObstacles() {
gameState.obstacles = gameState.obstacles.filter(obstacle => {
obstacle.y += obstacle.speed;
obstacle.element.style.transform = `translateY(${obstacle.y}px)`;
// Remove if out of screen
if (obstacle.y > elements.gameArea.offsetHeight) {
obstacle.element.remove();
// If obstacle passes safely, increase combo
if ((gameState.playerPosition === 'left' && obstacle.lane === 'right') ||
(gameState.playerPosition === 'right' && obstacle.lane === 'left')) {
gameState.combo += 1;
if (gameState.combo > gameState.maxCombo) {
gameState.maxCombo = gameState.combo;
}
updateCombo();
}
return false;
}
return true;
});
}
// Check collisions
function checkCollisions() {
const playerRect = elements.player.getBoundingClientRect();
const gameAreaRect = elements.gameArea.getBoundingClientRect();
const playerTop = playerRect.top - gameAreaRect.top;
const playerBottom = playerRect.bottom - gameAreaRect.top;
const playerLeft = playerRect.left - gameAreaRect.left;
const playerRight = playerRect.right - gameAreaRect.left;
for (const obstacle of gameState.obstacles) {
const obstacleRect = obstacle.element.getBoundingClientRect();
const obstacleTop = obstacleRect.top - gameAreaRect.top;
const obstacleBottom = obstacleRect.bottom - gameAreaRect.top;
const obstacleLeft = obstacleRect.left - gameAreaRect.left;
const obstacleRight = obstacleRect.right - gameAreaRect.left;
// Check if player and obstacle overlap
if (playerBottom > obstacleTop && playerTop < obstacleBottom &&
playerRight > obstacleLeft && playerLeft < obstacleRight) {
// Check if in same lane
if ((gameState.playerPosition === 'left' && obstacle.lane === 'left') ||
(gameState.playerPosition === 'right' && obstacle.lane === 'right')) {
gameOver();
return;
} else {
// Successful dodge - increase score and combo
gameState.score += Math.floor(10 * gameState.speed);
gameState.combo += 1;
if (gameState.combo > gameState.maxCombo) {
gameState.maxCombo = gameState.combo;
}
// Create beat explosion
createBeatExplosion(
obstacle.lane === 'left' ? '25%' : '75%',
`${obstacleTop + obstacleRect.height / 2}px`
);
// Remove obstacle
obstacle.element.remove();
return false;
}
}
}
// Update UI if no collision
updateScore();
updateCombo();
}
// Create beat explosion
function createBeatExplosion(x, y) {
const explosion = document.createElement('div');
explosion.className = 'beat-explosion';
explosion.style.left = x;
explosion.style.top = y;
explosion.style.width = '100px';
explosion.style.height = '100px';
explosion.style.opacity = '0.8';
elements.gameArea.appendChild(explosion);
gameState.beatExplosions.push({
element: explosion,
size: 100,
opacity: 0.8
});
}
// Update beat explosions
function updateBeatExplosions() {
gameState.beatExplosions = gameState.beatExplosions.filter(explosion => {
explosion.size += 5;
explosion.opacity -= 0.02;
explosion.element.style.width = `${explosion.size}px`;
explosion.element.style.height = `${explosion.size}px`;
explosion.element.style.opacity = explosion.opacity;
// Remove if invisible
if (explosion.opacity <= 0) {
explosion.element.remove();
return false;
}
return true;
});
}
// Update difficulty
function updateDifficulty() {
if (gameState.isPaused || gameState.gameOver) return;
// Check if we should increase difficulty
const nextDifficulty = gameState.currentDifficulty + 1;
if (nextDifficulty < gameState.difficultyCurve.length &&
gameState.score >= gameState.difficultyCurve[nextDifficulty].threshold) {
gameState.currentDifficulty = nextDifficulty;
gameState.speed = gameState.difficultyCurve[gameState.currentDifficulty].speed;
// Update spawn rate
clearInterval(gameState.obstacleInterval);
gameState.obstacleInterval = setInterval(
spawnObstacle,
gameState.difficultyCurve[gameState.currentDifficulty].spawnRate
);
updateSpeed();
}
// Update progress bar (0-100% based on score to next level)
let progress = 0;
if (gameState.currentDifficulty < gameState.difficultyCurve.length - 1) {
const currentThreshold = gameState.difficultyCurve[gameState.currentDifficulty].threshold;
const nextThreshold = gameState.difficultyCurve[gameState.currentDifficulty + 1].threshold;
progress = ((gameState.score - currentThreshold) / (nextThreshold - currentThreshold)) * 100;
} else {
progress = 100;
}
elements.progressBar.style.width = `${Math.min(100, progress)}%`;
}
// Switch player lane
function switchLane() {
if (gameState.isPaused || gameState.gameOver || gameState.isIntro) return;
gameState.playerPosition = gameState.playerPosition === 'left' ? 'right' : 'left';
elements.player.style.left = gameState.playerPosition === 'left' ? '25%' : '75%';
// Add animation
elements.player.classList.remove('lane-switch-animation');
void elements.player.offsetWidth; // Trigger reflow
elements.player.classList.add('lane-switch-animation');
// Create small beat explosion at player position
const playerRect = elements.player.getBoundingClientRect();
const gameAreaRect = elements.gameArea.getBoundingClientRect();
const y = playerRect.top - gameAreaRect.top + playerRect.height / 2;
createBeatExplosion(gameState.playerPosition === 'left' ? '25%' : '75%', `${y}px`);
}
// Update score display
function updateScore() {
elements.scoreEl.textContent = gameState.score;
}
// Update combo display
function updateCombo() {
elements.comboEl.textContent = `${gameState.combo}x`;
// Add pop animation for combos over 5
if (gameState.combo >= 5) {
elements.comboEl.classList.add('combo-pop');
setTimeout(() => {
elements.comboEl.classList.remove('combo-pop');
}, 300);
}
}
// Update speed display
function updateSpeed() {
elements.speedEl.textContent = `${gameState.speed.toFixed(1)}x`;
}
// Game over
function gameOver() {
gameState.isPlaying = false;
gameState.gameOver = true;
clearInterval(gameState.obstacleInterval);
clearInterval(gameState.speedInterval);
// Show game over screen
elements.finalScore.textContent = gameState.score;
elements.finalCombo.textContent = `${gameState.maxCombo}x`;
elements.finalDifficulty.textContent = `${gameState.speed.toFixed(1)}x`;
elements.gameOverScreen.style.opacity = '1';
elements.gameOverScreen.style.pointerEvents = 'auto';
// Create explosion at player position
const playerRect = elements.player.getBoundingClientRect();
const gameAreaRect = elements.gameArea.getBoundingClientRect();
const x = gameState.playerPosition === 'left' ? '25%' : '75%';
const y = playerRect.top - gameAreaRect.top + playerRect.height / 2;
const bigExplosion = document.createElement('div');
bigExplosion.className = 'beat-explosion';
bigExplosion.style.left = x;
bigExplosion.style.top = `${y}px`;
bigExplosion.style.width = '200px';
bigExplosion.style.height = '200px';
bigExplosion.style.background = 'radial-gradient(circle, var(--danger), transparent 70%)';
bigExplosion.style.opacity = '0.9';
elements.gameArea.appendChild(bigExplosion);
// Make player disappear
elements.player.style.opacity = '0';
}
// Pause game
function pauseGame() {
if (!gameState.isPlaying || gameState.gameOver || gameState.isIntro) return;
gameState.isPaused = true;
elements.pauseScreen.style.opacity = '1';
elements.pauseScreen.style.pointerEvents = 'auto';
}
// Resume game
function resumeGame() {
if (!gameState.isPaused) return;
gameState.isPaused = false;
elements.pauseScreen.style.opacity = '0';
elements.pauseScreen.style.pointerEvents = 'none';
// Restart game loop
requestAnimationFrame(gameLoop);
}
// Setup event listeners
function setupEventListeners() {
// Lane switch on tap/click
elements.gameArea.addEventListener('click', switchLane);
elements.gameArea.addEventListener('touchstart', (e) => {
e.preventDefault();
switchLane();
});
// Start button
elements.startBtn.addEventListener('click', startGameWithCountdown);
// Pause button
elements.pauseBtn.addEventListener('click', pauseGame);
// Pause screen buttons
elements.resumeBtn.addEventListener('click', resumeGame);
elements.quitBtn.addEventListener('click', () => {
resetGameState();
elements.introScreen.style.display = 'flex';
gameState.isIntro = true;
});
// Game over screen buttons
elements.restartBtn.addEventListener('click', () => {
resetGameState();
startGameWithCountdown();
});
elements.menuBtn.addEventListener('click', () => {
resetGameState();
elements.introScreen.style.display = 'flex';
gameState.isIntro = true;
});
// Pause on window blur
window.addEventListener('blur', pauseGame);
}
// Start the game when page loads
window.addEventListener('DOMContentLoaded', initGame);
</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=LukasBe/drop-dodger-game" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>