Compare commits
3 Commits
e65d165acd
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 844c64c7b6 | |||
| 5091fc3d6e | |||
| 58ed4774ba |
592
game.js
592
game.js
@ -13,6 +13,9 @@ const finalScoreElement = document.getElementById('finalScore');
|
|||||||
const finalLevelElement = document.getElementById('finalLevel');
|
const finalLevelElement = document.getElementById('finalLevel');
|
||||||
const overlayRestartBtn = document.getElementById('overlayRestartBtn');
|
const overlayRestartBtn = document.getElementById('overlayRestartBtn');
|
||||||
const confirmUsernameBtn = document.getElementById('confirmUsernameBtn');
|
const confirmUsernameBtn = document.getElementById('confirmUsernameBtn');
|
||||||
|
const gameOverUsernameInput = document.getElementById('gameOverUsername');
|
||||||
|
const saveScoreBtn = document.getElementById('saveScoreBtn');
|
||||||
|
const scoreSavedMsg = document.getElementById('scoreSavedMsg');
|
||||||
|
|
||||||
// === GESTION DU MENU ===
|
// === GESTION DU MENU ===
|
||||||
const mainMenu = document.getElementById('mainMenu');
|
const mainMenu = document.getElementById('mainMenu');
|
||||||
@ -28,10 +31,29 @@ 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');
|
||||||
|
const fullscreenBtn = document.getElementById('fullscreenBtn');
|
||||||
|
const customizeBtn = document.getElementById('customizeBtn');
|
||||||
|
const customizeModal = document.getElementById('customizeModal');
|
||||||
|
const closeCustomizeBtn = document.getElementById('closeCustomizeBtn');
|
||||||
|
const saveCustomizeBtn = document.getElementById('saveCustomizeBtn');
|
||||||
|
const playerPreviewCanvas = document.getElementById('playerPreviewCanvas');
|
||||||
|
const colorOptions = document.querySelectorAll('.color-option');
|
||||||
|
const shapeOptions = document.querySelectorAll('.shape-option');
|
||||||
|
|
||||||
|
// Fonction pour vérifier si on est en mode plein écran
|
||||||
|
function isFullscreen() {
|
||||||
|
return !!(document.fullscreenElement || document.webkitFullscreenElement ||
|
||||||
|
document.mozFullScreenElement || document.msFullscreenElement);
|
||||||
|
}
|
||||||
|
|
||||||
let usernameConfirmed = false;
|
let usernameConfirmed = false;
|
||||||
|
let gameOverUsername = null;
|
||||||
|
|
||||||
const CELL_SIZE = 20;
|
// Personnalisation du joueur
|
||||||
|
let playerColor = localStorage.getItem('playerColor') || 'rainbow';
|
||||||
|
let playerShape = localStorage.getItem('playerShape') || 'round';
|
||||||
|
|
||||||
|
const CELL_SIZE = 25;
|
||||||
const COLS = 30;
|
const COLS = 30;
|
||||||
const ROWS = 30;
|
const ROWS = 30;
|
||||||
|
|
||||||
@ -233,7 +255,7 @@ let traps = [];
|
|||||||
let specialZones = [];
|
let specialZones = [];
|
||||||
|
|
||||||
class Pacman {
|
class Pacman {
|
||||||
constructor() {
|
constructor(color = null, shape = null) {
|
||||||
this.x = 14;
|
this.x = 14;
|
||||||
this.y = 23;
|
this.y = 23;
|
||||||
this.direction = 0;
|
this.direction = 0;
|
||||||
@ -245,6 +267,8 @@ class Pacman {
|
|||||||
this.pixelX = this.x * CELL_SIZE + CELL_SIZE / 2;
|
this.pixelX = this.x * CELL_SIZE + CELL_SIZE / 2;
|
||||||
this.pixelY = this.y * CELL_SIZE + CELL_SIZE / 2;
|
this.pixelY = this.y * CELL_SIZE + CELL_SIZE / 2;
|
||||||
this.colorAnimation = 0;
|
this.colorAnimation = 0;
|
||||||
|
this.color = color || playerColor;
|
||||||
|
this.shape = shape || playerShape;
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
@ -360,8 +384,8 @@ class Pacman {
|
|||||||
ghost.vulnerableTimer = vulnerableTime;
|
ghost.vulnerableTimer = vulnerableTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Afficher l'indicateur de poursuite
|
// Afficher l'indicateur de poursuite (sauf en mode plein écran)
|
||||||
if (pursuitIndicator) {
|
if (pursuitIndicator && !isFullscreen()) {
|
||||||
pursuitIndicator.style.display = 'block';
|
pursuitIndicator.style.display = 'block';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,19 +473,93 @@ class Pacman {
|
|||||||
const rotation = [Math.PI * 1.5, 0, Math.PI * 0.5, Math.PI];
|
const rotation = [Math.PI * 1.5, 0, Math.PI * 0.5, Math.PI];
|
||||||
ctx.rotate(rotation[this.direction]);
|
ctx.rotate(rotation[this.direction]);
|
||||||
|
|
||||||
|
// Utiliser la couleur personnalisée
|
||||||
|
let baseFillColor;
|
||||||
|
if (this.color === 'rainbow') {
|
||||||
const hue = (this.colorAnimation * 180 / Math.PI) % 360;
|
const hue = (this.colorAnimation * 180 / Math.PI) % 360;
|
||||||
ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
|
baseFillColor = `hsl(${hue}, 100%, 50%)`;
|
||||||
|
ctx.fillStyle = baseFillColor;
|
||||||
|
} else {
|
||||||
|
const colorMap = {
|
||||||
|
'yellow': '#ffd700',
|
||||||
|
'red': '#ff4444',
|
||||||
|
'blue': '#4444ff',
|
||||||
|
'green': '#44ff44',
|
||||||
|
'purple': '#aa44ff',
|
||||||
|
'orange': '#ff8844',
|
||||||
|
'pink': '#ff44aa'
|
||||||
|
};
|
||||||
|
baseFillColor = colorMap[this.color] || '#ffd700';
|
||||||
|
ctx.fillStyle = baseFillColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = CELL_SIZE * 0.4;
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
|
||||||
|
if (this.shape === 'triangle') {
|
||||||
|
const triangleSize = size * 1.5; // Plus grand pour une meilleure visibilité
|
||||||
|
|
||||||
|
// Dessiner le triangle avec contour et yeux
|
||||||
|
ctx.beginPath();
|
||||||
if (this.mouthOpen) {
|
if (this.mouthOpen) {
|
||||||
ctx.arc(0, 0, CELL_SIZE * 0.4, 0.2, Math.PI * 2 - 0.2);
|
// Triangle avec bouche ouverte - ouverture plus grande et visible
|
||||||
|
ctx.moveTo(0, -triangleSize * 0.9);
|
||||||
|
ctx.lineTo(-triangleSize * 0.85, triangleSize * 0.7);
|
||||||
|
ctx.lineTo(-triangleSize * 0.4, triangleSize * 0.4);
|
||||||
|
ctx.lineTo(0, triangleSize * 0.2);
|
||||||
|
ctx.lineTo(triangleSize * 0.4, triangleSize * 0.4);
|
||||||
|
ctx.lineTo(triangleSize * 0.85, triangleSize * 0.7);
|
||||||
|
ctx.closePath();
|
||||||
} else {
|
} else {
|
||||||
ctx.arc(0, 0, CELL_SIZE * 0.4, 0, Math.PI * 2);
|
// Triangle équilatéral complet
|
||||||
|
ctx.moveTo(0, -triangleSize);
|
||||||
|
ctx.lineTo(-triangleSize * 0.866, triangleSize * 0.5); // cos(30°) ≈ 0.866
|
||||||
|
ctx.lineTo(triangleSize * 0.866, triangleSize * 0.5);
|
||||||
|
ctx.closePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remplir le triangle
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Contour noir épais pour plus de définition
|
||||||
|
ctx.strokeStyle = '#000000';
|
||||||
|
ctx.lineWidth = 2.5;
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Dessiner les yeux
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(-triangleSize * 0.2, -triangleSize * 0.15, triangleSize * 0.12, 0, Math.PI * 2);
|
||||||
|
ctx.arc(triangleSize * 0.2, -triangleSize * 0.15, triangleSize * 0.12, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Pupilles
|
||||||
|
ctx.fillStyle = '#000000';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(-triangleSize * 0.2, -triangleSize * 0.15, triangleSize * 0.06, 0, Math.PI * 2);
|
||||||
|
ctx.arc(triangleSize * 0.2, -triangleSize * 0.15, triangleSize * 0.06, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Reflets dans les yeux pour plus de vie
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(-triangleSize * 0.22, -triangleSize * 0.17, triangleSize * 0.03, 0, Math.PI * 2);
|
||||||
|
ctx.arc(triangleSize * 0.18, -triangleSize * 0.17, triangleSize * 0.03, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Restaurer la couleur originale
|
||||||
|
ctx.fillStyle = baseFillColor;
|
||||||
|
} else {
|
||||||
|
// Forme ronde (par défaut)
|
||||||
|
if (this.mouthOpen) {
|
||||||
|
ctx.arc(0, 0, size, 0.2, Math.PI * 2 - 0.2);
|
||||||
|
} else {
|
||||||
|
ctx.arc(0, 0, size, 0, Math.PI * 2);
|
||||||
|
}
|
||||||
ctx.lineTo(0, 0);
|
ctx.lineTo(0, 0);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
@ -1230,7 +1328,7 @@ function checkCollisions() {
|
|||||||
gameRunning = false;
|
gameRunning = false;
|
||||||
statusElement.textContent = 'Game Over !';
|
statusElement.textContent = 'Game Over !';
|
||||||
showGameOver();
|
showGameOver();
|
||||||
saveScore();
|
// Le score sera sauvegardé quand l'utilisateur clique sur "Sauvegarder"
|
||||||
} else {
|
} else {
|
||||||
restartCurrentLevel();
|
restartCurrentLevel();
|
||||||
}
|
}
|
||||||
@ -1314,7 +1412,7 @@ function gameLoop() {
|
|||||||
ghost.vulnerableTimer = FRENZY_DURATION;
|
ghost.vulnerableTimer = FRENZY_DURATION;
|
||||||
}
|
}
|
||||||
if (frenzyIndicator) frenzyIndicator.style.display = 'block';
|
if (frenzyIndicator) frenzyIndicator.style.display = 'block';
|
||||||
if (pursuitIndicator) pursuitIndicator.style.display = 'block';
|
if (pursuitIndicator && !isFullscreen()) pursuitIndicator.style.display = 'block';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
frenzyTimer--;
|
frenzyTimer--;
|
||||||
@ -1339,13 +1437,16 @@ function gameLoop() {
|
|||||||
pacman.update();
|
pacman.update();
|
||||||
pacman.draw();
|
pacman.draw();
|
||||||
|
|
||||||
// Mettre à jour l'indicateur de poursuite
|
// Mettre à jour l'indicateur de poursuite (masquer en mode plein écran)
|
||||||
if (pursuitIndicator && (cherryEatenTimer > 0 || frenzyModeActive)) {
|
if (isFullscreen() && pursuitIndicator) {
|
||||||
|
pursuitIndicator.style.display = 'none';
|
||||||
|
} else if (pursuitIndicator && (cherryEatenTimer > 0 || frenzyModeActive) && !isFullscreen()) {
|
||||||
const timer = frenzyModeActive ? frenzyTimer : cherryEatenTimer;
|
const timer = frenzyModeActive ? frenzyTimer : cherryEatenTimer;
|
||||||
const seconds = Math.ceil(timer / 60);
|
const seconds = Math.ceil(timer / 60);
|
||||||
if (pursuitTimerElement) {
|
if (pursuitTimerElement) {
|
||||||
pursuitTimerElement.textContent = seconds;
|
pursuitTimerElement.textContent = seconds;
|
||||||
}
|
}
|
||||||
|
pursuitIndicator.style.display = 'block';
|
||||||
} else if (pursuitIndicator && !frenzyModeActive) {
|
} else if (pursuitIndicator && !frenzyModeActive) {
|
||||||
pursuitIndicator.style.display = 'none';
|
pursuitIndicator.style.display = 'none';
|
||||||
}
|
}
|
||||||
@ -1757,6 +1858,77 @@ function placeBonuses() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === REDIMENSIONNEMENT ADAPTATIF DU CANVAS ===
|
||||||
|
function resizeCanvas() {
|
||||||
|
if (!gameWrapper || gameWrapper.style.display === 'none') {
|
||||||
|
return; // Ne pas redimensionner si le jeu n'est pas visible
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = document.querySelector('.container');
|
||||||
|
const gameHeader = document.querySelector('.game-header');
|
||||||
|
const gameInfo = document.querySelector('.game-info');
|
||||||
|
const userInput = document.querySelector('.user-input-section');
|
||||||
|
const powerUps = document.querySelector('.power-ups-display');
|
||||||
|
const frenzyIndicator = document.querySelector('.frenzy-indicator');
|
||||||
|
const pursuitIndicator = document.querySelector('.pursuit-indicator');
|
||||||
|
const instructions = document.querySelector('.instructions');
|
||||||
|
const leaderboard = document.querySelector('.game-leaderboard');
|
||||||
|
|
||||||
|
if (!container || !canvas) return;
|
||||||
|
|
||||||
|
// Calculer l'espace disponible en hauteur
|
||||||
|
const windowHeight = window.innerHeight;
|
||||||
|
const windowWidth = window.innerWidth;
|
||||||
|
|
||||||
|
// Hauteur des éléments autour du canvas
|
||||||
|
let usedHeight = 0;
|
||||||
|
if (gameHeader) usedHeight += gameHeader.offsetHeight + 20;
|
||||||
|
if (userInput && userInput.offsetHeight > 0) usedHeight += userInput.offsetHeight + 10;
|
||||||
|
if (gameInfo) usedHeight += gameInfo.offsetHeight + 20;
|
||||||
|
if (powerUps && powerUps.offsetHeight > 0) usedHeight += powerUps.offsetHeight + 10;
|
||||||
|
if (frenzyIndicator && frenzyIndicator.offsetHeight > 0 && frenzyIndicator.style.display !== 'none') {
|
||||||
|
usedHeight += frenzyIndicator.offsetHeight + 10;
|
||||||
|
}
|
||||||
|
if (pursuitIndicator && pursuitIndicator.offsetHeight > 0 && pursuitIndicator.style.display !== 'none') {
|
||||||
|
usedHeight += pursuitIndicator.offsetHeight + 10;
|
||||||
|
}
|
||||||
|
if (instructions) usedHeight += instructions.offsetHeight + 20;
|
||||||
|
|
||||||
|
// Padding du container (30px top + 30px bottom)
|
||||||
|
usedHeight += 60;
|
||||||
|
|
||||||
|
// Marges et espacement
|
||||||
|
usedHeight += 40;
|
||||||
|
|
||||||
|
// Largeur disponible (en tenant compte du leaderboard à droite si visible)
|
||||||
|
let usedWidth = 0;
|
||||||
|
const containerPadding = 60; // 30px left + 30px right
|
||||||
|
const gaps = 60; // Espacement entre éléments
|
||||||
|
|
||||||
|
if (leaderboard && window.innerWidth > 1200) {
|
||||||
|
const leaderboardRect = leaderboard.getBoundingClientRect();
|
||||||
|
if (leaderboardRect.width > 0) {
|
||||||
|
usedWidth += leaderboardRect.width + 30; // + gap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usedWidth += containerPadding + gaps;
|
||||||
|
|
||||||
|
const availableHeight = windowHeight - usedHeight;
|
||||||
|
const availableWidth = windowWidth - usedWidth;
|
||||||
|
|
||||||
|
// La taille du canvas sera le minimum entre hauteur et largeur disponible
|
||||||
|
// Utilise 95% de l'espace disponible avec une taille minimale de 400px
|
||||||
|
const maxSize = Math.min(availableHeight, availableWidth);
|
||||||
|
const minSize = 400;
|
||||||
|
const canvasDisplaySize = Math.max(minSize, Math.min(maxSize * 0.95, 1200));
|
||||||
|
|
||||||
|
// Appliquer la taille au canvas via CSS
|
||||||
|
canvas.style.width = canvasDisplaySize + 'px';
|
||||||
|
canvas.style.height = canvasDisplaySize + 'px';
|
||||||
|
canvas.style.maxWidth = '100%';
|
||||||
|
canvas.style.maxHeight = 'calc(100vh - ' + usedHeight + 'px)';
|
||||||
|
}
|
||||||
|
|
||||||
function initGame() {
|
function initGame() {
|
||||||
resetUsernameInput(); // Réinitialiser le champ nom
|
resetUsernameInput(); // Réinitialiser le champ nom
|
||||||
|
|
||||||
@ -1822,6 +1994,10 @@ function initGame() {
|
|||||||
placeBonuses();
|
placeBonuses();
|
||||||
countDots();
|
countDots();
|
||||||
updateGameLeaderboard();
|
updateGameLeaderboard();
|
||||||
|
|
||||||
|
// Redimensionner le canvas après l'initialisation
|
||||||
|
setTimeout(resizeCanvas, 100);
|
||||||
|
|
||||||
gameLoop();
|
gameLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1869,9 +2045,15 @@ function getScores() {
|
|||||||
return scoresJson ? JSON.parse(scoresJson) : [];
|
return scoresJson ? JSON.parse(scoresJson) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveScore() {
|
function saveScore(usernameForScore = null) {
|
||||||
let username;
|
let username;
|
||||||
if (!usernameConfirmed) {
|
|
||||||
|
// Priorité au nom passé en paramètre, puis au nom du Game Over, puis au nom confirmé, sinon "Anonyme"
|
||||||
|
if (usernameForScore) {
|
||||||
|
username = usernameForScore.trim() || 'Anonyme';
|
||||||
|
} else if (gameOverUsername) {
|
||||||
|
username = gameOverUsername.trim() || 'Anonyme';
|
||||||
|
} else if (!usernameConfirmed) {
|
||||||
username = 'Anonyme';
|
username = 'Anonyme';
|
||||||
} else {
|
} else {
|
||||||
username = usernameInput.value.trim() || 'Anonyme';
|
username = usernameInput.value.trim() || 'Anonyme';
|
||||||
@ -2042,6 +2224,21 @@ usernameInput.addEventListener('keypress', (e) => {
|
|||||||
function showGameOver() {
|
function showGameOver() {
|
||||||
finalScoreElement.textContent = score;
|
finalScoreElement.textContent = score;
|
||||||
finalLevelElement.textContent = level;
|
finalLevelElement.textContent = level;
|
||||||
|
|
||||||
|
// Réinitialiser le champ de nom et cacher le message
|
||||||
|
if (gameOverUsernameInput) {
|
||||||
|
gameOverUsernameInput.value = usernameInput.value.trim() || '';
|
||||||
|
gameOverUsernameInput.disabled = false;
|
||||||
|
gameOverUsernameInput.focus();
|
||||||
|
}
|
||||||
|
if (saveScoreBtn) {
|
||||||
|
saveScoreBtn.disabled = false;
|
||||||
|
}
|
||||||
|
if (scoreSavedMsg) {
|
||||||
|
scoreSavedMsg.style.display = 'none';
|
||||||
|
}
|
||||||
|
gameOverUsername = null; // Réinitialiser
|
||||||
|
|
||||||
gameOverlay.classList.add('active');
|
gameOverlay.classList.add('active');
|
||||||
restartBtn.style.display = 'block';
|
restartBtn.style.display = 'block';
|
||||||
}
|
}
|
||||||
@ -2050,7 +2247,39 @@ function hideGameOver() {
|
|||||||
gameOverlay.classList.remove('active');
|
gameOverlay.classList.remove('active');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sauvegarder le score avec le nom du Game Over
|
||||||
|
if (saveScoreBtn && gameOverUsernameInput) {
|
||||||
|
saveScoreBtn.addEventListener('click', () => {
|
||||||
|
const username = gameOverUsernameInput.value.trim();
|
||||||
|
if (username.length === 0) {
|
||||||
|
alert('Veuillez entrer un nom d\'utilisateur !');
|
||||||
|
gameOverUsernameInput.focus();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gameOverUsername = username;
|
||||||
|
saveScore(username);
|
||||||
|
|
||||||
|
// Afficher le message de confirmation
|
||||||
|
if (scoreSavedMsg) {
|
||||||
|
scoreSavedMsg.style.display = 'block';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Désactiver le champ et le bouton
|
||||||
|
gameOverUsernameInput.disabled = true;
|
||||||
|
saveScoreBtn.disabled = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Permettre de sauvegarder avec la touche Entrée
|
||||||
|
gameOverUsernameInput.addEventListener('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter' && !saveScoreBtn.disabled) {
|
||||||
|
saveScoreBtn.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
overlayRestartBtn.addEventListener('click', () => {
|
overlayRestartBtn.addEventListener('click', () => {
|
||||||
|
hideGameOver();
|
||||||
initGame();
|
initGame();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2267,7 +2496,11 @@ playBtn.addEventListener('click', () => {
|
|||||||
resetUsernameInput();
|
resetUsernameInput();
|
||||||
// Toujours initialiser le jeu quand on clique sur JOUER
|
// Toujours initialiser le jeu quand on clique sur JOUER
|
||||||
gameRunning = false; // S'assurer que le jeu est arrêté
|
gameRunning = false; // S'assurer que le jeu est arrêté
|
||||||
|
// Attendre un peu pour que le DOM soit rendu avant de redimensionner
|
||||||
|
setTimeout(() => {
|
||||||
initGame();
|
initGame();
|
||||||
|
resizeCanvas();
|
||||||
|
}, 50);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Retour au menu depuis le jeu
|
// Retour au menu depuis le jeu
|
||||||
@ -2315,6 +2548,262 @@ if (rulesModal) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// === GESTION DU MODAL DE PERSONNALISATION ===
|
||||||
|
let selectedColor = playerColor;
|
||||||
|
let selectedShape = playerShape;
|
||||||
|
|
||||||
|
// Ouvrir le modal de personnalisation
|
||||||
|
if (customizeBtn) {
|
||||||
|
customizeBtn.addEventListener('click', () => {
|
||||||
|
if (customizeModal) {
|
||||||
|
customizeModal.style.display = 'flex';
|
||||||
|
selectedColor = playerColor;
|
||||||
|
selectedShape = playerShape;
|
||||||
|
updateColorSelection();
|
||||||
|
updateShapeSelection();
|
||||||
|
updatePlayerPreview();
|
||||||
|
startPreviewAnimation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fermer le modal de personnalisation
|
||||||
|
if (closeCustomizeBtn) {
|
||||||
|
closeCustomizeBtn.addEventListener('click', () => {
|
||||||
|
if (customizeModal) {
|
||||||
|
customizeModal.style.display = 'none';
|
||||||
|
stopPreviewAnimation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fermer en cliquant en dehors du modal
|
||||||
|
if (customizeModal) {
|
||||||
|
customizeModal.addEventListener('click', (e) => {
|
||||||
|
if (e.target === customizeModal) {
|
||||||
|
customizeModal.style.display = 'none';
|
||||||
|
stopPreviewAnimation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gérer la sélection de couleur
|
||||||
|
if (colorOptions) {
|
||||||
|
colorOptions.forEach(option => {
|
||||||
|
option.addEventListener('click', () => {
|
||||||
|
selectedColor = option.getAttribute('data-color');
|
||||||
|
updateColorSelection();
|
||||||
|
updatePlayerPreview();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gérer la sélection de forme
|
||||||
|
if (shapeOptions) {
|
||||||
|
shapeOptions.forEach(option => {
|
||||||
|
option.addEventListener('click', () => {
|
||||||
|
selectedShape = option.getAttribute('data-shape');
|
||||||
|
updateShapeSelection();
|
||||||
|
updatePlayerPreview();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mettre à jour la sélection visuelle
|
||||||
|
function updateColorSelection() {
|
||||||
|
if (colorOptions) {
|
||||||
|
colorOptions.forEach(option => {
|
||||||
|
if (option.getAttribute('data-color') === selectedColor) {
|
||||||
|
option.classList.add('active');
|
||||||
|
} else {
|
||||||
|
option.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateShapeSelection() {
|
||||||
|
if (shapeOptions) {
|
||||||
|
shapeOptions.forEach(option => {
|
||||||
|
if (option.getAttribute('data-shape') === selectedShape) {
|
||||||
|
option.classList.add('active');
|
||||||
|
} else {
|
||||||
|
option.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prévisualisation du joueur
|
||||||
|
let previewAnimationId = null;
|
||||||
|
function startPreviewAnimation() {
|
||||||
|
if (!playerPreviewCanvas) return;
|
||||||
|
|
||||||
|
let angle = 0;
|
||||||
|
let mouthOpen = true;
|
||||||
|
|
||||||
|
function animate() {
|
||||||
|
if (!playerPreviewCanvas) return;
|
||||||
|
const ctx = playerPreviewCanvas.getContext('2d');
|
||||||
|
ctx.clearRect(0, 0, playerPreviewCanvas.width, playerPreviewCanvas.height);
|
||||||
|
|
||||||
|
// Fond
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
ctx.fillRect(0, 0, playerPreviewCanvas.width, playerPreviewCanvas.height);
|
||||||
|
|
||||||
|
// Dessiner le joueur au centre
|
||||||
|
ctx.save();
|
||||||
|
ctx.translate(playerPreviewCanvas.width / 2, playerPreviewCanvas.height / 2);
|
||||||
|
|
||||||
|
// Couleur
|
||||||
|
if (selectedColor === 'rainbow') {
|
||||||
|
angle += 0.05;
|
||||||
|
const hue = (angle * 180 / Math.PI) % 360;
|
||||||
|
ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
|
||||||
|
} else {
|
||||||
|
const colorMap = {
|
||||||
|
'yellow': '#ffd700',
|
||||||
|
'red': '#ff4444',
|
||||||
|
'blue': '#4444ff',
|
||||||
|
'green': '#44ff44',
|
||||||
|
'purple': '#aa44ff',
|
||||||
|
'orange': '#ff8844',
|
||||||
|
'pink': '#ff44aa'
|
||||||
|
};
|
||||||
|
ctx.fillStyle = colorMap[selectedColor] || '#ffd700';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Animation de la bouche
|
||||||
|
if (Math.floor(angle * 5) % 2 === 0) {
|
||||||
|
mouthOpen = !mouthOpen;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size = 40;
|
||||||
|
ctx.beginPath();
|
||||||
|
|
||||||
|
if (selectedShape === 'triangle') {
|
||||||
|
const triangleSize = size * 1.5; // Plus grand pour une meilleure visibilité
|
||||||
|
|
||||||
|
// Dessiner le triangle avec contour et yeux
|
||||||
|
ctx.beginPath();
|
||||||
|
if (mouthOpen) {
|
||||||
|
// Triangle avec bouche ouverte
|
||||||
|
ctx.moveTo(0, -triangleSize * 0.9);
|
||||||
|
ctx.lineTo(-triangleSize * 0.85, triangleSize * 0.7);
|
||||||
|
ctx.lineTo(-triangleSize * 0.4, triangleSize * 0.4);
|
||||||
|
ctx.lineTo(0, triangleSize * 0.2);
|
||||||
|
ctx.lineTo(triangleSize * 0.4, triangleSize * 0.4);
|
||||||
|
ctx.lineTo(triangleSize * 0.85, triangleSize * 0.7);
|
||||||
|
ctx.closePath();
|
||||||
|
} else {
|
||||||
|
// Triangle équilatéral complet
|
||||||
|
ctx.moveTo(0, -triangleSize);
|
||||||
|
ctx.lineTo(-triangleSize * 0.866, triangleSize * 0.5);
|
||||||
|
ctx.lineTo(triangleSize * 0.866, triangleSize * 0.5);
|
||||||
|
ctx.closePath();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remplir le triangle
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Contour noir épais
|
||||||
|
ctx.strokeStyle = '#000000';
|
||||||
|
ctx.lineWidth = 2.5;
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// Dessiner les yeux
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(-triangleSize * 0.2, -triangleSize * 0.15, triangleSize * 0.12, 0, Math.PI * 2);
|
||||||
|
ctx.arc(triangleSize * 0.2, -triangleSize * 0.15, triangleSize * 0.12, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Pupilles
|
||||||
|
ctx.fillStyle = '#000000';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(-triangleSize * 0.2, -triangleSize * 0.15, triangleSize * 0.06, 0, Math.PI * 2);
|
||||||
|
ctx.arc(triangleSize * 0.2, -triangleSize * 0.15, triangleSize * 0.06, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Reflets dans les yeux
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(-triangleSize * 0.22, -triangleSize * 0.17, triangleSize * 0.03, 0, Math.PI * 2);
|
||||||
|
ctx.arc(triangleSize * 0.18, -triangleSize * 0.17, triangleSize * 0.03, 0, Math.PI * 2);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// Restaurer la couleur originale
|
||||||
|
if (selectedColor === 'rainbow') {
|
||||||
|
const hue = (angle * 180 / Math.PI) % 360;
|
||||||
|
ctx.fillStyle = `hsl(${hue}, 100%, 50%)`;
|
||||||
|
} else {
|
||||||
|
const colorMap = {
|
||||||
|
'yellow': '#ffd700',
|
||||||
|
'red': '#ff4444',
|
||||||
|
'blue': '#4444ff',
|
||||||
|
'green': '#44ff44',
|
||||||
|
'purple': '#aa44ff',
|
||||||
|
'orange': '#ff8844',
|
||||||
|
'pink': '#ff44aa'
|
||||||
|
};
|
||||||
|
ctx.fillStyle = colorMap[selectedColor] || '#ffd700';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Dessiner un cercle
|
||||||
|
if (mouthOpen) {
|
||||||
|
ctx.arc(0, 0, size, 0.2, Math.PI * 2 - 0.2);
|
||||||
|
} else {
|
||||||
|
ctx.arc(0, 0, size, 0, Math.PI * 2);
|
||||||
|
}
|
||||||
|
ctx.lineTo(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
previewAnimationId = requestAnimationFrame(animate);
|
||||||
|
}
|
||||||
|
|
||||||
|
animate();
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopPreviewAnimation() {
|
||||||
|
if (previewAnimationId) {
|
||||||
|
cancelAnimationFrame(previewAnimationId);
|
||||||
|
previewAnimationId = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePlayerPreview() {
|
||||||
|
// L'animation se mettra à jour automatiquement car elle utilise selectedColor
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sauvegarder la personnalisation
|
||||||
|
if (saveCustomizeBtn) {
|
||||||
|
saveCustomizeBtn.addEventListener('click', () => {
|
||||||
|
playerColor = selectedColor;
|
||||||
|
playerShape = selectedShape;
|
||||||
|
localStorage.setItem('playerColor', playerColor);
|
||||||
|
localStorage.setItem('playerShape', playerShape);
|
||||||
|
|
||||||
|
// Mettre à jour le joueur actuel si le jeu est en cours
|
||||||
|
if (pacman) {
|
||||||
|
pacman.color = playerColor;
|
||||||
|
pacman.shape = playerShape;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fermer le modal
|
||||||
|
if (customizeModal) {
|
||||||
|
customizeModal.style.display = 'none';
|
||||||
|
stopPreviewAnimation();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message de confirmation
|
||||||
|
alert('Personnalisation sauvegardée !');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Fermer le classement dans le jeu
|
// Fermer le classement dans le jeu
|
||||||
if (closeLeaderboardBtn) {
|
if (closeLeaderboardBtn) {
|
||||||
closeLeaderboardBtn.addEventListener('click', () => {
|
closeLeaderboardBtn.addEventListener('click', () => {
|
||||||
@ -2385,3 +2874,80 @@ function updateBestScore() {
|
|||||||
updateMenuLeaderboard();
|
updateMenuLeaderboard();
|
||||||
updateBestScore();
|
updateBestScore();
|
||||||
|
|
||||||
|
// Appeler au chargement, au redimensionnement et quand le jeu devient visible
|
||||||
|
window.addEventListener('resize', () => {
|
||||||
|
if (gameWrapper && gameWrapper.style.display !== 'none') {
|
||||||
|
resizeCanvas();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Redimensionner quand le jeu est affiché
|
||||||
|
if (gameWrapper) {
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
if (gameWrapper && gameWrapper.style.display !== 'none') {
|
||||||
|
// Attendre un peu pour que le DOM soit complètement rendu
|
||||||
|
setTimeout(resizeCanvas, 100);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(gameWrapper, { attributes: true, attributeFilter: ['style'] });
|
||||||
|
}
|
||||||
|
|
||||||
|
// === GESTION DU PLEIN ÉCRAN ===
|
||||||
|
function toggleFullscreen() {
|
||||||
|
const gameWrapper = document.getElementById('gameWrapper');
|
||||||
|
|
||||||
|
if (!document.fullscreenElement && !document.webkitFullscreenElement &&
|
||||||
|
!document.mozFullScreenElement && !document.msFullscreenElement) {
|
||||||
|
// Entrer en plein écran
|
||||||
|
if (gameWrapper.requestFullscreen) {
|
||||||
|
gameWrapper.requestFullscreen();
|
||||||
|
} else if (gameWrapper.webkitRequestFullscreen) {
|
||||||
|
gameWrapper.webkitRequestFullscreen();
|
||||||
|
} else if (gameWrapper.mozRequestFullScreen) {
|
||||||
|
gameWrapper.mozRequestFullScreen();
|
||||||
|
} else if (gameWrapper.msRequestFullscreen) {
|
||||||
|
gameWrapper.msRequestFullscreen();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Quitter le plein écran
|
||||||
|
if (document.exitFullscreen) {
|
||||||
|
document.exitFullscreen();
|
||||||
|
} else if (document.webkitExitFullscreen) {
|
||||||
|
document.webkitExitFullscreen();
|
||||||
|
} else if (document.mozCancelFullScreen) {
|
||||||
|
document.mozCancelFullScreen();
|
||||||
|
} else if (document.msExitFullscreen) {
|
||||||
|
document.msExitFullscreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFullscreenButton() {
|
||||||
|
const isFullscreenMode = document.fullscreenElement || document.webkitFullscreenElement ||
|
||||||
|
document.mozFullScreenElement || document.msFullscreenElement;
|
||||||
|
|
||||||
|
if (fullscreenBtn) {
|
||||||
|
fullscreenBtn.textContent = isFullscreenMode ? '✕ Sortir' : '⛶ Plein écran';
|
||||||
|
fullscreenBtn.title = isFullscreenMode ? 'Quitter le plein écran' : 'Plein écran';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cacher l'indicateur de poursuite en mode plein écran
|
||||||
|
if (isFullscreenMode && pursuitIndicator) {
|
||||||
|
pursuitIndicator.style.display = 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redimensionner le canvas après changement de mode plein écran
|
||||||
|
setTimeout(resizeCanvas, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ajouter l'event listener au bouton
|
||||||
|
if (fullscreenBtn) {
|
||||||
|
fullscreenBtn.addEventListener('click', toggleFullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Écouter les changements de mode plein écran
|
||||||
|
document.addEventListener('fullscreenchange', updateFullscreenButton);
|
||||||
|
document.addEventListener('webkitfullscreenchange', updateFullscreenButton);
|
||||||
|
document.addEventListener('mozfullscreenchange', updateFullscreenButton);
|
||||||
|
document.addEventListener('MSFullscreenChange', updateFullscreenButton);
|
||||||
|
|
||||||
|
|||||||
79
index.html
79
index.html
@ -21,6 +21,9 @@
|
|||||||
<button class="menu-btn" id="playBtn">
|
<button class="menu-btn" id="playBtn">
|
||||||
<span class="btn-icon">▶</span> JOUER
|
<span class="btn-icon">▶</span> JOUER
|
||||||
</button>
|
</button>
|
||||||
|
<button class="menu-btn" id="customizeBtn">
|
||||||
|
<span class="btn-icon">🎨</span> PERSONNALISER
|
||||||
|
</button>
|
||||||
<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>
|
||||||
@ -38,6 +41,7 @@
|
|||||||
<div class="game-header">
|
<div class="game-header">
|
||||||
<button class="back-btn" id="backToMenuBtn">← Menu</button>
|
<button class="back-btn" id="backToMenuBtn">← Menu</button>
|
||||||
<h2>OULVIC</h2>
|
<h2>OULVIC</h2>
|
||||||
|
<button class="fullscreen-btn" id="fullscreenBtn" title="Plein écran">⛶ Plein écran</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="user-input-section">
|
<div class="user-input-section">
|
||||||
<label for="username">Nom d'utilisateur:</label>
|
<label for="username">Nom d'utilisateur:</label>
|
||||||
@ -63,7 +67,7 @@
|
|||||||
<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>
|
||||||
<canvas id="gameCanvas" width="600" height="600"></canvas>
|
<canvas id="gameCanvas" width="750" height="750"></canvas>
|
||||||
|
|
||||||
<!-- Contrôles tactiles mobile -->
|
<!-- Contrôles tactiles mobile -->
|
||||||
<div class="mobile-controls" id="mobileControls">
|
<div class="mobile-controls" id="mobileControls">
|
||||||
@ -86,6 +90,12 @@
|
|||||||
<h2 class="game-over-title">GAME OVER</h2>
|
<h2 class="game-over-title">GAME OVER</h2>
|
||||||
<p class="final-score">Score Final: <span id="finalScore">0</span></p>
|
<p class="final-score">Score Final: <span id="finalScore">0</span></p>
|
||||||
<p class="final-level">Niveau Atteint: <span id="finalLevel">1</span></p>
|
<p class="final-level">Niveau Atteint: <span id="finalLevel">1</span></p>
|
||||||
|
<div class="game-over-username-section">
|
||||||
|
<label for="gameOverUsername">Nom pour le classement:</label>
|
||||||
|
<input type="text" id="gameOverUsername" placeholder="Entrez votre nom" maxlength="15">
|
||||||
|
<button id="saveScoreBtn" class="save-score-btn">💾 Sauvegarder</button>
|
||||||
|
</div>
|
||||||
|
<p class="score-saved-msg" id="scoreSavedMsg" style="display: none; color: #00ff00; font-size: 0.7em; margin-top: 10px;">Score sauvegardé !</p>
|
||||||
<button id="overlayRestartBtn">REJOUER</button>
|
<button id="overlayRestartBtn">REJOUER</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -221,6 +231,73 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- MODAL PERSONNALISATION -->
|
||||||
|
<div class="customize-modal" id="customizeModal" style="display: none;">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>🎨 PERSONNALISER VOTRE JOUEUR</h2>
|
||||||
|
<button class="close-modal-btn" id="closeCustomizeBtn">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="customize-content">
|
||||||
|
<div class="customize-section">
|
||||||
|
<h3>Couleur du joueur</h3>
|
||||||
|
<div class="color-options">
|
||||||
|
<div class="color-option" data-color="yellow" data-name="Jaune">
|
||||||
|
<div class="color-preview" style="background: #ffd700;"></div>
|
||||||
|
<span>Jaune (Classique)</span>
|
||||||
|
</div>
|
||||||
|
<div class="color-option" data-color="red" data-name="Rouge">
|
||||||
|
<div class="color-preview" style="background: #ff4444;"></div>
|
||||||
|
<span>Rouge</span>
|
||||||
|
</div>
|
||||||
|
<div class="color-option" data-color="blue" data-name="Bleu">
|
||||||
|
<div class="color-preview" style="background: #4444ff;"></div>
|
||||||
|
<span>Bleu</span>
|
||||||
|
</div>
|
||||||
|
<div class="color-option" data-color="green" data-name="Vert">
|
||||||
|
<div class="color-preview" style="background: #44ff44;"></div>
|
||||||
|
<span>Vert</span>
|
||||||
|
</div>
|
||||||
|
<div class="color-option" data-color="purple" data-name="Violet">
|
||||||
|
<div class="color-preview" style="background: #aa44ff;"></div>
|
||||||
|
<span>Violet</span>
|
||||||
|
</div>
|
||||||
|
<div class="color-option" data-color="orange" data-name="Orange">
|
||||||
|
<div class="color-preview" style="background: #ff8844;"></div>
|
||||||
|
<span>Orange</span>
|
||||||
|
</div>
|
||||||
|
<div class="color-option active" data-color="rainbow" data-name="Arc-en-ciel">
|
||||||
|
<div class="color-preview rainbow-preview"></div>
|
||||||
|
<span>Arc-en-ciel (Animé)</span>
|
||||||
|
</div>
|
||||||
|
<div class="color-option" data-color="pink" data-name="Rose">
|
||||||
|
<div class="color-preview" style="background: #ff44aa;"></div>
|
||||||
|
<span>Rose</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="customize-section">
|
||||||
|
<h3>Forme du joueur</h3>
|
||||||
|
<div class="shape-options">
|
||||||
|
<div class="shape-option active" data-shape="round" data-name="Rond">
|
||||||
|
<div class="shape-preview round-preview"></div>
|
||||||
|
<span>Rond (Classique)</span>
|
||||||
|
</div>
|
||||||
|
<div class="shape-option" data-shape="triangle" data-name="Triangle">
|
||||||
|
<div class="shape-preview triangle-preview"></div>
|
||||||
|
<span>Triangle</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="customize-preview">
|
||||||
|
<h3>Aperçu</h3>
|
||||||
|
<canvas id="playerPreviewCanvas" width="200" height="200"></canvas>
|
||||||
|
</div>
|
||||||
|
<button class="save-customize-btn" id="saveCustomizeBtn">💾 Sauvegarder</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<script src="game.js"></script>
|
<script src="game.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
304
style.css
304
style.css
@ -323,6 +323,26 @@ body::after {
|
|||||||
transform: translateX(-3px);
|
transform: translateX(-3px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fullscreen-btn {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 0.7em;
|
||||||
|
font-family: 'Press Start 2P', cursive;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #ffd700;
|
||||||
|
border: 2px solid #ffd700;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
line-height: 1.2;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullscreen-btn:hover {
|
||||||
|
background: rgba(255, 215, 0, 0.2);
|
||||||
|
transform: scale(1.1);
|
||||||
|
box-shadow: 0 0 10px rgba(255, 215, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
/* === MODAL CLASSEMENT === */
|
/* === MODAL CLASSEMENT === */
|
||||||
.leaderboard-modal {
|
.leaderboard-modal {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -695,6 +715,14 @@ h1 {
|
|||||||
0 0 40px rgba(255, 215, 0, 0.3),
|
0 0 40px rgba(255, 215, 0, 0.3),
|
||||||
inset 0 0 60px rgba(0, 0, 255, 0.1);
|
inset 0 0 60px rgba(0, 0, 255, 0.1);
|
||||||
animation: canvasGlow 3s ease-in-out infinite alternate;
|
animation: canvasGlow 3s ease-in-out infinite alternate;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 90vh;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
image-rendering: -moz-crisp-edges;
|
||||||
|
image-rendering: -webkit-crisp-edges;
|
||||||
|
image-rendering: pixelated;
|
||||||
|
image-rendering: crisp-edges;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* === INPUT UTILISATEUR === */
|
/* === INPUT UTILISATEUR === */
|
||||||
@ -1095,6 +1123,71 @@ h1 {
|
|||||||
transform: translateY(0) scale(0.98);
|
transform: translateY(0) scale(0.98);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-over-username-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.game-over-username-section label {
|
||||||
|
font-size: 0.7em;
|
||||||
|
color: #ffd700;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gameOverUsername {
|
||||||
|
padding: 10px 15px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
font-family: 'Press Start 2P', cursive;
|
||||||
|
border: 2px solid #ffd700;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgba(0, 0, 0, 0.7);
|
||||||
|
color: #fff;
|
||||||
|
outline: none;
|
||||||
|
max-width: 250px;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gameOverUsername:focus {
|
||||||
|
border-color: #ffed4e;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 215, 0, 0.6);
|
||||||
|
background: rgba(0, 0, 0, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-score-btn {
|
||||||
|
padding: 10px 20px;
|
||||||
|
font-size: 0.7em;
|
||||||
|
font-family: 'Press Start 2P', cursive;
|
||||||
|
background: linear-gradient(180deg, #00ff00 0%, #00cc00 100%);
|
||||||
|
color: #000;
|
||||||
|
border: 2px solid #00ff00;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
box-shadow: 0 4px 10px rgba(0, 255, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-score-btn:hover {
|
||||||
|
background: linear-gradient(180deg, #00ff88 0%, #00ff00 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 15px rgba(0, 255, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-score-btn:active {
|
||||||
|
transform: translateY(0) scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-score-btn:disabled {
|
||||||
|
background: rgba(100, 100, 100, 0.5);
|
||||||
|
border-color: #666;
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
/* === CONTROLES TACTILES MOBILE === */
|
/* === CONTROLES TACTILES MOBILE === */
|
||||||
.mobile-controls {
|
.mobile-controls {
|
||||||
display: none;
|
display: none;
|
||||||
@ -1240,3 +1333,214 @@ footer {
|
|||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* === MODAL PERSONNALISATION === */
|
||||||
|
.customize-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.9);
|
||||||
|
z-index: 1000;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
animation: fadeIn 0.3s ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customize-modal.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customize-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 30px;
|
||||||
|
max-width: 800px;
|
||||||
|
width: 90%;
|
||||||
|
max-height: 90vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customize-section {
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 15px;
|
||||||
|
border: 2px solid #ffd700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customize-section h3 {
|
||||||
|
color: #ffd700;
|
||||||
|
font-size: 1em;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-options {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 2px solid rgba(255, 215, 0, 0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option:hover {
|
||||||
|
background: rgba(255, 215, 0, 0.1);
|
||||||
|
border-color: #ffd700;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option.active {
|
||||||
|
background: rgba(255, 215, 0, 0.2);
|
||||||
|
border-color: #ffd700;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 215, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-preview {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 3px solid #fff;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rainbow-preview {
|
||||||
|
background: linear-gradient(45deg, #ff0000, #ff8800, #ffff00, #00ff00, #0088ff, #0000ff, #8800ff);
|
||||||
|
animation: rainbowRotate 3s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rainbowRotate {
|
||||||
|
0% { filter: hue-rotate(0deg); }
|
||||||
|
100% { filter: hue-rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option span {
|
||||||
|
font-size: 0.7em;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customize-preview {
|
||||||
|
background: rgba(0, 0, 0, 0.6);
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 15px;
|
||||||
|
border: 2px solid #ffd700;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customize-preview h3 {
|
||||||
|
color: #ffd700;
|
||||||
|
font-size: 1em;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#playerPreviewCanvas {
|
||||||
|
border: 3px solid #ffd700;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #000;
|
||||||
|
box-shadow: 0 0 20px rgba(255, 215, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-customize-btn {
|
||||||
|
padding: 15px 30px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
font-family: 'Press Start 2P', cursive;
|
||||||
|
background: linear-gradient(180deg, #00ff00 0%, #00cc00 100%);
|
||||||
|
color: #000;
|
||||||
|
border: 2px solid #00ff00;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
box-shadow: 0 4px 10px rgba(0, 255, 0, 0.3);
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-customize-btn:hover {
|
||||||
|
background: linear-gradient(180deg, #00ff88 0%, #00ff00 100%);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 15px rgba(0, 255, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-customize-btn:active {
|
||||||
|
transform: translateY(0) scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shape-options {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shape-option {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 15px;
|
||||||
|
background: rgba(255, 255, 255, 0.05);
|
||||||
|
border: 2px solid rgba(255, 215, 0, 0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shape-option:hover {
|
||||||
|
background: rgba(255, 215, 0, 0.1);
|
||||||
|
border-color: #ffd700;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shape-option.active {
|
||||||
|
background: rgba(255, 215, 0, 0.2);
|
||||||
|
border-color: #ffd700;
|
||||||
|
box-shadow: 0 0 15px rgba(255, 215, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.shape-preview {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.round-preview {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ffd700;
|
||||||
|
border: 3px solid #fff;
|
||||||
|
box-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.triangle-preview {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-left: 25px solid transparent;
|
||||||
|
border-right: 25px solid transparent;
|
||||||
|
border-bottom: 45px solid #ffd700;
|
||||||
|
border-top: none;
|
||||||
|
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.3));
|
||||||
|
}
|
||||||
|
|
||||||
|
.shape-option span {
|
||||||
|
font-size: 0.7em;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user