-
- NEW HIGH SCORE!
+
+
+ 🎉 NEW HIGH SCORE 🏆
-
-
-
-
TRY AGAIN
+
+ TRY AGAIN
+
@@ -288,23 +307,32 @@
// 2. GLOBAL GAME STATE & TRACKERS
// ==========================================
// Tracks the current menu/game phase.
- let gameState = 'start';
+ let gameState = 'start';
+
// Current session score.
- let score = 0;
+ let score = 0;
+
// Array to hold {name, score} objects for the leaderboard.
- let highscores = [];
+ let highscores = [];
+
// Time and Speed setup for smooth animations.
let lastTime = 0;
let pipeSpawnTimer = 0;
+
+ // NEW: Party popper 🎉
+ let confettiParticles = [];
// Target pixel distance between pipes. Ensures that when
// the game speeds up, the horizontal gap stays identical.
- const PIPE_DISTANCE = 220;
+ const PIPE_DISTANCE = 220;
+
// Starting speed in pixels per second.
- const BASE_SPEED = 120;
+ const BASE_SPEED = 120;
+
// How much faster to go every 10 points.
- const SPEED_INCREMENT = 15;
+ const SPEED_INCREMENT = 15;
+
// ==========================================
// 3. GAME ENTITIES (BIRD & PIPES)
@@ -355,7 +383,8 @@
update: function(dt) {
if (gameState === 'playing') {
// Apply gravity over time
- this.velocity += this.gravity * dt * 60;
+ this.velocity += this.gravity * dt * 60;
+
// Move the bird by the current velocity
this.y += this.velocity * dt;
@@ -487,8 +516,8 @@
// Clears pipes and resets speed for a new game
reset: function() {
this.position = [];
- this.dx = BASE_SPEED;
- pipeSpawnTimer = 0;
+ this.dx = BASE_SPEED;
+ pipeSpawnTimer = 0;
}
};
@@ -588,7 +617,7 @@
document.getElementById('restartButton').classList.remove('hidden');
// Clear the input box for the next session
- nameInput.value = '';
+ nameInput.value = '';
// Re-render the visual list
renderLeaderboard();
@@ -608,9 +637,10 @@
(highscores.length < 5 || score > highscores[highscores.length - 1].score);
if (isHighScore) {
+ spawnConfetti();
// Show input block, force user to save before restarting
document.getElementById('highscoreEntry').classList.remove('hidden');
- document.getElementById('restartButton').classList.add('hidden');
+ document.getElementById('restartButton').classList.add('hidden');
} else {
// Skip input block, just show standard restart button
document.getElementById('highscoreEntry').classList.add('hidden');
@@ -645,6 +675,54 @@
resetGame();
}
+ // Function to create the confetti particles
+ function spawnConfetti() {
+ for (let i = 0; i < 150; i++) {
+ confettiParticles.push({
+ x: Math.random() * canvas.width,
+ y: -10, // Start just above the screen
+ vx: (Math.random() - 0.5) * 4, // Horizontal drift
+ vy: Math.random() * 3 + 2, // Speed falling
+ rotation: Math.random() * Math.PI * 2, // Starting angle
+ rotationSpeed: (Math.random() - 0.5) * 0.2, // How fast it spins
+ color: `hsl(${Math.random() * 360}, 100%, 50%)`,
+ life: 200 // Lasts a bit longer
+ });
+ }
+ }
+
+ // Function to update and draw confetti particles
+ function updateAndDrawConfetti() {
+ confettiParticles.forEach((p, i) => {
+ p.x += p.vx;
+ p.y += p.vy;
+ p.rotation += p.rotationSpeed; // Update the angle
+ p.life--;
+
+ ctx.save(); // Save the current canvas state
+
+ // Move to the particle's position and rotate
+ ctx.translate(p.x, p.y);
+ ctx.rotate(p.rotation);
+
+ // Draw the Rhomboid centered at 0,0
+ ctx.fillStyle = p.color;
+ ctx.beginPath();
+ const w = 6; // Width
+ const h = 10; // Height
+ ctx.moveTo(0, -h / 2); // Top
+ ctx.lineTo(w / 2, 0); // Right
+ ctx.lineTo(0, h / 2); // Bottom
+ ctx.lineTo(-w / 2, 0); // Left
+ ctx.closePath();
+ ctx.fill();
+
+ ctx.restore(); // Restore state so the rest of the game draws normally
+
+ if (p.life <= 0) confettiParticles.splice(i, 1);
+ });
+ }
+
// ==========================================
// 7. CORE RENDER & UPDATE LOOP
// ==========================================
@@ -654,7 +732,9 @@
drawBackground();
pipes.draw();
drawGround();
- bird.draw();
+ bird.draw();
+ // Draw confetti on top of everything
+ updateAndDrawConfetti();
}
// Master update function that processes physics
@@ -696,7 +776,7 @@
// Only prevent default zooming behavior during active gameplay.
// This allows the highscore form typing to work normally.
if (gameState === 'playing') {
- e.preventDefault();
+ e.preventDefault();
flapBird();
}
}, { passive: false });