premier
This commit is contained in:
797
game.js
Normal file
797
game.js
Normal file
@ -0,0 +1,797 @@
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const scoreElement = document.getElementById('score');
|
||||
const levelElement = document.getElementById('level');
|
||||
const statusElement = document.getElementById('status');
|
||||
const restartBtn = document.getElementById('restartBtn');
|
||||
const usernameInput = document.getElementById('username');
|
||||
const leaderboardElement = document.getElementById('leaderboard');
|
||||
|
||||
const CELL_SIZE = 20;
|
||||
const COLS = 30;
|
||||
const ROWS = 30;
|
||||
|
||||
const WALL = 1;
|
||||
const DOT = 2;
|
||||
const EMPTY = 0;
|
||||
const TUNNEL = 3;
|
||||
const BONUS_CHERRY = 4;
|
||||
const BONUS_LUDO = 5;
|
||||
|
||||
const originalMaze1 = [
|
||||
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,2,2,2,2,2,1,1,2,2,2,2,1,1,1,1,2,2,2,2,1,1,2,2,2,2,2,2,1],
|
||||
[1,1,1,1,1,1,2,1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1,2,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,1,2,1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1,2,1,0,0,0,0,0],
|
||||
[0,0,0,0,0,1,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,2,1,0,0,0,0,0],
|
||||
[0,0,0,0,0,1,2,1,1,0,1,1,1,3,3,3,3,1,1,1,0,1,1,2,1,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,2,1,1,0,1,0,0,0,0,0,0,0,0,1,0,1,1,2,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,0,2,0,0,0,1,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,2,1,1,0,1,0,0,0,0,0,0,0,0,1,0,1,1,2,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,1,2,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,2,1,0,0,0,0,0],
|
||||
[0,0,0,0,0,1,2,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,2,1,0,0,0,0,0],
|
||||
[0,0,0,0,0,1,2,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,2,1,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,2,1,1,0,1,1,1,1,1,1,1,1,1,1,0,1,1,2,1,1,1,1,1,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,2,2,1,1,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,1,1,2,2,2,1],
|
||||
[1,1,1,2,1,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,1,2,1,1,1],
|
||||
[1,1,1,2,1,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,1,2,1,1,1],
|
||||
[1,2,2,2,2,2,2,1,1,2,2,2,2,1,1,1,1,2,2,2,2,1,1,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
|
||||
];
|
||||
|
||||
const originalMaze2 = [
|
||||
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,2,1,1,2,1,1,1,1,2,1,1,2,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,2,2,1,1,2,1,1,2,1,1,2,1,1,1,1,2,1,1,2,1,1,2,1,1,2,2,2,1],
|
||||
[1,1,1,2,1,1,2,2,2,2,1,1,2,2,2,2,2,2,1,1,2,2,2,2,1,1,2,1,1,1],
|
||||
[1,2,2,2,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,2,2,2,1],
|
||||
[1,2,1,1,1,1,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,1,1,1,1,2,1],
|
||||
[1,2,2,2,2,2,2,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,2,2,2,2,2,2,1],
|
||||
[1,1,1,1,1,1,2,1,1,2,2,2,2,2,1,1,2,2,2,2,2,1,1,2,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,0,0,0,0,0],
|
||||
[0,0,0,0,0,1,2,1,1,2,1,0,0,0,0,0,0,0,0,1,2,1,1,2,1,0,0,0,0,0],
|
||||
[0,0,0,0,0,1,2,1,1,2,1,0,1,1,3,3,1,1,0,1,2,1,1,2,1,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,2,1,1,2,1,0,1,0,0,0,0,1,0,1,2,1,1,2,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,0,2,0,0,2,0,0,1,0,0,0,0,1,0,0,2,0,0,2,0,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,2,1,1,2,1,0,1,0,0,0,0,1,0,1,2,1,1,2,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,1,2,1,1,2,1,0,1,1,1,1,1,1,0,1,2,1,1,2,1,0,0,0,0,0],
|
||||
[0,0,0,0,0,1,2,1,1,2,1,0,0,0,0,0,0,0,0,1,2,1,1,2,1,0,0,0,0,0],
|
||||
[0,0,0,0,0,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,2,1,1,2,2,2,2,2,1,1,2,2,2,2,2,1,1,2,1,1,1,1,1,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,1,1,1,2,1,1,2,1,1,1,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,1,1,1,1,2,1,1,2,1,1,1,1,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,2,2,1,1,2,2,2,2,2,2,2,2,0,0,2,2,2,2,2,2,2,2,1,1,2,2,2,1],
|
||||
[1,1,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,1,1],
|
||||
[1,2,2,2,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
|
||||
];
|
||||
|
||||
const originalMaze3 = [
|
||||
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1],
|
||||
[1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,1],
|
||||
[1,1,1,2,1,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,1,2,1,1,1],
|
||||
[1,2,2,2,2,2,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,1,1,1,2,1,1,0,0,0,0,0,0,1,1,2,1,1,1,1,1,1,1,2,1],
|
||||
[1,2,1,1,1,1,1,1,1,2,1,1,0,1,1,1,1,0,1,1,2,1,1,1,1,1,1,1,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,1,1,0,1,3,3,1,0,1,1,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,1,1,1,1,1,1,1,1,2,1,1,0,1,1,1,1,0,1,1,2,1,1,1,1,1,1,1,1,1],
|
||||
[0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0],
|
||||
[1,1,1,1,1,1,1,1,1,2,1,1,0,1,1,1,1,0,1,1,2,1,1,1,1,1,1,1,1,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,1,1,0,1,3,3,1,0,1,1,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,1,1,1,2,1,1,0,1,1,1,1,0,1,1,2,1,1,1,1,1,1,1,2,1],
|
||||
[1,2,1,1,1,1,1,1,1,2,1,1,0,0,0,0,0,0,1,1,2,1,1,1,1,1,1,1,2,1],
|
||||
[1,2,2,2,2,2,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,2,2,2,2,2,1],
|
||||
[1,1,1,2,1,1,2,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,2,1,1,2,1,1,1],
|
||||
[1,2,2,2,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,1],
|
||||
[1,2,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1,1,1,1,1,2,1,1,1,1,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1],
|
||||
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
|
||||
];
|
||||
|
||||
const mazeVariants = [originalMaze1, originalMaze2, originalMaze3];
|
||||
let originalMaze = originalMaze1;
|
||||
let currentMazeIndex = 0;
|
||||
|
||||
let maze = originalMaze.map(row => [...row]);
|
||||
|
||||
let score = 0;
|
||||
let level = 1;
|
||||
let gameRunning = true;
|
||||
let totalDots = 0;
|
||||
const TEST_MODE = true;
|
||||
let cherriesEaten = 0;
|
||||
let isChangingLevel = false;
|
||||
|
||||
class Pacman {
|
||||
constructor() {
|
||||
this.x = 14;
|
||||
this.y = 23;
|
||||
this.direction = 0;
|
||||
this.nextDirection = 0;
|
||||
this.mouthAngle = 0;
|
||||
this.mouthOpen = true;
|
||||
this.speed = 0.15;
|
||||
this.pixelX = this.x * CELL_SIZE + CELL_SIZE / 2;
|
||||
this.pixelY = this.y * CELL_SIZE + CELL_SIZE / 2;
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!gameRunning) return;
|
||||
|
||||
this.mouthAngle += 0.2;
|
||||
if (this.mouthAngle > Math.PI * 2) {
|
||||
this.mouthAngle = 0;
|
||||
this.mouthOpen = !this.mouthOpen;
|
||||
}
|
||||
|
||||
const gridX = Math.floor(this.pixelX / CELL_SIZE);
|
||||
const gridY = Math.floor(this.pixelY / CELL_SIZE);
|
||||
|
||||
if (this.canMove(this.nextDirection)) {
|
||||
this.direction = this.nextDirection;
|
||||
}
|
||||
|
||||
if (this.canMove(this.direction)) {
|
||||
const dx = [0, 1, 0, -1];
|
||||
const dy = [-1, 0, 1, 0];
|
||||
|
||||
this.pixelX += dx[this.direction] * this.speed * CELL_SIZE;
|
||||
this.pixelY += dy[this.direction] * this.speed * CELL_SIZE;
|
||||
|
||||
if (this.pixelX < 0) {
|
||||
this.pixelX = COLS * CELL_SIZE;
|
||||
} else if (this.pixelX > COLS * CELL_SIZE) {
|
||||
this.pixelX = 0;
|
||||
}
|
||||
} else {
|
||||
this.pixelX = gridX * CELL_SIZE + CELL_SIZE / 2;
|
||||
this.pixelY = gridY * CELL_SIZE + CELL_SIZE / 2;
|
||||
}
|
||||
|
||||
this.x = Math.floor(this.pixelX / CELL_SIZE);
|
||||
this.y = Math.floor(this.pixelY / CELL_SIZE);
|
||||
|
||||
this.collectDot();
|
||||
}
|
||||
|
||||
canMove(direction) {
|
||||
const dx = [0, 1, 0, -1];
|
||||
const dy = [-1, 0, 1, 0];
|
||||
|
||||
const nextX = Math.floor((this.pixelX + dx[direction] * CELL_SIZE * 0.5) / CELL_SIZE);
|
||||
const nextY = Math.floor((this.pixelY + dy[direction] * CELL_SIZE * 0.5) / CELL_SIZE);
|
||||
|
||||
if (nextX < 0 || nextX >= COLS || nextY < 0 || nextY >= ROWS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return maze[nextY][nextX] !== WALL;
|
||||
}
|
||||
|
||||
collectDot() {
|
||||
if (maze[this.y][this.x] === DOT) {
|
||||
maze[this.y][this.x] = EMPTY;
|
||||
score += 10;
|
||||
scoreElement.textContent = score;
|
||||
totalDots--;
|
||||
|
||||
if (totalDots === 0 && !isChangingLevel) {
|
||||
console.log('Toutes les pastilles collectées, passage au niveau suivant');
|
||||
nextLevel();
|
||||
}
|
||||
} else if (maze[this.y][this.x] === BONUS_CHERRY) {
|
||||
if (isChangingLevel) {
|
||||
console.log('Changement de niveau en cours, cerise ignorée');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Cerise collectée, cherriesEaten:', cherriesEaten);
|
||||
maze[this.y][this.x] = EMPTY;
|
||||
score += 100;
|
||||
scoreElement.textContent = score;
|
||||
bonuses = bonuses.filter(b => !(b.x === this.x && b.y === this.y && b.type === BONUS_CHERRY));
|
||||
cherriesEaten++;
|
||||
|
||||
console.log('Après incrémentation, cherriesEaten:', cherriesEaten, 'TEST_MODE:', TEST_MODE, 'isChangingLevel:', isChangingLevel);
|
||||
|
||||
if (TEST_MODE && cherriesEaten >= 2 && !isChangingLevel) {
|
||||
console.log('2 cerises mangées, appel de nextLevel()');
|
||||
cherriesEaten = 0;
|
||||
nextLevel();
|
||||
}
|
||||
} else if (maze[this.y][this.x] === BONUS_LUDO) {
|
||||
maze[this.y][this.x] = EMPTY;
|
||||
score += 200;
|
||||
scoreElement.textContent = score;
|
||||
bonuses = bonuses.filter(b => !(b.x === this.x && b.y === this.y && b.type === BONUS_LUDO));
|
||||
}
|
||||
}
|
||||
|
||||
draw() {
|
||||
ctx.save();
|
||||
ctx.translate(this.pixelX, this.pixelY);
|
||||
|
||||
const rotation = [Math.PI * 1.5, 0, Math.PI * 0.5, Math.PI];
|
||||
ctx.rotate(rotation[this.direction]);
|
||||
|
||||
ctx.fillStyle = '#ffd700';
|
||||
ctx.beginPath();
|
||||
|
||||
if (this.mouthOpen) {
|
||||
ctx.arc(0, 0, CELL_SIZE * 0.4, 0.2, Math.PI * 2 - 0.2);
|
||||
} else {
|
||||
ctx.arc(0, 0, CELL_SIZE * 0.4, 0, Math.PI * 2);
|
||||
}
|
||||
|
||||
ctx.lineTo(0, 0);
|
||||
ctx.fill();
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
class Ghost {
|
||||
constructor(x, y, color) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.color = color;
|
||||
this.direction = Math.floor(Math.random() * 4);
|
||||
this.baseSpeed = 0.1;
|
||||
this.speed = this.baseSpeed;
|
||||
this.pixelX = this.x * CELL_SIZE + CELL_SIZE / 2;
|
||||
this.pixelY = this.y * CELL_SIZE + CELL_SIZE / 2;
|
||||
this.moveCounter = 0;
|
||||
}
|
||||
|
||||
updateSpeed() {
|
||||
this.speed = this.baseSpeed * (1 + (level - 1) * 0.15);
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!gameRunning) return;
|
||||
|
||||
this.moveCounter++;
|
||||
|
||||
if (this.moveCounter > 30 || !this.canMove(this.direction)) {
|
||||
const possibleDirections = [];
|
||||
for (let i = 0; i < 4; i++) {
|
||||
if (this.canMove(i)) {
|
||||
possibleDirections.push(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (possibleDirections.length > 0) {
|
||||
this.direction = possibleDirections[Math.floor(Math.random() * possibleDirections.length)];
|
||||
}
|
||||
this.moveCounter = 0;
|
||||
}
|
||||
|
||||
const dx = [0, 1, 0, -1];
|
||||
const dy = [-1, 0, 1, 0];
|
||||
|
||||
this.pixelX += dx[this.direction] * this.speed * CELL_SIZE;
|
||||
this.pixelY += dy[this.direction] * this.speed * CELL_SIZE;
|
||||
|
||||
if (this.pixelX < 0) {
|
||||
this.pixelX = COLS * CELL_SIZE;
|
||||
} else if (this.pixelX > COLS * CELL_SIZE) {
|
||||
this.pixelX = 0;
|
||||
}
|
||||
|
||||
this.x = Math.floor(this.pixelX / CELL_SIZE);
|
||||
this.y = Math.floor(this.pixelY / CELL_SIZE);
|
||||
}
|
||||
|
||||
canMove(direction) {
|
||||
const dx = [0, 1, 0, -1];
|
||||
const dy = [-1, 0, 1, 0];
|
||||
|
||||
const nextX = Math.floor((this.pixelX + dx[direction] * CELL_SIZE * 0.5) / CELL_SIZE);
|
||||
const nextY = Math.floor((this.pixelY + dy[direction] * CELL_SIZE * 0.5) / CELL_SIZE);
|
||||
|
||||
if (nextX < 0 || nextX >= COLS || nextY < 0 || nextY >= ROWS) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return maze[nextY][nextX] !== WALL;
|
||||
}
|
||||
|
||||
draw() {
|
||||
ctx.save();
|
||||
ctx.translate(this.pixelX, this.pixelY);
|
||||
ctx.fillStyle = this.color;
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, -CELL_SIZE * 0.15, CELL_SIZE * 0.3, Math.PI, 0, false);
|
||||
ctx.rect(-CELL_SIZE * 0.3, -CELL_SIZE * 0.15, CELL_SIZE * 0.6, CELL_SIZE * 0.45);
|
||||
ctx.fill();
|
||||
|
||||
ctx.beginPath();
|
||||
ctx.rect(-CELL_SIZE * 0.3, CELL_SIZE * 0.3, CELL_SIZE * 0.2, CELL_SIZE * 0.2);
|
||||
ctx.rect(-CELL_SIZE * 0.1, CELL_SIZE * 0.3, CELL_SIZE * 0.2, CELL_SIZE * 0.2);
|
||||
ctx.rect(CELL_SIZE * 0.1, CELL_SIZE * 0.3, CELL_SIZE * 0.2, CELL_SIZE * 0.2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.beginPath();
|
||||
ctx.arc(-CELL_SIZE * 0.15, -CELL_SIZE * 0.1, CELL_SIZE * 0.08, 0, Math.PI * 2);
|
||||
ctx.arc(CELL_SIZE * 0.15, -CELL_SIZE * 0.1, CELL_SIZE * 0.08, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.beginPath();
|
||||
ctx.arc(-CELL_SIZE * 0.15, -CELL_SIZE * 0.1, CELL_SIZE * 0.04, 0, Math.PI * 2);
|
||||
ctx.arc(CELL_SIZE * 0.15, -CELL_SIZE * 0.1, CELL_SIZE * 0.04, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
class Bonus {
|
||||
constructor(x, y, type) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.type = type;
|
||||
this.animation = 0;
|
||||
}
|
||||
|
||||
update() {
|
||||
this.animation += 0.1;
|
||||
if (this.animation > Math.PI * 2) {
|
||||
this.animation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
draw() {
|
||||
const cellX = this.x * CELL_SIZE + CELL_SIZE / 2;
|
||||
const cellY = this.y * CELL_SIZE + CELL_SIZE / 2;
|
||||
const scale = 1 + Math.sin(this.animation) * 0.2;
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(cellX, cellY);
|
||||
ctx.scale(scale, scale);
|
||||
|
||||
if (this.type === BONUS_CHERRY) {
|
||||
ctx.fillStyle = '#ff0000';
|
||||
ctx.beginPath();
|
||||
ctx.arc(0, 0, CELL_SIZE * 0.25, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.fillStyle = '#00ff00';
|
||||
ctx.beginPath();
|
||||
ctx.arc(-CELL_SIZE * 0.15, -CELL_SIZE * 0.2, CELL_SIZE * 0.1, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
ctx.strokeStyle = '#00aa00';
|
||||
ctx.lineWidth = 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(-CELL_SIZE * 0.15, -CELL_SIZE * 0.3);
|
||||
ctx.lineTo(-CELL_SIZE * 0.25, -CELL_SIZE * 0.4);
|
||||
ctx.stroke();
|
||||
} else if (this.type === BONUS_LUDO) {
|
||||
ctx.fillStyle = '#ffd700';
|
||||
ctx.font = `bold ${CELL_SIZE * 0.4}px Arial`;
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.fillText('L', 0, 0);
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
let pacman = new Pacman();
|
||||
const ghosts = [
|
||||
new Ghost(14, 11, '#ff0000'),
|
||||
new Ghost(15, 11, '#ff00ff'),
|
||||
new Ghost(14, 12, '#00ffff'),
|
||||
new Ghost(15, 12, '#ffa500')
|
||||
];
|
||||
|
||||
let bonuses = [];
|
||||
|
||||
function countDots() {
|
||||
totalDots = 0;
|
||||
if (!maze || maze.length === 0) {
|
||||
console.error('countDots() - maze est vide ou undefined');
|
||||
return;
|
||||
}
|
||||
for (let y = 0; y < ROWS; y++) {
|
||||
if (!maze[y]) {
|
||||
console.error(`countDots() - maze[${y}] est undefined`);
|
||||
continue;
|
||||
}
|
||||
for (let x = 0; x < COLS; x++) {
|
||||
if (maze[y][x] === DOT) {
|
||||
totalDots++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawMaze() {
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
for (let y = 0; y < ROWS; y++) {
|
||||
for (let x = 0; x < COLS; x++) {
|
||||
const cellX = x * CELL_SIZE;
|
||||
const cellY = y * CELL_SIZE;
|
||||
|
||||
if (maze[y][x] === WALL) {
|
||||
ctx.fillStyle = '#0000ff';
|
||||
ctx.fillRect(cellX, cellY, CELL_SIZE, CELL_SIZE);
|
||||
ctx.strokeStyle = '#000080';
|
||||
ctx.strokeRect(cellX, cellY, CELL_SIZE, CELL_SIZE);
|
||||
} else if (maze[y][x] === DOT) {
|
||||
ctx.fillStyle = '#fff';
|
||||
ctx.beginPath();
|
||||
ctx.arc(cellX + CELL_SIZE / 2, cellY + CELL_SIZE / 2, 2, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let bonus of bonuses) {
|
||||
bonus.update();
|
||||
bonus.draw();
|
||||
}
|
||||
}
|
||||
|
||||
function checkCollisions() {
|
||||
if (!gameRunning) return;
|
||||
|
||||
for (let ghost of ghosts) {
|
||||
const distance = Math.sqrt(
|
||||
Math.pow(pacman.pixelX - ghost.pixelX, 2) +
|
||||
Math.pow(pacman.pixelY - ghost.pixelY, 2)
|
||||
);
|
||||
|
||||
if (distance < CELL_SIZE * 0.6) {
|
||||
gameRunning = false;
|
||||
statusElement.textContent = 'Game Over !';
|
||||
restartBtn.style.display = 'block';
|
||||
saveScore();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function gameLoop() {
|
||||
if (isChangingLevel || !gameRunning) {
|
||||
if (isChangingLevel) {
|
||||
console.log('gameLoop() - Changement de niveau en cours, arrêt');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
drawMaze();
|
||||
pacman.update();
|
||||
pacman.draw();
|
||||
|
||||
for (let ghost of ghosts) {
|
||||
ghost.update();
|
||||
ghost.draw();
|
||||
}
|
||||
|
||||
checkCollisions();
|
||||
|
||||
if (gameRunning && !isChangingLevel) {
|
||||
requestAnimationFrame(gameLoop);
|
||||
} else {
|
||||
console.log('gameLoop() - Arrêt de la boucle, gameRunning:', gameRunning, 'isChangingLevel:', isChangingLevel);
|
||||
}
|
||||
}
|
||||
|
||||
function nextLevel() {
|
||||
console.log('=== nextLevel() appelée ===');
|
||||
console.log('gameRunning:', gameRunning, 'isChangingLevel:', isChangingLevel);
|
||||
|
||||
if (!gameRunning || isChangingLevel) {
|
||||
console.log('nextLevel() annulée - gameRunning:', gameRunning, 'isChangingLevel:', isChangingLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('Début du changement de niveau');
|
||||
isChangingLevel = true;
|
||||
const wasRunning = gameRunning;
|
||||
gameRunning = false;
|
||||
console.log('gameRunning mis à false, wasRunning:', wasRunning);
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
console.log('Dans requestAnimationFrame, changement du niveau');
|
||||
level++;
|
||||
console.log('Nouveau niveau:', level);
|
||||
levelElement.textContent = level;
|
||||
cherriesEaten = 0;
|
||||
|
||||
const mazeIndex = (level - 1) % mazeVariants.length;
|
||||
console.log('Index du labyrinthe:', mazeIndex);
|
||||
currentMazeIndex = mazeIndex;
|
||||
const newMaze = mazeVariants[mazeIndex];
|
||||
|
||||
console.log('Création du nouveau labyrinthe');
|
||||
console.log('newMaze.length:', newMaze.length, 'ROWS:', ROWS);
|
||||
console.log('newMaze[0]?.length:', newMaze[0]?.length, 'COLS:', COLS);
|
||||
|
||||
if (!newMaze || newMaze.length !== ROWS) {
|
||||
console.error('Erreur: Le nouveau labyrinthe a une taille incorrecte:', newMaze?.length);
|
||||
isChangingLevel = false;
|
||||
gameRunning = wasRunning;
|
||||
return;
|
||||
}
|
||||
|
||||
maze = [];
|
||||
for (let i = 0; i < newMaze.length; i++) {
|
||||
if (!newMaze[i] || newMaze[i].length !== COLS) {
|
||||
console.error(`Erreur: Ligne ${i} du labyrinthe a une taille incorrecte:`, newMaze[i]?.length);
|
||||
}
|
||||
maze[i] = [...newMaze[i]];
|
||||
}
|
||||
|
||||
console.log('Labyrinthe copié, maze.length:', maze.length);
|
||||
|
||||
randomizeMaze();
|
||||
|
||||
countDots();
|
||||
console.log('Total dots:', totalDots);
|
||||
|
||||
bonuses = [];
|
||||
|
||||
console.log('Réinitialisation de Pacman et des fantômes');
|
||||
pacman = new Pacman();
|
||||
ghosts[0] = new Ghost(14, 11, '#ff0000');
|
||||
ghosts[1] = new Ghost(15, 11, '#ff00ff');
|
||||
ghosts[2] = new Ghost(14, 12, '#00ffff');
|
||||
ghosts[3] = new Ghost(15, 12, '#ffa500');
|
||||
|
||||
for (let ghost of ghosts) {
|
||||
ghost.updateSpeed();
|
||||
}
|
||||
|
||||
placeBonuses();
|
||||
console.log('Bonus placés, nombre:', bonuses.length);
|
||||
|
||||
statusElement.textContent = `Niveau ${level} - Labyrinthe ${mazeIndex + 1} !`;
|
||||
statusElement.style.color = '#00ff00';
|
||||
|
||||
console.log('Redessin du labyrinthe');
|
||||
drawMaze();
|
||||
pacman.draw();
|
||||
for (let ghost of ghosts) {
|
||||
ghost.draw();
|
||||
}
|
||||
|
||||
console.log('Attente de 2 secondes avant redémarrage');
|
||||
setTimeout(() => {
|
||||
console.log('Redémarrage de la boucle de jeu');
|
||||
isChangingLevel = false;
|
||||
gameRunning = wasRunning;
|
||||
console.log('isChangingLevel:', isChangingLevel, 'gameRunning:', gameRunning);
|
||||
if (gameRunning) {
|
||||
statusElement.textContent = 'En jeu';
|
||||
statusElement.style.color = '#ffd700';
|
||||
console.log('Appel de gameLoop() pour redémarrer');
|
||||
gameLoop();
|
||||
}
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function randomizeMaze() {
|
||||
const modificationRate = 0.15;
|
||||
const changes = Math.floor(ROWS * COLS * modificationRate);
|
||||
|
||||
console.log('Modification aléatoire du labyrinthe,', changes, 'changements');
|
||||
|
||||
for (let i = 0; i < changes; i++) {
|
||||
const x = Math.floor(Math.random() * COLS);
|
||||
const y = Math.floor(Math.random() * ROWS);
|
||||
|
||||
if (x === 0 || x === COLS - 1 || y === 0 || y === ROWS - 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const currentCell = maze[y][x];
|
||||
|
||||
if (currentCell === WALL) {
|
||||
if (Math.random() < 0.7) {
|
||||
maze[y][x] = DOT;
|
||||
} else {
|
||||
maze[y][x] = EMPTY;
|
||||
}
|
||||
} else if (currentCell === EMPTY) {
|
||||
if (Math.random() < 0.6) {
|
||||
maze[y][x] = DOT;
|
||||
} else if (Math.random() < 0.3) {
|
||||
maze[y][x] = WALL;
|
||||
}
|
||||
} else if (currentCell === DOT) {
|
||||
if (Math.random() < 0.3) {
|
||||
maze[y][x] = EMPTY;
|
||||
} else if (Math.random() < 0.1) {
|
||||
maze[y][x] = WALL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let y = 1; y < ROWS - 1; y++) {
|
||||
for (let x = 1; x < COLS - 1; x++) {
|
||||
if (maze[y][x] === WALL) {
|
||||
const neighbors = [
|
||||
maze[y-1][x], maze[y+1][x], maze[y][x-1], maze[y][x+1]
|
||||
];
|
||||
const wallCount = neighbors.filter(c => c === WALL).length;
|
||||
|
||||
if (wallCount === 0 && Math.random() < 0.4) {
|
||||
maze[y][x] = DOT;
|
||||
} else if (wallCount === 4 && Math.random() < 0.3) {
|
||||
maze[y][x] = EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Labyrinthe modifié aléatoirement');
|
||||
}
|
||||
|
||||
function placeBonuses() {
|
||||
bonuses = [];
|
||||
const bonusPositions = [
|
||||
{x: 1, y: 1, type: BONUS_CHERRY},
|
||||
{x: 28, y: 1, type: BONUS_CHERRY},
|
||||
{x: 1, y: 28, type: BONUS_CHERRY},
|
||||
{x: 28, y: 28, type: BONUS_CHERRY},
|
||||
{x: 14, y: 14, type: BONUS_LUDO},
|
||||
{x: 15, y: 14, type: BONUS_LUDO}
|
||||
];
|
||||
|
||||
for (let pos of bonusPositions) {
|
||||
if (maze[pos.y][pos.x] === EMPTY || maze[pos.y][pos.x] === DOT) {
|
||||
maze[pos.y][pos.x] = pos.type;
|
||||
bonuses.push(new Bonus(pos.x, pos.y, pos.type));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initGame() {
|
||||
currentMazeIndex = 0;
|
||||
originalMaze = mazeVariants[0];
|
||||
maze = originalMaze.map(row => [...row]);
|
||||
countDots();
|
||||
score = 0;
|
||||
level = 1;
|
||||
cherriesEaten = 0;
|
||||
scoreElement.textContent = score;
|
||||
levelElement.textContent = level;
|
||||
gameRunning = true;
|
||||
statusElement.textContent = 'En jeu';
|
||||
statusElement.style.color = '#ffd700';
|
||||
restartBtn.style.display = 'none';
|
||||
|
||||
pacman = new Pacman();
|
||||
ghosts[0] = new Ghost(14, 11, '#ff0000');
|
||||
ghosts[1] = new Ghost(15, 11, '#ff00ff');
|
||||
ghosts[2] = new Ghost(14, 12, '#00ffff');
|
||||
ghosts[3] = new Ghost(15, 12, '#ffa500');
|
||||
|
||||
for (let ghost of ghosts) {
|
||||
ghost.updateSpeed();
|
||||
}
|
||||
|
||||
placeBonuses();
|
||||
countDots();
|
||||
gameLoop();
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (!gameRunning) return;
|
||||
|
||||
switch(e.key) {
|
||||
case 'ArrowUp':
|
||||
pacman.nextDirection = 0;
|
||||
e.preventDefault();
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
pacman.nextDirection = 1;
|
||||
e.preventDefault();
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
pacman.nextDirection = 2;
|
||||
e.preventDefault();
|
||||
break;
|
||||
case 'ArrowLeft':
|
||||
pacman.nextDirection = 3;
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
function getScores() {
|
||||
const scoresJson = localStorage.getItem('pacmanScores');
|
||||
return scoresJson ? JSON.parse(scoresJson) : [];
|
||||
}
|
||||
|
||||
function saveScore() {
|
||||
const username = usernameInput.value.trim() || 'Anonyme';
|
||||
if (score > 0) {
|
||||
const scores = getScores();
|
||||
scores.push({
|
||||
username: username,
|
||||
score: score,
|
||||
date: new Date().toISOString()
|
||||
});
|
||||
scores.sort((a, b) => b.score - a.score);
|
||||
const topScores = scores.slice(0, 10);
|
||||
localStorage.setItem('pacmanScores', JSON.stringify(topScores));
|
||||
updateLeaderboard();
|
||||
}
|
||||
}
|
||||
|
||||
function updateLeaderboard() {
|
||||
const scores = getScores();
|
||||
leaderboardElement.innerHTML = '';
|
||||
|
||||
if (scores.length === 0) {
|
||||
leaderboardElement.innerHTML = '<div class="empty-leaderboard">Aucun score enregistré</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
scores.forEach((entry, index) => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'leaderboard-item' + (index < 3 ? ' top' : '');
|
||||
|
||||
const rank = document.createElement('div');
|
||||
rank.className = 'leaderboard-rank';
|
||||
rank.textContent = (index + 1) + '.';
|
||||
|
||||
const name = document.createElement('div');
|
||||
name.className = 'leaderboard-name';
|
||||
name.textContent = entry.username;
|
||||
|
||||
const scoreDiv = document.createElement('div');
|
||||
scoreDiv.className = 'leaderboard-score';
|
||||
scoreDiv.textContent = entry.score;
|
||||
|
||||
item.appendChild(rank);
|
||||
item.appendChild(name);
|
||||
item.appendChild(scoreDiv);
|
||||
leaderboardElement.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
restartBtn.addEventListener('click', () => {
|
||||
initGame();
|
||||
});
|
||||
|
||||
updateLeaderboard();
|
||||
initGame();
|
||||
|
||||
Reference in New Issue
Block a user