Compare commits
2 Commits
da0671f612
...
34fd7d1f4e
| Author | SHA1 | Date | |
|---|---|---|---|
| 34fd7d1f4e | |||
| 393377d3fc |
726
game.js
726
game.js
@ -18,9 +18,12 @@ const mainMenu = document.getElementById('mainMenu');
|
|||||||
const gameWrapper = document.getElementById('gameWrapper');
|
const gameWrapper = document.getElementById('gameWrapper');
|
||||||
const playBtn = document.getElementById('playBtn');
|
const playBtn = document.getElementById('playBtn');
|
||||||
const scoresBtn = document.getElementById('scoresBtn');
|
const scoresBtn = document.getElementById('scoresBtn');
|
||||||
|
const rulesBtn = document.getElementById('rulesBtn');
|
||||||
const backToMenuBtn = document.getElementById('backToMenuBtn');
|
const backToMenuBtn = document.getElementById('backToMenuBtn');
|
||||||
const leaderboardModal = document.getElementById('leaderboardModal');
|
const leaderboardModal = document.getElementById('leaderboardModal');
|
||||||
const closeModalBtn = document.getElementById('closeModalBtn');
|
const closeModalBtn = document.getElementById('closeModalBtn');
|
||||||
|
const rulesModal = document.getElementById('rulesModal');
|
||||||
|
const closeRulesBtn = document.getElementById('closeRulesBtn');
|
||||||
const menuLeaderboard = document.getElementById('menuLeaderboard');
|
const menuLeaderboard = document.getElementById('menuLeaderboard');
|
||||||
const leaderboardContainer = document.getElementById('leaderboardContainer');
|
const leaderboardContainer = document.getElementById('leaderboardContainer');
|
||||||
const closeLeaderboardBtn = document.getElementById('closeLeaderboardBtn');
|
const closeLeaderboardBtn = document.getElementById('closeLeaderboardBtn');
|
||||||
@ -37,6 +40,39 @@ const EMPTY = 0;
|
|||||||
const TUNNEL = 3;
|
const TUNNEL = 3;
|
||||||
const BONUS_CHERRY = 4;
|
const BONUS_CHERRY = 4;
|
||||||
const BONUS_LUDO = 5;
|
const BONUS_LUDO = 5;
|
||||||
|
const BONUS_SPEED = 6;
|
||||||
|
const BONUS_SHIELD = 7;
|
||||||
|
const BONUS_BOMB = 8;
|
||||||
|
const BONUS_MULTIPLIER = 9;
|
||||||
|
|
||||||
|
// Types de fantômes
|
||||||
|
const GHOST_HUNTER = 'hunter';
|
||||||
|
const GHOST_PATROL = 'patrol';
|
||||||
|
const GHOST_FAST = 'fast';
|
||||||
|
const GHOST_INVISIBLE = 'invisible';
|
||||||
|
const GHOST_NORMAL = 'normal';
|
||||||
|
|
||||||
|
// Zones spéciales
|
||||||
|
const ZONE_TELEPORT = 10;
|
||||||
|
const ZONE_BONUS = 11;
|
||||||
|
const ZONE_DANGER = 12;
|
||||||
|
|
||||||
|
// Fonction pour obtenir le type de fruit selon le niveau
|
||||||
|
function getFruitType() {
|
||||||
|
const fruits = [
|
||||||
|
{ name: 'cerise', color: '#ff0000', stemColor: '#00ff00', level: 1 },
|
||||||
|
{ name: 'banane', color: '#ffff00', stemColor: '#00aa00', level: 2 },
|
||||||
|
{ name: 'orange', color: '#ff8800', stemColor: '#00aa00', level: 3 },
|
||||||
|
{ name: 'pomme', color: '#ff0000', stemColor: '#8b4513', level: 4 },
|
||||||
|
{ name: 'raisin', color: '#8b00ff', stemColor: '#00aa00', level: 5 },
|
||||||
|
{ name: 'fraise', color: '#ff0066', stemColor: '#00ff00', level: 6 },
|
||||||
|
{ name: 'ananas', color: '#ffd700', stemColor: '#228b22', level: 7 }
|
||||||
|
];
|
||||||
|
|
||||||
|
// Trouver le fruit correspondant au niveau (avec rotation pour les niveaux élevés)
|
||||||
|
const fruitIndex = Math.min(level - 1, fruits.length - 1);
|
||||||
|
return fruits[fruitIndex];
|
||||||
|
}
|
||||||
|
|
||||||
const originalMaze1 = [
|
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,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],
|
||||||
@ -156,6 +192,44 @@ let lives = 3;
|
|||||||
let dotsRemainingElement = document.getElementById('dotsRemaining');
|
let dotsRemainingElement = document.getElementById('dotsRemaining');
|
||||||
let pursuitIndicator = document.getElementById('pursuitIndicator');
|
let pursuitIndicator = document.getElementById('pursuitIndicator');
|
||||||
let pursuitTimerElement = document.getElementById('pursuitTimer');
|
let pursuitTimerElement = document.getElementById('pursuitTimer');
|
||||||
|
let comboDisplay = document.getElementById('comboDisplay');
|
||||||
|
let comboValueElement = document.getElementById('comboValue');
|
||||||
|
let frenzyIndicator = document.getElementById('frenzyIndicator');
|
||||||
|
let speedBoostIndicator = document.getElementById('speedBoostIndicator');
|
||||||
|
let shieldIndicator = document.getElementById('shieldIndicator');
|
||||||
|
let multiplierIndicator = document.getElementById('multiplierIndicator');
|
||||||
|
let multiplierValueElement = document.getElementById('multiplierValue');
|
||||||
|
|
||||||
|
// Système de combo
|
||||||
|
let comboCount = 0;
|
||||||
|
let comboMultiplier = 1;
|
||||||
|
let lastDotTime = 0;
|
||||||
|
const COMBO_TIMEOUT = 3000; // 3 secondes
|
||||||
|
|
||||||
|
// Power-ups actifs
|
||||||
|
let speedBoostActive = false;
|
||||||
|
let speedBoostTimer = 0;
|
||||||
|
let shieldActive = false;
|
||||||
|
let shieldTimer = 0;
|
||||||
|
let scoreMultiplierActive = false;
|
||||||
|
let scoreMultiplierTimer = 0;
|
||||||
|
let scoreMultiplierValue = 1;
|
||||||
|
|
||||||
|
// Mode Frenzy
|
||||||
|
let frenzyModeActive = false;
|
||||||
|
let frenzyTimer = 0;
|
||||||
|
let frenzyCooldown = 0;
|
||||||
|
const FRENZY_INTERVAL = 30000; // 30 secondes
|
||||||
|
const FRENZY_DURATION = 10000; // 10 secondes
|
||||||
|
|
||||||
|
|
||||||
|
// Obstacles temporaires
|
||||||
|
let temporaryWalls = [];
|
||||||
|
let slowZones = [];
|
||||||
|
let traps = [];
|
||||||
|
|
||||||
|
// Zones spéciales
|
||||||
|
let specialZones = [];
|
||||||
|
|
||||||
class Pacman {
|
class Pacman {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -233,7 +307,30 @@ class Pacman {
|
|||||||
collectDot() {
|
collectDot() {
|
||||||
if (maze[this.y][this.x] === DOT) {
|
if (maze[this.y][this.x] === DOT) {
|
||||||
maze[this.y][this.x] = EMPTY;
|
maze[this.y][this.x] = EMPTY;
|
||||||
score += 10;
|
|
||||||
|
// Système de combo
|
||||||
|
const currentTime = Date.now();
|
||||||
|
if (currentTime - lastDotTime < COMBO_TIMEOUT) {
|
||||||
|
comboCount++;
|
||||||
|
comboMultiplier = Math.min(1 + Math.floor(comboCount / 5), 5); // Max x5
|
||||||
|
} else {
|
||||||
|
comboCount = 1;
|
||||||
|
comboMultiplier = 1;
|
||||||
|
}
|
||||||
|
lastDotTime = currentTime;
|
||||||
|
|
||||||
|
// Afficher le combo
|
||||||
|
if (comboDisplay && comboValueElement) {
|
||||||
|
if (comboMultiplier > 1) {
|
||||||
|
comboDisplay.style.display = 'block';
|
||||||
|
comboValueElement.textContent = comboMultiplier;
|
||||||
|
} else {
|
||||||
|
comboDisplay.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let points = 10 * comboMultiplier * (frenzyModeActive ? 3 : 1) * scoreMultiplierValue;
|
||||||
|
score += Math.floor(points);
|
||||||
scoreElement.textContent = score;
|
scoreElement.textContent = score;
|
||||||
totalDots--;
|
totalDots--;
|
||||||
if (dotsRemainingElement) {
|
if (dotsRemainingElement) {
|
||||||
@ -276,9 +373,71 @@ class Pacman {
|
|||||||
}
|
}
|
||||||
} else if (maze[this.y][this.x] === BONUS_LUDO) {
|
} else if (maze[this.y][this.x] === BONUS_LUDO) {
|
||||||
maze[this.y][this.x] = EMPTY;
|
maze[this.y][this.x] = EMPTY;
|
||||||
score += 15;
|
let points = 200 * (frenzyModeActive ? 3 : 1) * scoreMultiplierValue;
|
||||||
|
score += Math.floor(points);
|
||||||
scoreElement.textContent = score;
|
scoreElement.textContent = score;
|
||||||
bonuses = bonuses.filter(b => !(b.x === this.x && b.y === this.y && b.type === BONUS_LUDO));
|
bonuses = bonuses.filter(b => !(b.x === this.x && b.y === this.y && b.type === BONUS_LUDO));
|
||||||
|
} else if (maze[this.y][this.x] === BONUS_SPEED) {
|
||||||
|
maze[this.y][this.x] = EMPTY;
|
||||||
|
speedBoostActive = true;
|
||||||
|
speedBoostTimer = 600; // 10 secondes à 60 FPS
|
||||||
|
this.speed = this.baseSpeed * 2;
|
||||||
|
bonuses = bonuses.filter(b => !(b.x === this.x && b.y === this.y && b.type === BONUS_SPEED));
|
||||||
|
if (speedBoostIndicator) speedBoostIndicator.style.display = 'block';
|
||||||
|
} else if (maze[this.y][this.x] === BONUS_SHIELD) {
|
||||||
|
maze[this.y][this.x] = EMPTY;
|
||||||
|
shieldActive = true;
|
||||||
|
shieldTimer = 900; // 15 secondes
|
||||||
|
bonuses = bonuses.filter(b => !(b.x === this.x && b.y === this.y && b.type === BONUS_SHIELD));
|
||||||
|
if (shieldIndicator) shieldIndicator.style.display = 'block';
|
||||||
|
} else if (maze[this.y][this.x] === BONUS_BOMB) {
|
||||||
|
maze[this.y][this.x] = EMPTY;
|
||||||
|
// Repousser tous les fantômes
|
||||||
|
for (let ghost of ghosts) {
|
||||||
|
const dx = ghost.x - this.x;
|
||||||
|
const dy = ghost.y - this.y;
|
||||||
|
const distance = Math.sqrt(dx * dx + dy * dy);
|
||||||
|
if (distance < 5) {
|
||||||
|
ghost.x = Math.max(0, Math.min(COLS - 1, ghost.x + Math.sign(dx) * 3));
|
||||||
|
ghost.y = Math.max(0, Math.min(ROWS - 1, ghost.y + Math.sign(dy) * 3));
|
||||||
|
ghost.pixelX = ghost.x * CELL_SIZE + CELL_SIZE / 2;
|
||||||
|
ghost.pixelY = ghost.y * CELL_SIZE + CELL_SIZE / 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bonuses = bonuses.filter(b => !(b.x === this.x && b.y === this.y && b.type === BONUS_BOMB));
|
||||||
|
} else if (maze[this.y][this.x] === BONUS_MULTIPLIER) {
|
||||||
|
maze[this.y][this.x] = EMPTY;
|
||||||
|
scoreMultiplierActive = true;
|
||||||
|
scoreMultiplierTimer = 1800; // 30 secondes
|
||||||
|
scoreMultiplierValue = 2;
|
||||||
|
bonuses = bonuses.filter(b => !(b.x === this.x && b.y === this.y && b.type === BONUS_MULTIPLIER));
|
||||||
|
if (multiplierIndicator && multiplierValueElement) {
|
||||||
|
multiplierIndicator.style.display = 'block';
|
||||||
|
multiplierValueElement.textContent = scoreMultiplierValue;
|
||||||
|
}
|
||||||
|
} else if (maze[this.y][this.x] === ZONE_TELEPORT) {
|
||||||
|
// Téléportation aléatoire
|
||||||
|
let newX, newY;
|
||||||
|
do {
|
||||||
|
newX = Math.floor(Math.random() * COLS);
|
||||||
|
newY = Math.floor(Math.random() * ROWS);
|
||||||
|
} while (maze[newY][newX] === WALL || (newX === this.x && newY === this.y));
|
||||||
|
|
||||||
|
this.x = newX;
|
||||||
|
this.y = newY;
|
||||||
|
this.pixelX = newX * CELL_SIZE + CELL_SIZE / 2;
|
||||||
|
this.pixelY = newY * CELL_SIZE + CELL_SIZE / 2;
|
||||||
|
} else if (maze[this.y][this.x] === ZONE_BONUS) {
|
||||||
|
// Zone bonus : double les points
|
||||||
|
// Déjà géré par le système de combo
|
||||||
|
} else if (maze[this.y][this.x] === ZONE_DANGER) {
|
||||||
|
// Zone danger : ralentit Pacman
|
||||||
|
this.speed = this.baseSpeed * 0.5;
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!speedBoostActive) {
|
||||||
|
this.speed = this.baseSpeed;
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,10 +467,11 @@ class Pacman {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Ghost {
|
class Ghost {
|
||||||
constructor(x, y, color) {
|
constructor(x, y, color, type = GHOST_NORMAL) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.color = color;
|
this.color = color;
|
||||||
|
this.type = type;
|
||||||
this.direction = Math.floor(Math.random() * 4);
|
this.direction = Math.floor(Math.random() * 4);
|
||||||
this.baseSpeed = 0.15;
|
this.baseSpeed = 0.15;
|
||||||
this.speed = this.baseSpeed;
|
this.speed = this.baseSpeed;
|
||||||
@ -323,15 +483,40 @@ class Ghost {
|
|||||||
this.vulnerableTimer = 0;
|
this.vulnerableTimer = 0;
|
||||||
this.startX = x;
|
this.startX = x;
|
||||||
this.startY = y;
|
this.startY = y;
|
||||||
|
this.isInvisible = false;
|
||||||
|
this.invisibleTimer = 0;
|
||||||
|
this.patrolTarget = null;
|
||||||
|
this.patrolIndex = 0;
|
||||||
|
|
||||||
|
// Ajustements selon le type
|
||||||
|
if (type === GHOST_FAST) {
|
||||||
|
this.baseSpeed = 0.25;
|
||||||
|
} else if (type === GHOST_INVISIBLE && level >= 5) {
|
||||||
|
this.isInvisible = true;
|
||||||
|
this.invisibleTimer = 300;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSpeed() {
|
updateSpeed() {
|
||||||
this.speed = this.baseSpeed * (1 + (level - 1) * 0.2);
|
let speedMultiplier = 1 + (level - 1) * 0.2;
|
||||||
|
if (this.type === GHOST_FAST) {
|
||||||
|
speedMultiplier *= 1.5;
|
||||||
|
}
|
||||||
|
this.speed = this.baseSpeed * speedMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
if (!gameRunning || isPaused) return;
|
if (!gameRunning || isPaused) return;
|
||||||
|
|
||||||
|
// Gestion de l'invisibilité
|
||||||
|
if (this.type === GHOST_INVISIBLE && level >= 5) {
|
||||||
|
this.invisibleTimer--;
|
||||||
|
if (this.invisibleTimer <= 0) {
|
||||||
|
this.isInvisible = !this.isInvisible;
|
||||||
|
this.invisibleTimer = this.isInvisible ? 300 : 300;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Gestion de la vulnérabilité
|
// Gestion de la vulnérabilité
|
||||||
if (this.isVulnerable) {
|
if (this.isVulnerable) {
|
||||||
this.vulnerableTimer--;
|
this.vulnerableTimer--;
|
||||||
@ -367,8 +552,17 @@ class Ghost {
|
|||||||
// Fuir le joueur quand vulnérable
|
// Fuir le joueur quand vulnérable
|
||||||
this.direction = this.getDirectionAwayFromPacman(possibleDirections);
|
this.direction = this.getDirectionAwayFromPacman(possibleDirections);
|
||||||
} else {
|
} else {
|
||||||
// Toujours poursuivre le joueur
|
// Comportement selon le type
|
||||||
|
if (this.type === GHOST_HUNTER) {
|
||||||
this.direction = this.getDirectionToPacman(possibleDirections);
|
this.direction = this.getDirectionToPacman(possibleDirections);
|
||||||
|
} else if (this.type === GHOST_PATROL) {
|
||||||
|
this.direction = this.getPatrolDirection(possibleDirections);
|
||||||
|
} else if (this.type === GHOST_FAST || this.type === GHOST_INVISIBLE) {
|
||||||
|
this.direction = this.getDirectionToPacman(possibleDirections);
|
||||||
|
} else {
|
||||||
|
// Normal : toujours poursuivre
|
||||||
|
this.direction = this.getDirectionToPacman(possibleDirections);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.moveCounter = 0;
|
this.moveCounter = 0;
|
||||||
@ -462,6 +656,50 @@ class Ghost {
|
|||||||
return bestDirection;
|
return bestDirection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPatrolDirection(possibleDirections) {
|
||||||
|
// Patrouille : bloque les passages stratégiques
|
||||||
|
const dx = [0, 1, 0, -1];
|
||||||
|
const dy = [-1, 0, 1, 0];
|
||||||
|
|
||||||
|
// Chercher à se placer entre Pacman et les sorties
|
||||||
|
const pacmanX = pacman.x;
|
||||||
|
const pacmanY = pacman.y;
|
||||||
|
|
||||||
|
// Si proche de Pacman, le poursuivre
|
||||||
|
const distance = Math.sqrt(
|
||||||
|
Math.pow(pacmanX - this.x, 2) +
|
||||||
|
Math.pow(pacmanY - this.y, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (distance < 5) {
|
||||||
|
return this.getDirectionToPacman(possibleDirections);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sinon, patrouiller vers les zones centrales
|
||||||
|
const centerX = COLS / 2;
|
||||||
|
const centerY = ROWS / 2;
|
||||||
|
|
||||||
|
let bestDirection = possibleDirections[0];
|
||||||
|
let minDistance = Infinity;
|
||||||
|
|
||||||
|
for (let dir of possibleDirections) {
|
||||||
|
const nextX = this.x + dx[dir];
|
||||||
|
const nextY = this.y + dy[dir];
|
||||||
|
|
||||||
|
const dist = Math.sqrt(
|
||||||
|
Math.pow(centerX - nextX, 2) +
|
||||||
|
Math.pow(centerY - nextY, 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (dist < minDistance) {
|
||||||
|
minDistance = dist;
|
||||||
|
bestDirection = dir;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestDirection;
|
||||||
|
}
|
||||||
|
|
||||||
canMove(direction) {
|
canMove(direction) {
|
||||||
const dx = [0, 1, 0, -1];
|
const dx = [0, 1, 0, -1];
|
||||||
const dy = [-1, 0, 1, 0];
|
const dy = [-1, 0, 1, 0];
|
||||||
@ -477,6 +715,14 @@ class Ghost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
draw() {
|
draw() {
|
||||||
|
// Si invisible et niveau >= 5, ne pas dessiner 50% du temps
|
||||||
|
if (this.type === GHOST_INVISIBLE && this.isInvisible && level >= 5) {
|
||||||
|
const shouldDraw = Math.floor(Date.now() / 200) % 2 === 0;
|
||||||
|
if (!shouldDraw) {
|
||||||
|
return; // Ne pas dessiner quand invisible
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.translate(this.pixelX, this.pixelY);
|
ctx.translate(this.pixelX, this.pixelY);
|
||||||
|
|
||||||
@ -488,6 +734,10 @@ class Ghost {
|
|||||||
ctx.fillStyle = flash === 0 ? '#0000ff' : '#ffffff';
|
ctx.fillStyle = flash === 0 ? '#0000ff' : '#ffffff';
|
||||||
} else {
|
} else {
|
||||||
ctx.fillStyle = this.color;
|
ctx.fillStyle = this.color;
|
||||||
|
// Fantôme invisible : opacité réduite
|
||||||
|
if (this.type === GHOST_INVISIBLE && this.isInvisible && level >= 5) {
|
||||||
|
ctx.globalAlpha = 0.5;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx.strokeStyle = '#000000';
|
ctx.strokeStyle = '#000000';
|
||||||
ctx.lineWidth = 2;
|
ctx.lineWidth = 2;
|
||||||
@ -528,6 +778,7 @@ class Ghost {
|
|||||||
ctx.arc(size * 0.2, -size * 0.1, size * 0.06, 0, Math.PI * 2);
|
ctx.arc(size * 0.2, -size * 0.1, size * 0.06, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.globalAlpha = 1.0;
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -557,12 +808,17 @@ class Bonus {
|
|||||||
ctx.scale(scale, scale);
|
ctx.scale(scale, scale);
|
||||||
|
|
||||||
if (this.type === BONUS_CHERRY) {
|
if (this.type === BONUS_CHERRY) {
|
||||||
ctx.fillStyle = '#ff0000';
|
const fruit = getFruitType();
|
||||||
|
|
||||||
|
// Dessiner le fruit selon le type
|
||||||
|
if (fruit.name === 'cerise') {
|
||||||
|
// Cerise (rouge avec feuille verte)
|
||||||
|
ctx.fillStyle = fruit.color;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(0, 0, CELL_SIZE * 0.25, 0, Math.PI * 2);
|
ctx.arc(0, 0, CELL_SIZE * 0.25, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
ctx.fillStyle = '#00ff00';
|
ctx.fillStyle = fruit.stemColor;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(-CELL_SIZE * 0.15, -CELL_SIZE * 0.2, CELL_SIZE * 0.1, 0, Math.PI * 2);
|
ctx.arc(-CELL_SIZE * 0.15, -CELL_SIZE * 0.2, CELL_SIZE * 0.1, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
@ -573,6 +829,136 @@ class Bonus {
|
|||||||
ctx.moveTo(-CELL_SIZE * 0.15, -CELL_SIZE * 0.3);
|
ctx.moveTo(-CELL_SIZE * 0.15, -CELL_SIZE * 0.3);
|
||||||
ctx.lineTo(-CELL_SIZE * 0.25, -CELL_SIZE * 0.4);
|
ctx.lineTo(-CELL_SIZE * 0.25, -CELL_SIZE * 0.4);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
} else if (fruit.name === 'banane') {
|
||||||
|
// Banane (jaune courbée)
|
||||||
|
ctx.fillStyle = fruit.color;
|
||||||
|
ctx.beginPath();
|
||||||
|
// Dessiner une forme de banane courbée
|
||||||
|
ctx.moveTo(-CELL_SIZE * 0.25, -CELL_SIZE * 0.1);
|
||||||
|
ctx.quadraticCurveTo(0, CELL_SIZE * 0.2, CELL_SIZE * 0.25, -CELL_SIZE * 0.1);
|
||||||
|
ctx.quadraticCurveTo(0, -CELL_SIZE * 0.3, -CELL_SIZE * 0.25, -CELL_SIZE * 0.1);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.strokeStyle = '#ffaa00';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Tige
|
||||||
|
ctx.strokeStyle = fruit.stemColor;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(-CELL_SIZE * 0.2, -CELL_SIZE * 0.15);
|
||||||
|
ctx.lineTo(-CELL_SIZE * 0.25, -CELL_SIZE * 0.25);
|
||||||
|
ctx.stroke();
|
||||||
|
} else if (fruit.name === 'orange') {
|
||||||
|
// Orange (cercle orange)
|
||||||
|
ctx.fillStyle = fruit.color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(0, 0, CELL_SIZE * 0.25, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.strokeStyle = '#ff6600';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Tige
|
||||||
|
ctx.strokeStyle = fruit.stemColor;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, -CELL_SIZE * 0.25);
|
||||||
|
ctx.lineTo(0, -CELL_SIZE * 0.35);
|
||||||
|
ctx.stroke();
|
||||||
|
} else if (fruit.name === 'pomme') {
|
||||||
|
// Pomme (rouge avec tige brune)
|
||||||
|
ctx.fillStyle = fruit.color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(0, CELL_SIZE * 0.05, CELL_SIZE * 0.25, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Feuille (ovale)
|
||||||
|
ctx.fillStyle = '#00ff00';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.scale(1, 1.5);
|
||||||
|
ctx.arc(CELL_SIZE * 0.15, -CELL_SIZE * 0.07, CELL_SIZE * 0.08, 0, Math.PI * 2);
|
||||||
|
ctx.scale(1, 1/1.5);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Tige
|
||||||
|
ctx.strokeStyle = fruit.stemColor;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, -CELL_SIZE * 0.2);
|
||||||
|
ctx.lineTo(0, -CELL_SIZE * 0.3);
|
||||||
|
ctx.stroke();
|
||||||
|
} else if (fruit.name === 'raisin') {
|
||||||
|
// Raisin (grappes violettes)
|
||||||
|
ctx.fillStyle = fruit.color;
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
for (let j = 0; j < 2; j++) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc((i - 1) * CELL_SIZE * 0.15, (j - 0.5) * CELL_SIZE * 0.15, CELL_SIZE * 0.1, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tige
|
||||||
|
ctx.strokeStyle = fruit.stemColor;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, -CELL_SIZE * 0.3);
|
||||||
|
ctx.lineTo(0, -CELL_SIZE * 0.4);
|
||||||
|
ctx.stroke();
|
||||||
|
} else if (fruit.name === 'fraise') {
|
||||||
|
// Fraise (rouge avec graines)
|
||||||
|
ctx.fillStyle = fruit.color;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(0, CELL_SIZE * 0.05, CELL_SIZE * 0.25, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Graines
|
||||||
|
ctx.fillStyle = '#ffff00';
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc((i - 2) * CELL_SIZE * 0.1, CELL_SIZE * 0.05, 2, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feuilles
|
||||||
|
ctx.fillStyle = fruit.stemColor;
|
||||||
|
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();
|
||||||
|
} else if (fruit.name === 'ananas') {
|
||||||
|
// Ananas (jaune avec texture)
|
||||||
|
ctx.fillStyle = fruit.color;
|
||||||
|
ctx.beginPath();
|
||||||
|
// Forme ovale pour l'ananas
|
||||||
|
ctx.scale(0.7, 1);
|
||||||
|
ctx.arc(0, 0, CELL_SIZE * 0.3, 0, Math.PI * 2);
|
||||||
|
ctx.scale(1/0.7, 1);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Texture
|
||||||
|
ctx.strokeStyle = '#ffaa00';
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
for (let i = -2; i <= 2; i++) {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(i * CELL_SIZE * 0.08, -CELL_SIZE * 0.3);
|
||||||
|
ctx.lineTo(i * CELL_SIZE * 0.08, CELL_SIZE * 0.3);
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feuilles
|
||||||
|
ctx.fillStyle = fruit.stemColor;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, -CELL_SIZE * 0.3);
|
||||||
|
ctx.lineTo(-CELL_SIZE * 0.1, -CELL_SIZE * 0.4);
|
||||||
|
ctx.lineTo(CELL_SIZE * 0.1, -CELL_SIZE * 0.4);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
} else if (this.type === BONUS_LUDO) {
|
} else if (this.type === BONUS_LUDO) {
|
||||||
const size = CELL_SIZE * 0.45;
|
const size = CELL_SIZE * 0.45;
|
||||||
|
|
||||||
@ -611,6 +997,62 @@ class Bonus {
|
|||||||
ctx.textAlign = 'center';
|
ctx.textAlign = 'center';
|
||||||
ctx.textBaseline = 'middle';
|
ctx.textBaseline = 'middle';
|
||||||
ctx.fillText('L', 0, 0);
|
ctx.fillText('L', 0, 0);
|
||||||
|
} else if (this.type === BONUS_SPEED) {
|
||||||
|
// Étoile de vitesse
|
||||||
|
ctx.fillStyle = '#00ffff';
|
||||||
|
ctx.strokeStyle = '#0088ff';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(0, 0, CELL_SIZE * 0.3, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = `bold ${CELL_SIZE * 0.4}px Arial`;
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillText('⚡', 0, 0);
|
||||||
|
} else if (this.type === BONUS_SHIELD) {
|
||||||
|
// Bouclier
|
||||||
|
ctx.fillStyle = '#00ff00';
|
||||||
|
ctx.strokeStyle = '#008800';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(0, 0, CELL_SIZE * 0.3, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = `bold ${CELL_SIZE * 0.4}px Arial`;
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillText('🛡', 0, 0);
|
||||||
|
} else if (this.type === BONUS_BOMB) {
|
||||||
|
// Bombe
|
||||||
|
ctx.fillStyle = '#ff0000';
|
||||||
|
ctx.strokeStyle = '#880000';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(0, 0, CELL_SIZE * 0.3, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = `bold ${CELL_SIZE * 0.4}px Arial`;
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillText('💣', 0, 0);
|
||||||
|
} else if (this.type === BONUS_MULTIPLIER) {
|
||||||
|
// Multiplicateur
|
||||||
|
ctx.fillStyle = '#ff00ff';
|
||||||
|
ctx.strokeStyle = '#880088';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(0, 0, CELL_SIZE * 0.3, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = `bold ${CELL_SIZE * 0.4}px Arial`;
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.fillText('✨', 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
@ -618,12 +1060,46 @@ class Bonus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let pacman = new Pacman();
|
let pacman = new Pacman();
|
||||||
const ghosts = [
|
let ghosts = [];
|
||||||
new Ghost(14, 11, '#ff0000'),
|
|
||||||
new Ghost(15, 11, '#ff00ff'),
|
function createGhosts() {
|
||||||
new Ghost(14, 12, '#00ffff'),
|
ghosts = [];
|
||||||
new Ghost(15, 12, '#ffa500')
|
const ghostColors = ['#ff0000', '#ff00ff', '#00ffff', '#ffa500', '#00ff00', '#ffff00', '#ff00ff', '#00ffff'];
|
||||||
];
|
const ghostTypes = [GHOST_NORMAL, GHOST_HUNTER, GHOST_PATROL, GHOST_FAST, GHOST_INVISIBLE];
|
||||||
|
|
||||||
|
// Nombre de fantômes selon le niveau
|
||||||
|
let numGhosts = 4;
|
||||||
|
if (level >= 3 && level < 5) numGhosts = 5;
|
||||||
|
else if (level >= 5 && level < 10) numGhosts = 6;
|
||||||
|
else if (level >= 10) numGhosts = 7;
|
||||||
|
|
||||||
|
const positions = [
|
||||||
|
{x: 14, y: 11}, {x: 15, y: 11}, {x: 14, y: 12}, {x: 15, y: 12},
|
||||||
|
{x: 13, y: 11}, {x: 16, y: 11}, {x: 13, y: 12}, {x: 16, y: 12}
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < numGhosts; i++) {
|
||||||
|
const pos = positions[i % positions.length];
|
||||||
|
let type = GHOST_NORMAL;
|
||||||
|
|
||||||
|
// Types selon le niveau
|
||||||
|
if (level >= 2 && i === 1) type = GHOST_HUNTER;
|
||||||
|
if (level >= 3 && i === 2) type = GHOST_PATROL;
|
||||||
|
if (level >= 4 && i === 3) type = GHOST_FAST;
|
||||||
|
if (level >= 5 && i >= 4) {
|
||||||
|
const rand = Math.random();
|
||||||
|
if (rand < 0.3) type = GHOST_INVISIBLE;
|
||||||
|
else if (rand < 0.6) type = GHOST_FAST;
|
||||||
|
else type = GHOST_HUNTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
ghosts.push(new Ghost(pos.x, pos.y, ghostColors[i % ghostColors.length], type));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let ghost of ghosts) {
|
||||||
|
ghost.updateSpeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let bonuses = [];
|
let bonuses = [];
|
||||||
|
|
||||||
@ -669,6 +1145,27 @@ function drawMaze() {
|
|||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(cellX + CELL_SIZE / 2, cellY + CELL_SIZE / 2, 2, 0, Math.PI * 2);
|
ctx.arc(cellX + CELL_SIZE / 2, cellY + CELL_SIZE / 2, 2, 0, Math.PI * 2);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
} else if (maze[y][x] === ZONE_TELEPORT) {
|
||||||
|
// Zone de téléportation
|
||||||
|
ctx.fillStyle = 'rgba(255, 0, 255, 0.3)';
|
||||||
|
ctx.fillRect(cellX, cellY, CELL_SIZE, CELL_SIZE);
|
||||||
|
ctx.strokeStyle = '#ff00ff';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeRect(cellX, cellY, CELL_SIZE, CELL_SIZE);
|
||||||
|
} else if (maze[y][x] === ZONE_BONUS) {
|
||||||
|
// Zone bonus
|
||||||
|
ctx.fillStyle = 'rgba(255, 215, 0, 0.3)';
|
||||||
|
ctx.fillRect(cellX, cellY, CELL_SIZE, CELL_SIZE);
|
||||||
|
ctx.strokeStyle = '#ffd700';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeRect(cellX, cellY, CELL_SIZE, CELL_SIZE);
|
||||||
|
} else if (maze[y][x] === ZONE_DANGER) {
|
||||||
|
// Zone danger
|
||||||
|
ctx.fillStyle = 'rgba(255, 0, 0, 0.3)';
|
||||||
|
ctx.fillRect(cellX, cellY, CELL_SIZE, CELL_SIZE);
|
||||||
|
ctx.strokeStyle = '#ff0000';
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.strokeRect(cellX, cellY, CELL_SIZE, CELL_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -701,6 +1198,20 @@ function checkCollisions() {
|
|||||||
ghost.pixelX = ghost.x * CELL_SIZE + CELL_SIZE / 2;
|
ghost.pixelX = ghost.x * CELL_SIZE + CELL_SIZE / 2;
|
||||||
ghost.pixelY = ghost.y * CELL_SIZE + CELL_SIZE / 2;
|
ghost.pixelY = ghost.y * CELL_SIZE + CELL_SIZE / 2;
|
||||||
ghost.direction = Math.floor(Math.random() * 4);
|
ghost.direction = Math.floor(Math.random() * 4);
|
||||||
|
} else {
|
||||||
|
// Vérifier le bouclier
|
||||||
|
if (shieldActive) {
|
||||||
|
// Bouclier actif : repousser le fantôme sans perdre de vie
|
||||||
|
shieldActive = false;
|
||||||
|
shieldTimer = 0;
|
||||||
|
if (shieldIndicator) shieldIndicator.style.display = 'none';
|
||||||
|
// Repousser le fantôme
|
||||||
|
const dx = ghost.x - pacman.x;
|
||||||
|
const dy = ghost.y - pacman.y;
|
||||||
|
ghost.x = Math.max(0, Math.min(COLS - 1, ghost.x + Math.sign(dx) * 2));
|
||||||
|
ghost.y = Math.max(0, Math.min(ROWS - 1, ghost.y + Math.sign(dy) * 2));
|
||||||
|
ghost.pixelX = ghost.x * CELL_SIZE + CELL_SIZE / 2;
|
||||||
|
ghost.pixelY = ghost.y * CELL_SIZE + CELL_SIZE / 2;
|
||||||
} else {
|
} else {
|
||||||
// Perdre une vie
|
// Perdre une vie
|
||||||
lives--;
|
lives--;
|
||||||
@ -717,6 +1228,7 @@ function checkCollisions() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function gameLoop() {
|
function gameLoop() {
|
||||||
@ -744,21 +1256,88 @@ function gameLoop() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gestion des power-ups
|
||||||
|
if (speedBoostActive) {
|
||||||
|
speedBoostTimer--;
|
||||||
|
if (speedBoostTimer <= 0) {
|
||||||
|
speedBoostActive = false;
|
||||||
|
pacman.speed = pacman.baseSpeed;
|
||||||
|
if (speedBoostIndicator) speedBoostIndicator.style.display = 'none';
|
||||||
|
} else if (speedBoostIndicator) {
|
||||||
|
const seconds = Math.ceil(speedBoostTimer / 60);
|
||||||
|
speedBoostIndicator.textContent = `⚡ Vitesse (${seconds}s)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shieldActive) {
|
||||||
|
shieldTimer--;
|
||||||
|
if (shieldTimer <= 0) {
|
||||||
|
shieldActive = false;
|
||||||
|
if (shieldIndicator) shieldIndicator.style.display = 'none';
|
||||||
|
} else if (shieldIndicator) {
|
||||||
|
const seconds = Math.ceil(shieldTimer / 60);
|
||||||
|
shieldIndicator.textContent = `🛡 Bouclier (${seconds}s)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scoreMultiplierActive) {
|
||||||
|
scoreMultiplierTimer--;
|
||||||
|
if (scoreMultiplierTimer <= 0) {
|
||||||
|
scoreMultiplierActive = false;
|
||||||
|
scoreMultiplierValue = 1;
|
||||||
|
if (multiplierIndicator) multiplierIndicator.style.display = 'none';
|
||||||
|
} else if (multiplierIndicator && multiplierValueElement) {
|
||||||
|
const seconds = Math.ceil(scoreMultiplierTimer / 60);
|
||||||
|
multiplierIndicator.textContent = `✨ x${scoreMultiplierValue} (${seconds}s)`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode Frenzy
|
||||||
|
if (!frenzyModeActive) {
|
||||||
|
frenzyCooldown++;
|
||||||
|
if (frenzyCooldown >= FRENZY_INTERVAL) {
|
||||||
|
frenzyModeActive = true;
|
||||||
|
frenzyTimer = FRENZY_DURATION;
|
||||||
|
frenzyCooldown = 0;
|
||||||
|
// Rendre tous les fantômes vulnérables
|
||||||
|
for (let ghost of ghosts) {
|
||||||
|
ghost.isVulnerable = true;
|
||||||
|
ghost.vulnerableTimer = FRENZY_DURATION;
|
||||||
|
}
|
||||||
|
if (frenzyIndicator) frenzyIndicator.style.display = 'block';
|
||||||
|
if (pursuitIndicator) pursuitIndicator.style.display = 'block';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
frenzyTimer--;
|
||||||
|
if (frenzyTimer <= 0) {
|
||||||
|
frenzyModeActive = false;
|
||||||
|
for (let ghost of ghosts) {
|
||||||
|
ghost.isVulnerable = false;
|
||||||
|
ghost.vulnerableTimer = 0;
|
||||||
|
}
|
||||||
|
if (frenzyIndicator) frenzyIndicator.style.display = 'none';
|
||||||
|
if (pursuitIndicator) pursuitIndicator.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Combo timeout
|
||||||
|
if (comboCount > 0 && Date.now() - lastDotTime > COMBO_TIMEOUT) {
|
||||||
|
comboCount = 0;
|
||||||
|
comboMultiplier = 1;
|
||||||
|
if (comboDisplay) comboDisplay.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
pacman.update();
|
pacman.update();
|
||||||
pacman.draw();
|
pacman.draw();
|
||||||
|
|
||||||
// Mettre à jour l'indicateur de poursuite
|
// Mettre à jour l'indicateur de poursuite
|
||||||
if (pursuitIndicator && cherryEatenTimer > 0) {
|
if (pursuitIndicator && (cherryEatenTimer > 0 || frenzyModeActive)) {
|
||||||
const seconds = Math.ceil(cherryEatenTimer / 60);
|
const timer = frenzyModeActive ? frenzyTimer : cherryEatenTimer;
|
||||||
|
const seconds = Math.ceil(timer / 60);
|
||||||
if (pursuitTimerElement) {
|
if (pursuitTimerElement) {
|
||||||
pursuitTimerElement.textContent = seconds;
|
pursuitTimerElement.textContent = seconds;
|
||||||
}
|
}
|
||||||
// Vérifier si tous les fantômes sont encore vulnérables
|
} else if (pursuitIndicator && !frenzyModeActive) {
|
||||||
const allVulnerable = ghosts.every(g => g.isVulnerable);
|
|
||||||
if (!allVulnerable && pursuitIndicator.style.display !== 'none') {
|
|
||||||
pursuitIndicator.style.display = 'none';
|
|
||||||
}
|
|
||||||
} else if (pursuitIndicator) {
|
|
||||||
pursuitIndicator.style.display = 'none';
|
pursuitIndicator.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -813,13 +1392,9 @@ function restartCurrentLevel() {
|
|||||||
|
|
||||||
pacman = new Pacman();
|
pacman = new Pacman();
|
||||||
pacman.speed = pacman.baseSpeed * (1 + (level - 1) * 0.05);
|
pacman.speed = pacman.baseSpeed * (1 + (level - 1) * 0.05);
|
||||||
ghosts[0] = new Ghost(14, 11, '#ff0000');
|
createGhosts();
|
||||||
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) {
|
for (let ghost of ghosts) {
|
||||||
ghost.updateSpeed();
|
|
||||||
ghost.isVulnerable = false;
|
ghost.isVulnerable = false;
|
||||||
ghost.vulnerableTimer = 0;
|
ghost.vulnerableTimer = 0;
|
||||||
}
|
}
|
||||||
@ -909,13 +1484,9 @@ function nextLevel() {
|
|||||||
console.log('Réinitialisation de Pacman et des fantômes');
|
console.log('Réinitialisation de Pacman et des fantômes');
|
||||||
pacman = new Pacman();
|
pacman = new Pacman();
|
||||||
pacman.speed = pacman.baseSpeed * (1 + (level - 1) * 0.05);
|
pacman.speed = pacman.baseSpeed * (1 + (level - 1) * 0.05);
|
||||||
ghosts[0] = new Ghost(14, 11, '#ff0000');
|
createGhosts();
|
||||||
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) {
|
for (let ghost of ghosts) {
|
||||||
ghost.updateSpeed();
|
|
||||||
ghost.isVulnerable = false;
|
ghost.isVulnerable = false;
|
||||||
ghost.vulnerableTimer = 0;
|
ghost.vulnerableTimer = 0;
|
||||||
}
|
}
|
||||||
@ -1132,8 +1703,45 @@ function placeBonuses() {
|
|||||||
{x: 15, y: 14, type: BONUS_LUDO}
|
{x: 15, y: 14, type: BONUS_LUDO}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Ajouter des power-ups selon le niveau
|
||||||
|
if (level >= 2) {
|
||||||
|
bonusPositions.push({x: 5, y: 5, type: BONUS_SPEED});
|
||||||
|
bonusPositions.push({x: 25, y: 5, type: BONUS_SHIELD});
|
||||||
|
}
|
||||||
|
if (level >= 3) {
|
||||||
|
bonusPositions.push({x: 5, y: 25, type: BONUS_BOMB});
|
||||||
|
bonusPositions.push({x: 25, y: 25, type: BONUS_MULTIPLIER});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zones spéciales selon le niveau
|
||||||
|
if (level >= 4) {
|
||||||
|
// Zones de téléportation
|
||||||
|
const teleportZones = [
|
||||||
|
{x: 7, y: 7}, {x: 23, y: 7}, {x: 7, y: 23}, {x: 23, y: 23}
|
||||||
|
];
|
||||||
|
for (let zone of teleportZones) {
|
||||||
|
if (maze[zone.y] && maze[zone.y][zone.x] === EMPTY) {
|
||||||
|
maze[zone.y][zone.x] = ZONE_TELEPORT;
|
||||||
|
specialZones.push({x: zone.x, y: zone.y, type: ZONE_TELEPORT});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level >= 5) {
|
||||||
|
// Zones bonus
|
||||||
|
const bonusZones = [
|
||||||
|
{x: 10, y: 10}, {x: 20, y: 10}, {x: 10, y: 20}, {x: 20, y: 20}
|
||||||
|
];
|
||||||
|
for (let zone of bonusZones) {
|
||||||
|
if (maze[zone.y] && maze[zone.y][zone.x] === EMPTY) {
|
||||||
|
maze[zone.y][zone.x] = ZONE_BONUS;
|
||||||
|
specialZones.push({x: zone.x, y: zone.y, type: ZONE_BONUS});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (let pos of bonusPositions) {
|
for (let pos of bonusPositions) {
|
||||||
if (maze[pos.y][pos.x] === EMPTY || maze[pos.y][pos.x] === DOT) {
|
if (maze[pos.y] && (maze[pos.y][pos.x] === EMPTY || maze[pos.y][pos.x] === DOT)) {
|
||||||
maze[pos.y][pos.x] = pos.type;
|
maze[pos.y][pos.x] = pos.type;
|
||||||
bonuses.push(new Bonus(pos.x, pos.y, pos.type));
|
bonuses.push(new Bonus(pos.x, pos.y, pos.type));
|
||||||
}
|
}
|
||||||
@ -1152,6 +1760,26 @@ function initGame() {
|
|||||||
lives = 3;
|
lives = 3;
|
||||||
cherriesEaten = 0;
|
cherriesEaten = 0;
|
||||||
isPaused = false;
|
isPaused = false;
|
||||||
|
|
||||||
|
// Réinitialiser les systèmes
|
||||||
|
comboCount = 0;
|
||||||
|
comboMultiplier = 1;
|
||||||
|
lastDotTime = 0;
|
||||||
|
speedBoostActive = false;
|
||||||
|
speedBoostTimer = 0;
|
||||||
|
shieldActive = false;
|
||||||
|
shieldTimer = 0;
|
||||||
|
scoreMultiplierActive = false;
|
||||||
|
scoreMultiplierTimer = 0;
|
||||||
|
scoreMultiplierValue = 1;
|
||||||
|
frenzyModeActive = false;
|
||||||
|
frenzyTimer = 0;
|
||||||
|
frenzyCooldown = 0;
|
||||||
|
specialZones = [];
|
||||||
|
temporaryWalls = [];
|
||||||
|
slowZones = [];
|
||||||
|
traps = [];
|
||||||
|
|
||||||
scoreElement.textContent = score;
|
scoreElement.textContent = score;
|
||||||
levelElement.textContent = level;
|
levelElement.textContent = level;
|
||||||
updateLivesDisplay();
|
updateLivesDisplay();
|
||||||
@ -1163,16 +1791,17 @@ function initGame() {
|
|||||||
if (pursuitIndicator) {
|
if (pursuitIndicator) {
|
||||||
pursuitIndicator.style.display = 'none';
|
pursuitIndicator.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
if (comboDisplay) comboDisplay.style.display = 'none';
|
||||||
|
if (frenzyIndicator) frenzyIndicator.style.display = 'none';
|
||||||
|
if (speedBoostIndicator) speedBoostIndicator.style.display = 'none';
|
||||||
|
if (shieldIndicator) shieldIndicator.style.display = 'none';
|
||||||
|
if (multiplierIndicator) multiplierIndicator.style.display = 'none';
|
||||||
|
|
||||||
pacman = new Pacman();
|
pacman = new Pacman();
|
||||||
pacman.speed = pacman.baseSpeed * (1 + (level - 1) * 0.05);
|
pacman.speed = pacman.baseSpeed * (1 + (level - 1) * 0.05);
|
||||||
ghosts[0] = new Ghost(14, 11, '#ff0000');
|
createGhosts();
|
||||||
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) {
|
for (let ghost of ghosts) {
|
||||||
ghost.updateSpeed();
|
|
||||||
ghost.isVulnerable = false;
|
ghost.isVulnerable = false;
|
||||||
ghost.vulnerableTimer = 0;
|
ghost.vulnerableTimer = 0;
|
||||||
}
|
}
|
||||||
@ -1605,11 +2234,34 @@ scoresBtn.addEventListener('click', () => {
|
|||||||
leaderboardModal.style.display = 'flex';
|
leaderboardModal.style.display = 'flex';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Ouvrir la modal des règles
|
||||||
|
if (rulesBtn) {
|
||||||
|
rulesBtn.addEventListener('click', () => {
|
||||||
|
rulesModal.style.display = 'flex';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Fermer le modal de classement
|
// Fermer le modal de classement
|
||||||
closeModalBtn.addEventListener('click', () => {
|
closeModalBtn.addEventListener('click', () => {
|
||||||
leaderboardModal.style.display = 'none';
|
leaderboardModal.style.display = 'none';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Fermer la modal des règles
|
||||||
|
if (closeRulesBtn) {
|
||||||
|
closeRulesBtn.addEventListener('click', () => {
|
||||||
|
rulesModal.style.display = 'none';
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fermer en cliquant en dehors de la modal des règles
|
||||||
|
if (rulesModal) {
|
||||||
|
rulesModal.addEventListener('click', (e) => {
|
||||||
|
if (e.target === rulesModal) {
|
||||||
|
rulesModal.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Fermer le classement dans le jeu
|
// Fermer le classement dans le jeu
|
||||||
if (closeLeaderboardBtn) {
|
if (closeLeaderboardBtn) {
|
||||||
closeLeaderboardBtn.addEventListener('click', () => {
|
closeLeaderboardBtn.addEventListener('click', () => {
|
||||||
|
|||||||
99
index.html
99
index.html
@ -24,6 +24,9 @@
|
|||||||
<button class="menu-btn" id="scoresBtn">
|
<button class="menu-btn" id="scoresBtn">
|
||||||
<span class="btn-icon">🏆</span> CLASSEMENT
|
<span class="btn-icon">🏆</span> CLASSEMENT
|
||||||
</button>
|
</button>
|
||||||
|
<button class="menu-btn" id="rulesBtn">
|
||||||
|
<span class="btn-icon">📖</span> RÈGLES
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -46,8 +49,17 @@
|
|||||||
<div class="level">Niveau: <span id="level">1</span></div>
|
<div class="level">Niveau: <span id="level">1</span></div>
|
||||||
<div class="lives">Vies: <span id="lives"><span class="heart">♥</span><span class="heart">♥</span><span class="heart">♥</span></span></div>
|
<div class="lives">Vies: <span id="lives"><span class="heart">♥</span><span class="heart">♥</span><span class="heart">♥</span></span></div>
|
||||||
<div class="dots-remaining">Pastilles: <span id="dotsRemaining">0</span></div>
|
<div class="dots-remaining">Pastilles: <span id="dotsRemaining">0</span></div>
|
||||||
|
<div class="combo-display" id="comboDisplay" style="display: none;">Combo x<span id="comboValue">1</span></div>
|
||||||
<div class="status" id="status">Prêt à jouer</div>
|
<div class="status" id="status">Prêt à jouer</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="power-ups-display" id="powerUpsDisplay">
|
||||||
|
<div class="power-up-item" id="speedBoostIndicator" style="display: none;">⚡ Vitesse</div>
|
||||||
|
<div class="power-up-item" id="shieldIndicator" style="display: none;">🛡 Bouclier</div>
|
||||||
|
<div class="power-up-item" id="multiplierIndicator" style="display: none;">✨ x<span id="multiplierValue">2</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="frenzy-indicator" id="frenzyIndicator" style="display: none;">
|
||||||
|
<span class="frenzy-text">🔥 FRENZY MODE - Score x3 !</span>
|
||||||
|
</div>
|
||||||
<div class="pursuit-indicator" id="pursuitIndicator" style="display: none;">
|
<div class="pursuit-indicator" id="pursuitIndicator" style="display: none;">
|
||||||
<span class="pursuit-text">👻 Poursuite active: <span id="pursuitTimer">0</span>s</span>
|
<span class="pursuit-text">👻 Poursuite active: <span id="pursuitTimer">0</span>s</span>
|
||||||
</div>
|
</div>
|
||||||
@ -104,6 +116,93 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- MODAL RÈGLES -->
|
||||||
|
<div class="rules-modal" id="rulesModal" style="display: none;">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>📖 RÈGLES DU JEU</h2>
|
||||||
|
<button class="close-modal-btn" id="closeRulesBtn">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="rules-content">
|
||||||
|
<div class="rule-section">
|
||||||
|
<h3>🎯 Objectif</h3>
|
||||||
|
<p>Collectez toutes les pastilles blanches pour gagner des points et progresser dans les niveaux.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rule-section">
|
||||||
|
<h3>🎮 Contrôles</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Flèches directionnelles</strong> : Déplacer Pacman</li>
|
||||||
|
<li><strong>Espace ou Échap</strong> : Mettre en pause</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rule-section">
|
||||||
|
<h3>🍒 Bonus de base</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Cerise rouge</strong> : Rend les fantômes vulnérables pendant quelques secondes</li>
|
||||||
|
<li><strong>Manger un fantôme vulnérable</strong> : +200 points</li>
|
||||||
|
<li><strong>4 cerises mangées</strong> : Passage au niveau suivant</li>
|
||||||
|
<li><strong>Étoile Ludo</strong> : +200 points (apparaît dès le niveau 1)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rule-section">
|
||||||
|
<h3>⚡ Power-ups (Niveau 2+)</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>⚡ Étoile de vitesse</strong> : Pacman devient 2x plus rapide pendant 10 secondes</li>
|
||||||
|
<li><strong>🛡 Bouclier</strong> : Protège d'une collision avec un fantôme (15 secondes)</li>
|
||||||
|
<li><strong>💣 Bombe</strong> : Repousse tous les fantômes proches (Niveau 3+)</li>
|
||||||
|
<li><strong>✨ Multiplicateur</strong> : Double tous les points pendant 30 secondes (Niveau 3+)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rule-section">
|
||||||
|
<h3>🔥 Mode Frenzy</h3>
|
||||||
|
<p>Toutes les <strong>30 secondes</strong>, le mode Frenzy s'active automatiquement : tous les fantômes deviennent vulnérables et vous gagnez <strong>3x plus de points</strong> pendant 10 secondes !</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rule-section">
|
||||||
|
<h3>💥 Système de Combo</h3>
|
||||||
|
<p>Collectez les pastilles rapidement pour créer un combo ! Chaque combo augmente votre multiplicateur de score (jusqu'à x5). Le combo se réinitialise si vous attendez trop longtemps entre deux collectes.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rule-section">
|
||||||
|
<h3>👻 Fantômes</h3>
|
||||||
|
<p>Évitez les fantômes ! Si vous les touchez, vous perdez une vie. Vous avez 3 vies au départ.</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Niveau 1-2</strong> : 4 fantômes normaux</li>
|
||||||
|
<li><strong>Niveau 3-4</strong> : 5 fantômes (dont des chasseurs et patrouilleurs)</li>
|
||||||
|
<li><strong>Niveau 5+</strong> : 6-7 fantômes (rapides, invisibles, etc.)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rule-section">
|
||||||
|
<h3>⭐ Système de score</h3>
|
||||||
|
<ul>
|
||||||
|
<li>Pastille : <strong>10 points</strong> (x combo x Frenzy x multiplicateur)</li>
|
||||||
|
<li>Fantôme vulnérable : <strong>200 points</strong></li>
|
||||||
|
<li>Cerise : <strong>100 points</strong></li>
|
||||||
|
<li>Étoile Ludo : <strong>200 points</strong></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rule-section">
|
||||||
|
<h3>🌐 Zones spéciales (Niveau 4+)</h3>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Zone de téléportation</strong> (violet) : Vous téléporte à un endroit aléatoire</li>
|
||||||
|
<li><strong>Zone bonus</strong> (doré) : Double les points dans cette zone</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rule-section">
|
||||||
|
<h3>🏆 Niveaux</h3>
|
||||||
|
<p>Le jeu devient plus difficile à chaque niveau : plus de fantômes, nouveaux types de fantômes, et de nouveaux power-ups apparaissent !</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="game.js"></script>
|
<script src="game.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
170
style.css
170
style.css
@ -374,10 +374,100 @@ body::after {
|
|||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-modal-btn:hover, .close-leaderboard-btn:hover {
|
||||||
|
background: rgba(255, 0, 0, 0.5);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* === MODAL RÈGLES === */
|
||||||
|
.rules-modal {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.9);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 2000;
|
||||||
|
animation: fadeIn 0.3s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-content {
|
||||||
|
max-height: 70vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 20px;
|
||||||
|
color: #fff;
|
||||||
font-family: 'Press Start 2P', cursive;
|
font-family: 'Press Start 2P', cursive;
|
||||||
|
font-size: 0.7em;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-content::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-content::-webkit-scrollbar-track {
|
||||||
|
background: rgba(255, 215, 0, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-content::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(255, 215, 0, 0.5);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rules-content::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: rgba(255, 215, 0, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-section {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
padding: 15px;
|
||||||
|
background: rgba(255, 215, 0, 0.1);
|
||||||
|
border-left: 3px solid #ffd700;
|
||||||
|
border-radius: 5px;
|
||||||
|
animation: fadeIn 0.5s ease-in;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-section h3 {
|
||||||
|
color: #ffd700;
|
||||||
|
font-size: 1em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-shadow: 0 0 10px rgba(255, 215, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-section p {
|
||||||
|
color: #fff;
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-section ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 0;
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-section li {
|
||||||
|
margin: 8px 0;
|
||||||
|
padding-left: 20px;
|
||||||
|
position: relative;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-section li:before {
|
||||||
|
content: "•";
|
||||||
|
color: #ffd700;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-section strong {
|
||||||
|
color: #ffd700;
|
||||||
}
|
}
|
||||||
|
|
||||||
.close-modal-btn:hover, .close-leaderboard-btn:hover {
|
.close-modal-btn:hover, .close-leaderboard-btn:hover {
|
||||||
@ -466,6 +556,70 @@ h1 {
|
|||||||
color: #00ffff;
|
color: #00ffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.combo-display {
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 215, 0, 0.8), rgba(255, 140, 0, 0.8));
|
||||||
|
padding: 10px 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px solid #ffd700;
|
||||||
|
color: #000;
|
||||||
|
text-align: center;
|
||||||
|
animation: pulse 0.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.power-ups-display {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 10px 0;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.power-up-item {
|
||||||
|
font-size: 0.8em;
|
||||||
|
padding: 8px 15px;
|
||||||
|
background: rgba(255, 215, 0, 0.2);
|
||||||
|
border: 2px solid #ffd700;
|
||||||
|
border-radius: 8px;
|
||||||
|
color: #ffd700;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.frenzy-indicator {
|
||||||
|
margin: 15px auto;
|
||||||
|
padding: 15px 30px;
|
||||||
|
background: linear-gradient(180deg, rgba(255, 0, 0, 0.8), rgba(255, 100, 0, 0.8));
|
||||||
|
border: 3px solid #ff0000;
|
||||||
|
border-radius: 15px;
|
||||||
|
text-align: center;
|
||||||
|
animation: frenzyPulse 0.3s ease-in-out infinite;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes frenzyPulse {
|
||||||
|
0%, 100% {
|
||||||
|
box-shadow: 0 0 20px rgba(255, 0, 0, 0.8);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
box-shadow: 0 0 40px rgba(255, 0, 0, 1);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.frenzy-text {
|
||||||
|
font-size: 1em;
|
||||||
|
color: #ffff00;
|
||||||
|
text-shadow: 0 0 15px rgba(255, 255, 0, 1);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
.score.updated {
|
.score.updated {
|
||||||
animation: scoreUp 0.3s ease-out;
|
animation: scoreUp 0.3s ease-out;
|
||||||
}
|
}
|
||||||
@ -986,4 +1140,18 @@ footer {
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
width: 95%;
|
width: 95%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.rules-content {
|
||||||
|
font-size: 0.6em;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-section {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rule-section h3 {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user