2025-11-21 17:09:16 +00:00

279 lines
8.5 KiB
JavaScript

document.addEventListener('DOMContentLoaded', () => {
const canvas = document.getElementById('gameCanvas');
if (!canvas) {
console.error('Canvas element not found!');
return;
}
const ctx = canvas.getContext('2d');
// Set fixed canvas size
canvas.width = 800;
canvas.height = 600;
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
let score = 0;
let lives = 3;
let gameOver = false;
let gameStarted = false;
let orangeInterval;
const player = {
width: 100,
height: 50,
x: canvas.width / 2 - 50,
y: canvas.height - 60,
speed: 500,
dx: 0,
draw() {
ctx.fillStyle = '#8B4513';
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x + this.width, this.y);
ctx.lineTo(this.x + this.width - 15, this.y + this.height);
ctx.lineTo(this.x + 15, this.y + this.height);
ctx.closePath();
ctx.fill();
ctx.strokeStyle = '#5C2F0E';
ctx.lineWidth = 8;
ctx.beginPath();
ctx.moveTo(this.x, this.y);
ctx.lineTo(this.x + this.width, this.y);
ctx.stroke();
}
};
const orangeProps = {
size: 20,
speed: 150,
};
let oranges = [];
function createOrange() {
oranges.push({
x: Math.random() * (canvas.width - orangeProps.size),
y: -orangeProps.size,
size: orangeProps.size,
speed: orangeProps.speed + Math.random() * 100,
draw() {
ctx.beginPath();
ctx.arc(this.x + this.size / 2, this.y + this.size / 2, this.size, 0, Math.PI * 2);
ctx.fillStyle = '#FFA500';
ctx.fill();
ctx.beginPath();
ctx.ellipse(this.x + this.size / 2 + 5, this.y, 8, 3, Math.PI / 4, 0, Math.PI * 2);
ctx.fillStyle = '#228B22';
ctx.fill();
}
});
}
let lastTime = 0;
function update(timestamp) {
if (!gameStarted) return;
if (gameOver) {
drawGameOver();
return;
}
const deltaTime = (timestamp - lastTime) / 1000;
lastTime = timestamp;
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawScene();
player.draw();
player.x += player.dx * player.speed * deltaTime;
if (player.x < 0) player.x = 0;
if (player.x + player.width > canvas.width) {
player.x = canvas.width - player.width;
}
oranges.forEach((orange, index) => {
orange.y += orange.speed * deltaTime;
orange.draw();
if (
orange.y + orange.size > player.y &&
orange.x < player.x + player.width &&
orange.x + orange.size > player.x
) {
score++;
playCatchSound();
oranges.splice(index, 1);
}
if (orange.y > canvas.height) {
lives--;
oranges.splice(index, 1);
if (lives <= 0) {
gameOver = true;
}
}
});
drawUI();
requestAnimationFrame(update);
}
function drawScene() {
ctx.fillStyle = '#0A192F';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#228B22';
ctx.fillRect(0, canvas.height - 20, canvas.width, 20);
}
function drawUI() {
ctx.fillStyle = '#E6F1FF';
ctx.font = '24px Poppins, sans-serif';
ctx.textAlign = 'left';
ctx.fillText(`Score: ${score}`, 20, 40);
ctx.textAlign = 'right';
ctx.fillText(`Lives: ${lives}`, canvas.width - 20, 40);
ctx.textAlign = 'center';
}
function drawGameOver() {
clearInterval(orangeInterval);
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#E6F1FF';
ctx.font = '50px Poppins, sans-serif';
ctx.textAlign = 'center';
ctx.fillText('GAME OVER', canvas.width / 2, canvas.height / 2 - 40);
ctx.font = '30px Poppins, sans-serif';
ctx.fillText(`Final Score: ${score}`, canvas.width / 2, canvas.height / 2 + 20);
ctx.font = '20px Poppins, sans-serif';
ctx.fillText('Click to Restart', canvas.width / 2, canvas.height / 2 + 70);
}
function drawInitialMessage() {
ctx.fillStyle = '#E6F1FF';
ctx.font = '30px Poppins, sans-serif';
ctx.textAlign = 'center';
ctx.fillText('Click "Play Now" to Start!', canvas.width / 2, canvas.height / 2);
}
function playCatchSound() {
if (!audioCtx) return;
const oscillator = audioCtx.createOscillator();
const gainNode = audioCtx.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioCtx.destination);
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(880, audioCtx.currentTime);
gainNode.gain.setValueAtTime(0.1, audioCtx.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.00001, audioCtx.currentTime + 0.5);
oscillator.start(audioCtx.currentTime);
oscillator.stop(audioCtx.currentTime + 0.5);
}
async function updateLeaderboard() {
try {
const response = await fetch('/api/scores/');
const scores = await response.json();
const leaderboardBody = document.getElementById('leaderboard-body');
leaderboardBody.innerHTML = '';
scores.forEach((score, index) => {
const row = `<tr>
<th scope="row">${index + 1}</th>
<td>${score.player_name}</td>
<td>${score.score}</td>
</tr>`;
leaderboardBody.innerHTML += row;
});
} catch (error) {
console.error('Error updating leaderboard:', error);
}
}
function move(e) {
if (!gameStarted || gameOver) return;
if (e.key === 'ArrowRight' || e.key === 'd') {
player.dx = 1;
} else if (e.key === 'ArrowLeft' || e.key === 'a') {
player.dx = -1;
}
}
function stopMove(e) {
if (['ArrowRight', 'd', 'ArrowLeft', 'a'].includes(e.key)) {
player.dx = 0;
}
}
function initGame() {
score = 0;
lives = 3;
oranges = [];
gameOver = false;
gameStarted = false;
player.x = canvas.width / 2 - player.width / 2;
drawScene();
drawInitialMessage();
}
function restartGame() {
score = 0;
lives = 3;
oranges = [];
gameOver = false;
player.x = canvas.width / 2 - player.width / 2;
startCountdown();
}
function startCountdown() {
let count = 3;
const countdown = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawScene();
ctx.fillStyle = '#E6F1FF';
ctx.font = '60px Poppins, sans-serif';
ctx.textAlign = 'center';
if (count > 0) {
ctx.fillText(count, canvas.width / 2, canvas.height / 2);
count--;
} else {
clearInterval(countdown);
ctx.fillText('Go!', canvas.width / 2, canvas.height / 2);
setTimeout(startGame, 500);
}
}, 1000);
}
function startGame() {
gameStarted = true;
lastTime = performance.now();
if (orangeInterval) clearInterval(orangeInterval);
orangeInterval = setInterval(createOrange, 1200);
requestAnimationFrame(update);
}
// Event Listeners
document.addEventListener('keydown', move);
document.addEventListener('keyup', stopMove);
const playButton = document.querySelector('.btn-play');
const gameArea = document.getElementById('game-area');
if (playButton && gameArea) {
playButton.addEventListener('click', (e) => {
e.preventDefault();
gameArea.scrollIntoView({ behavior: 'smooth' });
if (!gameStarted && !gameOver) {
startCountdown();
}
});
}
canvas.addEventListener('click', () => {
if (gameOver) {
restartGame();
}
});
// Initial Setup
initGame();
updateLeaderboard();
});