diff --git a/game.js b/game.js index e61c0d1..01812cc 100644 --- a/game.js +++ b/game.js @@ -7,6 +7,13 @@ const statusElement = document.getElementById('status'); const restartBtn = document.getElementById('restartBtn'); const usernameInput = document.getElementById('username'); const leaderboardElement = document.getElementById('leaderboard'); +const gameOverlay = document.getElementById('gameOverlay'); +const finalScoreElement = document.getElementById('finalScore'); +const finalLevelElement = document.getElementById('finalLevel'); +const overlayRestartBtn = document.getElementById('overlayRestartBtn'); +const confirmUsernameBtn = document.getElementById('confirmUsernameBtn'); + +let usernameConfirmed = false; const CELL_SIZE = 20; const COLS = 30; @@ -142,7 +149,7 @@ class Pacman { this.nextDirection = 0; this.mouthAngle = 0; this.mouthOpen = true; - this.baseSpeed = 0.15; + this.baseSpeed = 0.25; this.speed = this.baseSpeed; this.pixelX = this.x * CELL_SIZE + CELL_SIZE / 2; this.pixelY = this.y * CELL_SIZE + CELL_SIZE / 2; @@ -253,6 +260,7 @@ class Pacman { const hue = (this.colorAnimation * 180 / Math.PI) % 360; ctx.fillStyle = `hsl(${hue}, 100%, 50%)`; + ctx.beginPath(); if (this.mouthOpen) { @@ -263,6 +271,7 @@ class Pacman { ctx.lineTo(0, 0); ctx.fill(); + ctx.restore(); } } @@ -605,7 +614,7 @@ function checkCollisions() { if (lives <= 0) { gameRunning = false; statusElement.textContent = 'Game Over !'; - restartBtn.style.display = 'block'; + showGameOver(); saveScore(); } else { restartCurrentLevel(); @@ -993,6 +1002,8 @@ function placeBonuses() { } function initGame() { + resetUsernameInput(); // Réinitialiser le champ nom + currentMazeIndex = 0; originalMaze = mazeVariants[0]; maze = originalMaze.map(row => [...row]); @@ -1008,6 +1019,7 @@ function initGame() { statusElement.textContent = 'En jeu'; statusElement.style.color = '#ffd700'; restartBtn.style.display = 'none'; + hideGameOver(); pacman = new Pacman(); pacman.speed = pacman.baseSpeed * (1 + (level - 1) * 0.05); @@ -1054,12 +1066,19 @@ function getScores() { } function saveScore() { - const username = usernameInput.value.trim() || 'Anonyme'; + let username; + if (!usernameConfirmed) { + username = 'Anonyme'; + } else { + username = usernameInput.value.trim() || 'Anonyme'; + } + if (score > 0) { const scores = getScores(); scores.push({ username: username, score: score, + level: level, date: new Date().toISOString() }); scores.sort((a, b) => b.score - a.score); @@ -1090,12 +1109,17 @@ function updateLeaderboard() { name.className = 'leaderboard-name'; name.textContent = entry.username; + const levelDiv = document.createElement('div'); + levelDiv.className = 'leaderboard-level'; + levelDiv.textContent = 'Niv. ' + (entry.level || 1); + const scoreDiv = document.createElement('div'); scoreDiv.className = 'leaderboard-score'; scoreDiv.textContent = entry.score; item.appendChild(rank); item.appendChild(name); + item.appendChild(levelDiv); item.appendChild(scoreDiv); leaderboardElement.appendChild(item); }); @@ -1105,6 +1129,117 @@ restartBtn.addEventListener('click', () => { initGame(); }); +// === GESTION DU NOM D'UTILISATEUR === +function confirmUsername() { + const username = usernameInput.value.trim(); + + if (username.length === 0) { + alert('Veuillez entrer un nom d\'utilisateur !'); + usernameInput.focus(); + return; + } + + usernameConfirmed = true; + usernameInput.disabled = true; + confirmUsernameBtn.disabled = true; + confirmUsernameBtn.textContent = '✓ Confirmé'; + + // Supprimer les anciens messages de confirmation s'ils existent + const existingMsg = usernameInput.parentElement.querySelector('.username-confirmed'); + if (existingMsg) { + existingMsg.remove(); + } + + // Afficher un message de confirmation + const confirmationMsg = document.createElement('div'); + confirmationMsg.className = 'username-confirmed'; + confirmationMsg.textContent = `Bienvenue, ${username} !`; + usernameInput.parentElement.appendChild(confirmationMsg); + + // Masquer le message après 3 secondes + setTimeout(() => { + confirmationMsg.style.opacity = '0'; + setTimeout(() => confirmationMsg.remove(), 300); + }, 3000); +} + +function resetUsernameInput() { + usernameConfirmed = false; + usernameInput.disabled = false; + usernameInput.value = ''; + confirmUsernameBtn.disabled = false; + confirmUsernameBtn.textContent = '✓ Confirmer'; + + // Supprimer les messages de confirmation existants + const existingMsg = usernameInput.parentElement.querySelector('.username-confirmed'); + if (existingMsg) { + existingMsg.remove(); + } + + // Focus sur le champ après un court délai + setTimeout(() => usernameInput.focus(), 100); +} + +// Confirmation avec le bouton +confirmUsernameBtn.addEventListener('click', confirmUsername); + +// Confirmation avec la touche Entrée +usernameInput.addEventListener('keypress', (e) => { + if (e.key === 'Enter' && !usernameConfirmed) { + confirmUsername(); + } +}); + +// === GAME OVER OVERLAY === +function showGameOver() { + finalScoreElement.textContent = score; + finalLevelElement.textContent = level; + gameOverlay.classList.add('active'); + restartBtn.style.display = 'block'; +} + +function hideGameOver() { + gameOverlay.classList.remove('active'); +} + +overlayRestartBtn.addEventListener('click', () => { + initGame(); +}); + +// === CONTROLES TACTILES MOBILE === +document.querySelectorAll('.ctrl-btn').forEach(btn => { + // Touch events + btn.addEventListener('touchstart', (e) => { + e.preventDefault(); + const dir = parseInt(btn.dataset.dir); + if (!isNaN(dir) && gameRunning) { + pacman.nextDirection = dir; + } + }); + + // Click events (for testing on desktop) + btn.addEventListener('click', () => { + const dir = parseInt(btn.dataset.dir); + if (!isNaN(dir) && gameRunning) { + pacman.nextDirection = dir; + } + }); +}); + +// === ANIMATION DU SCORE === +let lastScore = 0; +function animateScore() { + if (score !== lastScore) { + const scoreParent = scoreElement.parentElement; + scoreParent.classList.remove('updated'); + void scoreParent.offsetWidth; // Force reflow + scoreParent.classList.add('updated'); + lastScore = score; + } + requestAnimationFrame(animateScore); +} +animateScore(); + updateLeaderboard(); initGame(); diff --git a/index.html b/index.html index 2b7eb58..bada8ba 100644 --- a/index.html +++ b/index.html @@ -13,6 +13,7 @@
+
Score: 0
@@ -21,9 +22,30 @@
Prêt à jouer
+ + +
+ +
+ + +
+ +
+

Utilisez les flèches directionnelles pour déplacer Oulvic

- + +
+ + +
+
+

GAME OVER

+

Score Final: 0

+

Niveau Atteint: 1

+ +
@@ -37,4 +59,3 @@ - diff --git a/style.css b/style.css index 92a8593..4c9d8de 100644 --- a/style.css +++ b/style.css @@ -1,3 +1,6 @@ +/* === POLICE ARCADE === */ +@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap'); + * { margin: 0; padding: 0; @@ -5,7 +8,7 @@ } body { - font-family: 'Arial', sans-serif; + font-family: 'Press Start 2P', cursive; background: #0a0a0a; background-image: radial-gradient(circle at 20% 30%, rgba(255, 0, 0, 0.1) 0%, transparent 50%), @@ -52,6 +55,77 @@ body::after { z-index: 0; } +/* === ANIMATIONS === */ +@keyframes neonFlicker { + 0%, 19%, 21%, 23%, 25%, 54%, 56%, 100% { + text-shadow: + 0 0 5px #ffd700, + 0 0 10px #ffd700, + 0 0 20px #ff8c00, + 0 0 40px #ff8c00, + 0 0 80px #ff4500; + } + 20%, 24%, 55% { + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); + } +} + +@keyframes rainbow { + 0% { color: #ff0000; } + 17% { color: #ff8000; } + 33% { color: #ffff00; } + 50% { color: #00ff00; } + 67% { color: #0080ff; } + 83% { color: #8000ff; } + 100% { color: #ff0000; } +} + +@keyframes heartbeat { + 0%, 100% { transform: scale(1); } + 15% { transform: scale(1.25); } + 30% { transform: scale(1); } + 45% { transform: scale(1.15); } +} + +@keyframes canvasGlow { + from { + box-shadow: + 0 0 20px rgba(255, 215, 0, 0.5), + 0 0 40px rgba(255, 215, 0, 0.3), + inset 0 0 60px rgba(0, 0, 255, 0.1); + } + to { + box-shadow: + 0 0 30px rgba(255, 215, 0, 0.7), + 0 0 60px rgba(255, 215, 0, 0.4), + inset 0 0 80px rgba(0, 0, 255, 0.15); + } +} + + +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 25% { transform: translateX(-10px); } + 75% { transform: translateX(10px); } +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.7; } +} + +@keyframes fadeIn { + from { opacity: 0; transform: scale(0.9); } + to { opacity: 1; transform: scale(1); } +} + +@keyframes scoreUp { + 0% { transform: scale(1); } + 50% { transform: scale(1.2); color: #00ff00; } + 100% { transform: scale(1); } +} + +/* === LAYOUT === */ .main-wrapper { display: flex; justify-content: center; @@ -65,124 +139,213 @@ body::after { .container { text-align: center; - background: rgba(0, 0, 0, 0.7); + background: rgba(0, 0, 0, 0.8); padding: 30px; - border-radius: 15px; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.8), 0 0 50px rgba(255, 0, 0, 0.2); + border-radius: 20px; + box-shadow: + 0 10px 30px rgba(0, 0, 0, 0.8), + 0 0 50px rgba(255, 0, 0, 0.2), + inset 0 0 30px rgba(255, 215, 0, 0.05); flex-shrink: 0; position: relative; z-index: 1; - border: 2px solid rgba(255, 0, 0, 0.3); + border: 3px solid rgba(255, 215, 0, 0.4); } +/* === TITRE ANIME AVEC 3D === */ h1 { - font-size: 3em; + font-size: 2.5em; margin-bottom: 20px; - text-shadow: 3px 3px 6px rgba(0, 0, 0, 0.5); - color: #ffd700; + letter-spacing: 5px; + animation: neonFlicker 3s infinite, rainbow 8s linear infinite; } +/* === BARRE D'INFO AMELIOREE === */ .game-info { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; - padding: 10px 20px; - background: rgba(0, 0, 0, 0.2); - border-radius: 10px; + padding: 15px 20px; + background: linear-gradient(180deg, rgba(0,0,0,0.6) 0%, rgba(0,0,50,0.4) 100%); + border-radius: 15px; + border: 2px solid rgba(255, 215, 0, 0.4); + box-shadow: + inset 0 0 20px rgba(255, 215, 0, 0.1); gap: 15px; flex-wrap: wrap; } -.score { - font-size: 1.5em; +.score, .level, .lives { + font-size: 0.9em; font-weight: bold; + background: rgba(0, 0, 0, 0.4); + padding: 10px 15px; + border-radius: 10px; + border: 1px solid rgba(255, 215, 0, 0.3); + transition: all 0.3s; +} + +.score.updated { + animation: scoreUp 0.3s ease-out; } .level { - font-size: 1.5em; - font-weight: bold; color: #ffd700; } -.lives { - font-size: 1.5em; - font-weight: bold; -} - +/* === COEURS ANIMES === */ .lives .heart { + display: inline-block; color: #ff0000; font-size: 1.2em; - margin: 0 2px; - transition: opacity 0.3s; + margin: 0 3px; + transition: opacity 0.3s, transform 0.3s; + animation: heartbeat 1.2s ease-in-out infinite; + filter: drop-shadow(0 0 5px #ff0000); } +.lives .heart:nth-child(2) { animation-delay: 0.15s; } +.lives .heart:nth-child(3) { animation-delay: 0.3s; } + #status { - font-size: 1.2em; + font-size: 0.8em; color: #ffd700; + text-shadow: 0 0 10px rgba(255, 215, 0, 0.5); } +/* === CANVAS AVEC GLOW ET EFFET 3D === */ #gameCanvas { - border: 3px solid #ffd700; - border-radius: 10px; + border: 4px solid #ffd700; + border-radius: 15px; background: #000; display: block; margin: 0 auto; - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); + box-shadow: + 0 0 20px rgba(255, 215, 0, 0.5), + 0 0 40px rgba(255, 215, 0, 0.3), + inset 0 0 60px rgba(0, 0, 255, 0.1); + animation: canvasGlow 3s ease-in-out infinite alternate; } +/* === INPUT UTILISATEUR === */ .user-input-section { margin-bottom: 15px; display: flex; align-items: center; justify-content: center; gap: 10px; + flex-wrap: wrap; } .user-input-section label { - font-size: 1.1em; + font-size: 0.7em; font-weight: bold; } #username { - padding: 8px 15px; - font-size: 1em; + padding: 10px 15px; + font-size: 0.8em; + font-family: 'Press Start 2P', cursive; border: 2px solid #ffd700; - border-radius: 8px; - background: rgba(0, 0, 0, 0.3); + border-radius: 10px; + background: rgba(0, 0, 0, 0.5); color: #fff; outline: none; max-width: 200px; + transition: all 0.3s; } #username:focus { border-color: #ffed4e; - box-shadow: 0 0 10px rgba(255, 215, 0, 0.5); + box-shadow: 0 0 15px rgba(255, 215, 0, 0.6); + background: rgba(0, 0, 0, 0.7); +} + +#username:disabled { + background: rgba(0, 255, 0, 0.1); + border-color: #00ff00; + color: #00ff00; + cursor: not-allowed; +} + +#username::placeholder { + color: rgba(255, 255, 255, 0.4); +} + +/* === BOUTON DE CONFIRMATION === */ +.confirm-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); +} + +.confirm-btn:hover { + background: linear-gradient(180deg, #00ff88 0%, #00ff00 100%); + transform: translateY(-2px); + box-shadow: 0 6px 15px rgba(0, 255, 0, 0.5); +} + +.confirm-btn:active { + transform: translateY(0) scale(0.98); +} + +.confirm-btn:disabled { + background: rgba(100, 100, 100, 0.5); + border-color: #666; + cursor: not-allowed; + opacity: 0.5; +} + +/* Message de confirmation */ +.username-confirmed { + font-size: 0.6em; + color: #00ff00; + margin-top: 5px; + text-shadow: 0 0 10px rgba(0, 255, 0, 0.5); + animation: fadeIn 0.3s ease-out; + width: 100%; + text-align: center; } .instructions { margin-top: 20px; - font-size: 1.1em; + font-size: 0.7em; + color: #aaa; } +/* === CLASSEMENT AVEC MEDAILLES === */ .leaderboard-container { - background: rgba(0, 0, 0, 0.7); + background: rgba(0, 0, 0, 0.8); padding: 25px; - border-radius: 15px; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.8), 0 0 50px rgba(255, 0, 0, 0.2); - min-width: 300px; + border-radius: 20px; + box-shadow: + 0 10px 30px rgba(0, 0, 0, 0.8), + 0 0 50px rgba(255, 0, 0, 0.2), + inset 0 0 30px rgba(255, 215, 0, 0.05); + min-width: 320px; max-height: 700px; position: relative; z-index: 1; - border: 2px solid rgba(255, 0, 0, 0.3); + border: 3px solid rgba(255, 215, 0, 0.4); } .leaderboard-container h2 { color: #ffd700; text-align: center; margin-bottom: 20px; - font-size: 2em; - text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); + font-size: 1.2em; + text-shadow: + 0 0 10px rgba(255, 215, 0, 0.5), + 2px 2px 4px rgba(0, 0, 0, 0.5); + letter-spacing: 2px; } #leaderboard { @@ -190,42 +353,94 @@ h1 { overflow-y: auto; } +#leaderboard::-webkit-scrollbar { + width: 8px; +} + +#leaderboard::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.3); + border-radius: 4px; +} + +#leaderboard::-webkit-scrollbar-thumb { + background: #ffd700; + border-radius: 4px; +} + .leaderboard-item { - background: rgba(255, 255, 255, 0.1); - padding: 12px; + background: linear-gradient(90deg, rgba(255,255,255,0.1) 0%, rgba(255,255,255,0.05) 100%); + padding: 12px 15px; margin-bottom: 10px; - border-radius: 8px; + border-radius: 10px; display: flex; justify-content: space-between; align-items: center; border-left: 4px solid #ffd700; + transition: all 0.3s ease; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); +} + +.leaderboard-item:hover { + transform: translateX(5px); + background: rgba(255, 215, 0, 0.15); + box-shadow: + 0 0 20px rgba(255, 215, 0, 0.2), + inset 0 1px 0 rgba(255, 255, 255, 0.2); +} + +/* Médailles top 3 */ +.leaderboard-item:nth-child(1) .leaderboard-rank::before { content: '🥇 '; } +.leaderboard-item:nth-child(2) .leaderboard-rank::before { content: '🥈 '; } +.leaderboard-item:nth-child(3) .leaderboard-rank::before { content: '🥉 '; } + +.leaderboard-item:nth-child(1) { + border-left-color: #ffd700; + background: linear-gradient(90deg, rgba(255,215,0,0.2) 0%, rgba(255,215,0,0.05) 100%); +} +.leaderboard-item:nth-child(2) { + border-left-color: #c0c0c0; + background: linear-gradient(90deg, rgba(192,192,192,0.15) 0%, rgba(192,192,192,0.05) 100%); +} +.leaderboard-item:nth-child(3) { + border-left-color: #cd7f32; + background: linear-gradient(90deg, rgba(205,127,50,0.15) 0%, rgba(205,127,50,0.05) 100%); } .leaderboard-item.top { - background: rgba(255, 215, 0, 0.2); - border-left-color: #ffed4e; font-weight: bold; } .leaderboard-rank { - font-size: 1.3em; + font-size: 0.9em; font-weight: bold; color: #ffd700; - min-width: 30px; + min-width: 60px; } .leaderboard-name { flex: 1; text-align: left; - margin-left: 15px; - font-size: 1.1em; + margin-left: 10px; + font-size: 0.7em; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.leaderboard-level { + font-size: 0.7em; + font-weight: bold; + color: #00ff00; + min-width: 60px; + text-align: center; + margin-right: 10px; } .leaderboard-score { - font-size: 1.2em; + font-size: 0.8em; font-weight: bold; color: #ffd700; - min-width: 80px; + min-width: 70px; text-align: right; } @@ -233,40 +448,154 @@ h1 { text-align: center; color: #aaa; padding: 20px; - font-style: italic; + font-size: 0.7em; } +/* === BOUTON REJOUER === */ #restartBtn { margin-top: 15px; - padding: 12px 30px; - font-size: 1.2em; - background: #ffd700; + padding: 15px 30px; + font-size: 0.9em; + font-family: 'Press Start 2P', cursive; + background: linear-gradient(180deg, #ffd700 0%, #ff8c00 100%); color: #000; border: none; - border-radius: 8px; + border-radius: 10px; cursor: pointer; font-weight: bold; - transition: background 0.3s; + transition: all 0.3s; + box-shadow: 0 5px 15px rgba(255, 215, 0, 0.4); } #restartBtn:hover { - background: #ffed4e; + background: linear-gradient(180deg, #ffed4e 0%, #ffa500 100%); + transform: translateY(-2px); + box-shadow: 0 8px 20px rgba(255, 215, 0, 0.6); } #restartBtn:active { - transform: scale(0.95); + transform: translateY(0) scale(0.98); } +/* === OVERLAY GAME OVER === */ +.game-overlay { + display: none; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.92); + justify-content: center; + align-items: center; + z-index: 10; + border-radius: 17px; + animation: fadeIn 0.5s ease-out; +} + +.game-overlay.active { + display: flex; +} + +.overlay-content { + text-align: center; + padding: 30px; +} + +.game-over-title { + font-size: 2em; + color: #ff0000; + text-shadow: + 0 0 10px #ff0000, + 0 0 20px #ff0000, + 0 0 40px #ff0000; + animation: shake 0.5s ease-in-out, pulse 1.5s ease-in-out infinite; + margin-bottom: 30px; +} + +.final-score, .final-level { + font-size: 0.9em; + margin: 15px 0; + color: #ffd700; +} + +.final-score span, .final-level span { + color: #00ff00; + font-size: 1.2em; +} + +#overlayRestartBtn { + margin-top: 25px; + padding: 15px 30px; + font-size: 0.9em; + font-family: 'Press Start 2P', cursive; + background: linear-gradient(180deg, #ffd700 0%, #ff8c00 100%); + color: #000; + border: none; + border-radius: 10px; + cursor: pointer; + font-weight: bold; + transition: all 0.3s; + box-shadow: 0 5px 15px rgba(255, 215, 0, 0.4); +} + +#overlayRestartBtn:hover { + background: linear-gradient(180deg, #ffed4e 0%, #ffa500 100%); + transform: translateY(-2px); + box-shadow: 0 8px 20px rgba(255, 215, 0, 0.6); +} + +#overlayRestartBtn:active { + transform: translateY(0) scale(0.98); +} + +/* === CONTROLES TACTILES MOBILE === */ +.mobile-controls { + display: none; + flex-direction: column; + align-items: center; + gap: 5px; + margin-top: 20px; +} + +.ctrl-row { + display: flex; + gap: 50px; +} + +.ctrl-btn { + width: 60px; + height: 60px; + font-size: 1.5em; + background: linear-gradient(145deg, rgba(255,215,0,0.2), rgba(255,215,0,0.1)); + border: 3px solid #ffd700; + border-radius: 15px; + color: #ffd700; + cursor: pointer; + transition: all 0.1s; + -webkit-tap-highlight-color: transparent; +} + +.ctrl-btn:active { + background: rgba(255, 215, 0, 0.4); + transform: scale(0.9); + box-shadow: + 0 0 15px rgba(255, 215, 0, 0.5), + inset 0 0 10px rgba(255, 215, 0, 0.3); +} + +/* === FOOTER === */ footer { text-align: center; margin-top: 30px; padding: 20px; color: #ffd700; - font-size: 1.1em; + font-size: 0.7em; font-weight: bold; text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); } +/* === RESPONSIVE === */ @media (max-width: 1200px) { .main-wrapper { flex-direction: column; @@ -286,7 +615,7 @@ footer { } h1 { - font-size: 2em; + font-size: 1.5em; } .game-info { @@ -298,5 +627,16 @@ footer { flex-direction: column; gap: 8px; } + + .mobile-controls { + display: flex; + } + + .game-over-title { + font-size: 1.5em; + } + + .final-score, .final-level { + font-size: 0.7em; + } } -