Files
pacmanludo/src/components/Game/Game.js

227 lines
8.2 KiB
JavaScript

import React, { useState, useEffect, useCallback } from 'react';
import { Pacman, Ghost, Bonus } from '../../utils/entities';
import { mazeVariants } from '../../utils/mazeData';
import { fillEmptySpaces, randomizeMaze, getBonusPositions, countDots } from '../../utils/gameLogic';
import GameCanvas from '../GameCanvas/GameCanvas';
import GameInfo from '../GameInfo/GameInfo';
import UserInput from '../UserInput/UserInput';
import Leaderboard from '../Leaderboard/Leaderboard';
import { useGameState } from '../../hooks/useGameState';
import { useKeyboard } from '../../hooks/useKeyboard';
import { useLeaderboard } from '../../hooks/useLeaderboard';
import { saveScore } from '../../utils/leaderboard';
function Game() {
const gameState = useGameState();
const { scores, addScore: addLeaderboardScore } = useLeaderboard();
const [username, setUsername] = useState('');
const [maze, setMaze] = useState([]);
const [pacman, setPacman] = useState(null);
const [ghosts, setGhosts] = useState([]);
const [bonuses, setBonuses] = useState([]);
const initializeGame = useCallback(() => {
const initialMaze = mazeVariants[0].map(row => [...row]);
fillEmptySpaces(initialMaze);
randomizeMaze(initialMaze);
setMaze(initialMaze);
const newPacman = new Pacman();
newPacman.speed = newPacman.baseSpeed * (1 + (gameState.level - 1) * 0.05);
setPacman(newPacman);
const newGhosts = [
new Ghost(14, 11, '#ff0000'),
new Ghost(15, 11, '#ff00ff'),
new Ghost(14, 12, '#00ffff'),
new Ghost(15, 12, '#ffa500')
];
newGhosts.forEach(ghost => ghost.updateSpeed(gameState.level));
setGhosts(newGhosts);
const bonusPositions = getBonusPositions();
const newBonuses = [];
const mazeCopy = initialMaze.map(row => [...row]);
for (let pos of bonusPositions) {
if (mazeCopy[pos.y][pos.x] === 0 || mazeCopy[pos.y][pos.x] === 2) {
mazeCopy[pos.y][pos.x] = pos.type;
newBonuses.push(new Bonus(pos.x, pos.y, pos.type));
}
}
setBonuses(newBonuses);
setMaze(mazeCopy);
}, [gameState.level]);
useEffect(() => {
initializeGame();
gameState.resetGame();
}, []);
const handleCollect = useCallback((type, points) => {
gameState.addScore(points);
if (type === 'cherry') {
const newCherriesEaten = gameState.cherriesEaten + 1;
gameState.setCherriesEaten(newCherriesEaten);
gameState.setCherryEatenRecently(true);
gameState.setCherryEatenTimer(Math.max(150, 300 - (gameState.level - 1) * 20));
}
}, [gameState]);
const handleCollision = useCallback(() => {
const newLives = gameState.lives - 1;
gameState.setLives(newLives);
if (newLives <= 0) {
gameState.setGameRunning(false);
gameState.setStatus('Game Over !');
if (username.trim() !== '') {
saveScore(username, gameState.score);
addLeaderboardScore(username, gameState.score);
}
} else {
gameState.restartLevel();
setTimeout(() => {
initializeGame();
gameState.setIsChangingLevel(false);
gameState.setGameRunning(true);
gameState.setStatus('En jeu');
}, 2000);
}
}, [gameState, username, initializeGame, addLeaderboardScore]);
const handleNextLevel = useCallback(() => {
const newLevel = gameState.level + 1;
gameState.setLevel(newLevel);
gameState.setCherriesEaten(0);
gameState.setIsChangingLevel(true);
gameState.setCherryEatenRecently(false);
gameState.setCherryEatenTimer(0);
setTimeout(() => {
const mazeIndex = (newLevel - 1) % mazeVariants.length;
const newMaze = mazeVariants[mazeIndex].map(row => [...row]);
fillEmptySpaces(newMaze);
randomizeMaze(newMaze);
setMaze(newMaze);
const newPacman = new Pacman();
newPacman.speed = newPacman.baseSpeed * (1 + (newLevel - 1) * 0.05);
setPacman(newPacman);
const newGhosts = [
new Ghost(14, 11, '#ff0000'),
new Ghost(15, 11, '#ff00ff'),
new Ghost(14, 12, '#00ffff'),
new Ghost(15, 12, '#ffa500')
];
newGhosts.forEach(ghost => ghost.updateSpeed(newLevel));
setGhosts(newGhosts);
const bonusPositions = getBonusPositions();
const newBonuses = [];
const mazeCopy = newMaze.map(row => [...row]);
for (let pos of bonusPositions) {
if (mazeCopy[pos.y][pos.x] === 0 || mazeCopy[pos.y][pos.x] === 2) {
mazeCopy[pos.y][pos.x] = pos.type;
newBonuses.push(new Bonus(pos.x, pos.y, pos.type));
}
}
setBonuses(newBonuses);
setMaze(mazeCopy);
gameState.setIsChangingLevel(false);
gameState.setGameRunning(true);
gameState.setStatus('En jeu');
}, 2000);
}, [gameState]);
const handleKeyPress = useCallback((key) => {
if (!gameState.gameRunning || !pacman) return;
switch(key) {
case 'ArrowUp':
pacman.nextDirection = 0;
break;
case 'ArrowRight':
pacman.nextDirection = 1;
break;
case 'ArrowDown':
pacman.nextDirection = 2;
break;
case 'ArrowLeft':
pacman.nextDirection = 3;
break;
default:
break;
}
}, [gameState.gameRunning, pacman]);
useKeyboard(handleKeyPress);
const handleRestart = useCallback(() => {
initializeGame();
gameState.resetGame();
}, [initializeGame, gameState]);
useEffect(() => {
if (gameState.cherryEatenTimer > 0) {
const timer = setInterval(() => {
const newTimer = gameState.cherryEatenTimer - 1;
if (newTimer <= 0) {
gameState.setCherryEatenRecently(false);
gameState.setCherryEatenTimer(0);
} else {
gameState.setCherryEatenTimer(newTimer);
}
}, 16);
return () => clearInterval(timer);
}
}, [gameState.cherryEatenTimer, gameState]);
useEffect(() => {
if (gameState.cherriesEaten >= 4 && !gameState.isChangingLevel && gameState.gameRunning) {
handleNextLevel();
}
}, [gameState.cherriesEaten, gameState.isChangingLevel, gameState.gameRunning, handleNextLevel]);
return (
<div className="main-wrapper">
<div className="container">
<h1>OULVIC</h1>
<UserInput username={username} onUsernameChange={setUsername} />
<GameInfo
score={gameState.score}
level={gameState.level}
lives={gameState.lives}
status={gameState.status}
/>
<GameCanvas
maze={maze}
pacman={pacman}
ghosts={ghosts}
bonuses={bonuses}
gameRunning={gameState.gameRunning}
isChangingLevel={gameState.isChangingLevel}
level={gameState.level}
cherryEatenRecently={gameState.cherryEatenRecently}
onCollect={handleCollect}
onCollision={handleCollision}
/>
<div className="instructions">
<p>Utilisez les flèches directionnelles pour déplacer Oulvic</p>
{!gameState.gameRunning && (
<button id="restartBtn" onClick={handleRestart}>
Rejouer
</button>
)}
</div>
</div>
<Leaderboard />
</div>
);
}
export default Game;