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.1; 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.9) { maze[y][x] = DOT; } } else if (currentCell === EMPTY) { maze[y][x] = DOT; } else if (currentCell === DOT) { if (Math.random() < 0.05) { maze[y][x] = WALL; } } } for (let y = 1; y < ROWS - 1; y++) { for (let x = 1; x < COLS - 1; x++) { if (maze[y][x] === EMPTY) { maze[y][x] = DOT; } else 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; const emptyCount = neighbors.filter(c => c === EMPTY).length; if (wallCount === 0 && Math.random() < 0.7) { maze[y][x] = DOT; } else if (emptyCount >= 2 && Math.random() < 0.9) { maze[y][x] = DOT; } } } } console.log('Labyrinthe modifié aléatoirement, tous les trous remplis'); } 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 = '
Aucun score enregistré
'; 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();