From 5091fc3d6e87dc0f16f7fd6036b95d02aa37686e Mon Sep 17 00:00:00 2001 From: thedevilbox Date: Tue, 2 Dec 2025 19:32:30 +0100 Subject: [PATCH] personalisation du personnage --- game.js | 248 +++++++++++++++++++++++++++++++++++++++++++++++++++-- index.html | 63 ++++++++++++++ style.css | 211 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 516 insertions(+), 6 deletions(-) diff --git a/game.js b/game.js index 8bf36b3..18a112f 100644 --- a/game.js +++ b/game.js @@ -13,6 +13,9 @@ const finalScoreElement = document.getElementById('finalScore'); const finalLevelElement = document.getElementById('finalLevel'); const overlayRestartBtn = document.getElementById('overlayRestartBtn'); const confirmUsernameBtn = document.getElementById('confirmUsernameBtn'); +const gameOverUsernameInput = document.getElementById('gameOverUsername'); +const saveScoreBtn = document.getElementById('saveScoreBtn'); +const scoreSavedMsg = document.getElementById('scoreSavedMsg'); // === GESTION DU MENU === const mainMenu = document.getElementById('mainMenu'); @@ -29,6 +32,12 @@ const menuLeaderboard = document.getElementById('menuLeaderboard'); const leaderboardContainer = document.getElementById('leaderboardContainer'); 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'); // Fonction pour vérifier si on est en mode plein écran function isFullscreen() { @@ -37,6 +46,10 @@ function isFullscreen() { } let usernameConfirmed = false; +let gameOverUsername = null; + +// Personnalisation du joueur +let playerColor = localStorage.getItem('playerColor') || 'rainbow'; const CELL_SIZE = 25; const COLS = 30; @@ -240,7 +253,7 @@ let traps = []; let specialZones = []; class Pacman { - constructor() { + constructor(color = null) { this.x = 14; this.y = 23; this.direction = 0; @@ -252,6 +265,7 @@ class Pacman { this.pixelX = this.x * CELL_SIZE + CELL_SIZE / 2; this.pixelY = this.y * CELL_SIZE + CELL_SIZE / 2; this.colorAnimation = 0; + this.color = color || playerColor; } update() { @@ -456,8 +470,22 @@ class Pacman { const rotation = [Math.PI * 1.5, 0, Math.PI * 0.5, Math.PI]; ctx.rotate(rotation[this.direction]); - const hue = (this.colorAnimation * 180 / Math.PI) % 360; - ctx.fillStyle = `hsl(${hue}, 100%, 50%)`; + // Utiliser la couleur personnalisée + if (this.color === 'rainbow') { + const hue = (this.colorAnimation * 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[this.color] || '#ffd700'; + } ctx.beginPath(); @@ -1237,7 +1265,7 @@ function checkCollisions() { gameRunning = false; statusElement.textContent = 'Game Over !'; showGameOver(); - saveScore(); + // Le score sera sauvegardé quand l'utilisateur clique sur "Sauvegarder" } else { restartCurrentLevel(); } @@ -1954,9 +1982,15 @@ function getScores() { return scoresJson ? JSON.parse(scoresJson) : []; } -function saveScore() { +function saveScore(usernameForScore = null) { 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'; } else { username = usernameInput.value.trim() || 'Anonyme'; @@ -2127,6 +2161,21 @@ usernameInput.addEventListener('keypress', (e) => { function showGameOver() { finalScoreElement.textContent = score; 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'); restartBtn.style.display = 'block'; } @@ -2135,7 +2184,39 @@ function hideGameOver() { 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', () => { + hideGameOver(); initGame(); }); @@ -2404,6 +2485,161 @@ if (rulesModal) { }); } +// === GESTION DU MODAL DE PERSONNALISATION === +let selectedColor = playerColor; + +// Ouvrir le modal de personnalisation +if (customizeBtn) { + customizeBtn.addEventListener('click', () => { + if (customizeModal) { + customizeModal.style.display = 'flex'; + selectedColor = playerColor; + updateColorSelection(); + 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(); + }); + }); +} + +// 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'); + } + }); + } +} + +// 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; + } + + ctx.beginPath(); + const radius = 40; + if (mouthOpen) { + ctx.arc(0, 0, radius, 0.2, Math.PI * 2 - 0.2); + } else { + ctx.arc(0, 0, radius, 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; + localStorage.setItem('playerColor', playerColor); + + // Mettre à jour le joueur actuel si le jeu est en cours + if (pacman) { + pacman.color = playerColor; + } + + // Fermer le modal + if (customizeModal) { + customizeModal.style.display = 'none'; + stopPreviewAnimation(); + } + + // Message de confirmation + alert('Personnalisation sauvegardée !'); + }); +} + // Fermer le classement dans le jeu if (closeLeaderboardBtn) { closeLeaderboardBtn.addEventListener('click', () => { diff --git a/index.html b/index.html index 70b7ccb..68e8a52 100644 --- a/index.html +++ b/index.html @@ -21,6 +21,9 @@ + @@ -87,6 +90,12 @@

GAME OVER

Score Final: 0

Niveau Atteint: 1

+
+ + + +
+ @@ -222,6 +231,60 @@ + + + diff --git a/style.css b/style.css index da3bd5a..bc5d8f4 100644 --- a/style.css +++ b/style.css @@ -1123,6 +1123,71 @@ h1 { 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 === */ .mobile-controls { display: none; @@ -1268,3 +1333,149 @@ footer { 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); +}